Merge "Revert^2 "Refactor/clean up Overview Actions buttons"" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 9080284..af175ce 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -83,3 +83,10 @@
description: "Enables two pane widget picker for unfolded foldables"
bug: "313922374"
}
+
+flag {
+ name: "enable_tablet_two_pane_picker_v2"
+ namespace: "launcher"
+ description: "Enables full width two pane widget picker for tablets in landscape and portrait"
+ bug: "315055849"
+}
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 5a9e147..8240f11 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -138,7 +138,7 @@
}
}
-// Next value 54
+// Next value 55
enum Attribute {
option allow_alias = true;
@@ -200,6 +200,7 @@
DATA_SOURCE_APPSEARCH_CATEGORY_SRP_PREVIEW = 48;
DATA_SOURCE_APPSEARCH_ENTITY_SRP_PREVIEW = 49;
DATA_SOURCE_AIAI_SEARCH_ROOT = 47;
+ DATA_SOURCE_LAUNCHER = 54;
// Web suggestions provided by AGA
ALL_APPS_SEARCH_RESULT_WEB_SUGGEST = 39;
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index d6ab54e..8db63e3 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -55,6 +55,7 @@
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
+import static com.android.launcher3.testing.shared.TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE;
import static com.android.launcher3.util.DisplayController.isTransientTaskbar;
import static com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
@@ -120,6 +121,7 @@
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.model.data.ItemInfo;
@@ -1651,6 +1653,15 @@
if (launcherIsForceInvisibleOrOpening) {
addCujInstrumentation(anim, playFallBackAnimation
? CUJ_APP_CLOSE_TO_HOME_FALLBACK : CUJ_APP_CLOSE_TO_HOME);
+
+ anim.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ AccessibilityManagerCompat.sendTestProtocolEventToTest(
+ mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE);
+ }
+ });
+
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 988ef80..60ee38f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -203,11 +203,16 @@
Display display = windowContext.getDisplay();
Context c = getApplicationContext();
mWindowManager = c.getSystemService(WindowManager.class);
- mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
- mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
+
+ boolean phoneMode = TaskbarManager.isPhoneMode(mDeviceProfile);
+ mLeftCorner = phoneMode
+ ? null
+ : display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+ mRightCorner = phoneMode
+ ? null
+ : display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
// Inflate views.
- boolean phoneMode = TaskbarManager.isPhoneMode(mDeviceProfile);
int taskbarLayout = DisplayController.isTransientTaskbar(this) && !phoneMode
? R.layout.transient_taskbar
: R.layout.taskbar;
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 5568459..9e58160 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -55,6 +55,7 @@
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.systemui.shared.system.QuickStepContract;
@@ -416,6 +417,10 @@
if (mLauncher.isDestroyed()) {
return;
}
+ LauncherTaskbarUIController taskbarUIController = mLauncher.getTaskbarUIController();
+ if (taskbarUIController != null) {
+ taskbarUIController.onLauncherVisibilityChanged(true);
+ }
// TODO: Catch the moment when launcher becomes visible after the top app un-occludes
// launcher and start animating afterwards. Currently we occasionally get a flicker from
// animating when launcher is still invisible.
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 221ce48..f801b3b 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -106,10 +106,10 @@
});
return response;
- case TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT: {
+ case TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD: {
final Resources resources = mContext.getResources();
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
+ resources.getDimensionPixelSize(R.dimen.taskbar_from_nav_threshold));
return response;
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
new file mode 100644
index 0000000..27de20c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -0,0 +1,1515 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
+
+import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING;
+import static com.android.quickstep.util.LogUtils.splitFailureMessage;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.IRecentsAnimationController;
+import android.view.IRecentsAnimationRunner;
+import android.view.IRemoteAnimationRunner;
+import android.view.MotionEvent;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.window.IOnBackInvokedCallback;
+import android.window.RemoteTransition;
+import android.window.TaskSnapshot;
+import android.window.TransitionFilter;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.util.ScreenshotRequest;
+import com.android.internal.view.AppearanceRegion;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.AssistUtils;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.RecentsAnimationListener;
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
+import com.android.systemui.shared.system.smartspace.SmartspaceState;
+import com.android.systemui.unfold.progress.IUnfoldAnimation;
+import com.android.systemui.unfold.progress.IUnfoldTransitionListener;
+import com.android.wm.shell.back.IBackAnimation;
+import com.android.wm.shell.bubbles.IBubbles;
+import com.android.wm.shell.bubbles.IBubblesListener;
+import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
+import com.android.wm.shell.desktopmode.IDesktopMode;
+import com.android.wm.shell.desktopmode.IDesktopTaskListener;
+import com.android.wm.shell.draganddrop.IDragAndDrop;
+import com.android.wm.shell.onehanded.IOneHanded;
+import com.android.wm.shell.pip.IPip;
+import com.android.wm.shell.pip.IPipAnimationListener;
+import com.android.wm.shell.recents.IRecentTasks;
+import com.android.wm.shell.recents.IRecentTasksListener;
+import com.android.wm.shell.splitscreen.ISplitScreen;
+import com.android.wm.shell.splitscreen.ISplitScreenListener;
+import com.android.wm.shell.splitscreen.ISplitSelectListener;
+import com.android.wm.shell.startingsurface.IStartingWindow;
+import com.android.wm.shell.startingsurface.IStartingWindowListener;
+import com.android.wm.shell.transition.IHomeTransitionListener;
+import com.android.wm.shell.transition.IShellTransitions;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+
+/**
+ * Holds the reference to SystemUI.
+ */
+public class SystemUiProxy implements ISystemUiProxy {
+ private static final String TAG = SystemUiProxy.class.getSimpleName();
+
+ public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
+ new MainThreadInitializedObject<>(SystemUiProxy::new);
+
+ private static final int MSG_SET_SHELF_HEIGHT = 1;
+ private static final int MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2;
+
+ private ISystemUiProxy mSystemUiProxy;
+ private IPip mPip;
+ private IBubbles mBubbles;
+ private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
+ private ISplitScreen mSplitScreen;
+ private IOneHanded mOneHanded;
+ private IShellTransitions mShellTransitions;
+ private IStartingWindow mStartingWindow;
+ private IRecentTasks mRecentTasks;
+ private IBackAnimation mBackAnimation;
+ private IDesktopMode mDesktopMode;
+ private IUnfoldAnimation mUnfoldAnimation;
+ private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
+ MAIN_EXECUTOR.execute(() -> clearProxy());
+ };
+
+ // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
+ // yet, and we'll need to set/register these listeners with SysUI when they do. Note that it is
+ // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
+ // in case SysUI needs to rebind.
+ private IPipAnimationListener mPipAnimationListener;
+ private IBubblesListener mBubblesListener;
+ private ISplitScreenListener mSplitScreenListener;
+ private ISplitSelectListener mSplitSelectListener;
+ private IStartingWindowListener mStartingWindowListener;
+ private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
+ private String mLauncherActivityClass;
+ private IRecentTasksListener mRecentTasksListener;
+ private IUnfoldTransitionListener mUnfoldAnimationListener;
+ private IDesktopTaskListener mDesktopTaskListener;
+ private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions =
+ new LinkedHashMap<>();
+ private IBinder mOriginalTransactionToken = null;
+ private IOnBackInvokedCallback mBackToLauncherCallback;
+ private IRemoteAnimationRunner mBackToLauncherRunner;
+ private IDragAndDrop mDragAndDrop;
+ private IHomeTransitionListener mHomeTransitionListener;
+
+ // Used to dedupe calls to SystemUI
+ private int mLastShelfHeight;
+ private boolean mLastShelfVisible;
+
+ // Used to dedupe calls to SystemUI
+ private int mLastLauncherKeepClearAreaHeight;
+ private boolean mLastLauncherKeepClearAreaHeightVisible;
+
+ private final Context mContext;
+ private final Handler mAsyncHandler;
+
+ // TODO(141886704): Find a way to remove this
+ private int mLastSystemUiStateFlags;
+
+ /**
+ * This is a singleton pending intent that is used to start recents via Shell (which is a
+ * different process). It is bare-bones, so it's expected that the component and options will
+ * be provided via fill-in intent.
+ */
+ private final PendingIntent mRecentsPendingIntent;
+
+ public SystemUiProxy(Context context) {
+ mContext = context;
+ mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync);
+ final Intent baseIntent = new Intent().setPackage(mContext.getPackageName());
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ mRecentsPendingIntent = PendingIntent.getActivity(mContext, 0, baseIntent,
+ PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
+ | Intent.FILL_IN_COMPONENT, options.toBundle());
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onBackPressed();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onBackPressed", e);
+ }
+ }
+ }
+
+ @Override
+ public void onImeSwitcherPressed() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onImeSwitcherPressed();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onImeSwitcherPressed", e);
+ }
+ }
+ }
+
+ @Override
+ public void setHomeRotationEnabled(boolean enabled) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.setHomeRotationEnabled(enabled);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onBackPressed", e);
+ }
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ // Do nothing
+ return null;
+ }
+
+ /**
+ * Sets proxy state, including death linkage, various listeners, and other configuration objects
+ */
+ @MainThread
+ public void setProxy(ISystemUiProxy proxy, IPip pip, IBubbles bubbles, ISplitScreen splitScreen,
+ IOneHanded oneHanded, IShellTransitions shellTransitions,
+ IStartingWindow startingWindow, IRecentTasks recentTasks,
+ ISysuiUnlockAnimationController sysuiUnlockAnimationController,
+ IBackAnimation backAnimation, IDesktopMode desktopMode,
+ IUnfoldAnimation unfoldAnimation, IDragAndDrop dragAndDrop) {
+ Preconditions.assertUIThread();
+ unlinkToDeath();
+ mSystemUiProxy = proxy;
+ mPip = pip;
+ mBubbles = bubbles;
+ mSplitScreen = splitScreen;
+ mOneHanded = oneHanded;
+ mShellTransitions = shellTransitions;
+ mStartingWindow = startingWindow;
+ mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
+ mRecentTasks = recentTasks;
+ mBackAnimation = backAnimation;
+ mDesktopMode = desktopMode;
+ mUnfoldAnimation = unfoldAnimation;
+ mDragAndDrop = dragAndDrop;
+ linkToDeath();
+ // re-attach the listeners once missing due to setProxy has not been initialized yet.
+ setPipAnimationListener(mPipAnimationListener);
+ setBubblesListener(mBubblesListener);
+ registerSplitScreenListener(mSplitScreenListener);
+ registerSplitSelectListener(mSplitSelectListener);
+ setHomeTransitionListener(mHomeTransitionListener);
+ setStartingWindowListener(mStartingWindowListener);
+ setLauncherUnlockAnimationController(
+ mLauncherActivityClass, mLauncherUnlockAnimationController);
+ new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition);
+ setupTransactionQueue();
+ registerRecentTasksListener(mRecentTasksListener);
+ setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner);
+ setUnfoldAnimationListener(mUnfoldAnimationListener);
+ setDesktopTaskListener(mDesktopTaskListener);
+ setAssistantOverridesRequested(
+ AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes());
+ }
+
+ /**
+ * Clear the proxy to release held resources and turn the majority of its operations into no-ops
+ */
+ @MainThread
+ public void clearProxy() {
+ setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null);
+ }
+
+ // TODO(141886704): Find a way to remove this
+ public void setLastSystemUiStateFlags(int stateFlags) {
+ mLastSystemUiStateFlags = stateFlags;
+ }
+
+ // TODO(141886704): Find a way to remove this
+ public int getLastSystemUiStateFlags() {
+ return mLastSystemUiStateFlags;
+ }
+
+ public boolean isActive() {
+ return mSystemUiProxy != null;
+ }
+
+ private void linkToDeath() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.asBinder().linkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to link sysui proxy death recipient");
+ }
+ }
+ }
+
+ private void unlinkToDeath() {
+ if (mSystemUiProxy != null) {
+ mSystemUiProxy.asBinder().unlinkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
+ }
+ }
+
+ @Override
+ public void startScreenPinning(int taskId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startScreenPinning(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startScreenPinning", e);
+ }
+ }
+ }
+
+ @Override
+ public void onOverviewShown(boolean fromHome) {
+ onOverviewShown(fromHome, TAG);
+ }
+
+ public void onOverviewShown(boolean fromHome, String tag) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onOverviewShown(fromHome);
+ } catch (RemoteException e) {
+ Log.w(tag, "Failed call onOverviewShown from: " + (fromHome ? "home" : "app"), e);
+ }
+ }
+ }
+
+ @MainThread
+ @Override
+ public void onStatusBarTouchEvent(MotionEvent event) {
+ Preconditions.assertUIThread();
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onStatusBarTouchEvent(event);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onStatusBarTouchEvent with arg: " + event, e);
+ }
+ }
+ }
+
+ @Override
+ public void onStatusBarTrackpadEvent(MotionEvent event) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onStatusBarTrackpadEvent(event);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onStatusBarTrackpadEvent with arg: " + event, e);
+ }
+ }
+ }
+
+ @Override
+ public void onAssistantProgress(float progress) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onAssistantProgress(progress);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onAssistantProgress with progress: " + progress, e);
+ }
+ }
+ }
+
+ @Override
+ public void onAssistantGestureCompletion(float velocity) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.onAssistantGestureCompletion(velocity);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onAssistantGestureCompletion", e);
+ }
+ }
+ }
+
+ @Override
+ public void startAssistant(Bundle args) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startAssistant(args);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startAssistant", e);
+ }
+ }
+ }
+
+ @Override
+ public void setAssistantOverridesRequested(int[] invocationTypes) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.setAssistantOverridesRequested(invocationTypes);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setAssistantOverridesRequested", e);
+ }
+ }
+ }
+
+ @Override
+ public void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.animateNavBarLongPress(isTouchDown, shrink, durationMs);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call animateNavBarLongPress", e);
+ }
+ }
+ }
+
+ @Override
+ public void notifyAccessibilityButtonClicked(int displayId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.notifyAccessibilityButtonClicked(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call notifyAccessibilityButtonClicked", e);
+ }
+ }
+ }
+
+ @Override
+ public void notifyAccessibilityButtonLongClicked() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.notifyAccessibilityButtonLongClicked();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call notifyAccessibilityButtonLongClicked", e);
+ }
+ }
+ }
+
+ @Override
+ public void stopScreenPinning() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.stopScreenPinning();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stopScreenPinning", e);
+ }
+ }
+ }
+
+ @Override
+ public void notifyPrioritizedRotation(int rotation) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.notifyPrioritizedRotation(rotation);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call notifyPrioritizedRotation with arg: " + rotation, e);
+ }
+ }
+ }
+
+ @Override
+ public void notifyTaskbarStatus(boolean visible, boolean stashed) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.notifyTaskbarStatus(visible, stashed);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call notifyTaskbarStatus with arg: " +
+ visible + ", " + stashed, e);
+ }
+ }
+ }
+
+ /**
+ * NOTE: If called to suspend, caller MUST call this method to also un-suspend
+ * @param suspend should be true to stop auto-hide, false to resume normal behavior
+ */
+ @Override
+ public void notifyTaskbarAutohideSuspend(boolean suspend) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.notifyTaskbarAutohideSuspend(suspend);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call notifyTaskbarAutohideSuspend with arg: " +
+ suspend, e);
+ }
+ }
+ }
+
+ @Override
+ public void takeScreenshot(ScreenshotRequest request) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.takeScreenshot(request);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call takeScreenshot");
+ }
+ }
+ }
+
+ @Override
+ public void expandNotificationPanel() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.expandNotificationPanel();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call expandNotificationPanel", e);
+ }
+ }
+ }
+
+ @Override
+ public void toggleNotificationPanel() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.toggleNotificationPanel();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call toggleNotificationPanel", e);
+ }
+ }
+ }
+
+ //
+ // Pip
+ //
+
+ /**
+ * Sets the shelf height.
+ */
+ public void setShelfHeight(boolean visible, int shelfHeight) {
+ Message.obtain(mAsyncHandler, MSG_SET_SHELF_HEIGHT,
+ visible ? 1 : 0 , shelfHeight).sendToTarget();
+ }
+
+ @WorkerThread
+ private void setShelfHeightAsync(int visibleInt, int shelfHeight) {
+ boolean visible = visibleInt != 0;
+ boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
+ IPip pip = mPip;
+ if (pip != null && changed) {
+ mLastShelfVisible = visible;
+ mLastShelfHeight = shelfHeight;
+ try {
+ pip.setShelfHeight(visible, shelfHeight);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setShelfHeight visible: " + visible
+ + " height: " + shelfHeight, e);
+ }
+ }
+ }
+
+ /**
+ * Sets the height of the keep clear area that is going to be reported by
+ * the Launcher for the Hotseat.
+ */
+ public void setLauncherKeepClearAreaHeight(boolean visible, int height) {
+ Message.obtain(mAsyncHandler, MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT,
+ visible ? 1 : 0 , height).sendToTarget();
+ }
+
+ @WorkerThread
+ private void setLauncherKeepClearAreaHeight(int visibleInt, int height) {
+ boolean visible = visibleInt != 0;
+ boolean changed = visible != mLastLauncherKeepClearAreaHeightVisible
+ || height != mLastLauncherKeepClearAreaHeight;
+ IPip pip = mPip;
+ if (pip != null && changed) {
+ mLastLauncherKeepClearAreaHeightVisible = visible;
+ mLastLauncherKeepClearAreaHeight = height;
+ try {
+ pip.setLauncherKeepClearAreaHeight(visible, height);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setLauncherKeepClearAreaHeight visible: " + visible
+ + " height: " + height, e);
+ }
+ }
+ }
+
+ /**
+ * Sets listener to get pip animation callbacks.
+ */
+ public void setPipAnimationListener(IPipAnimationListener listener) {
+ if (mPip != null) {
+ try {
+ mPip.setPipAnimationListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
+ }
+ }
+ mPipAnimationListener = listener;
+ }
+
+ /**
+ * @return Destination bounds of auto-pip animation, {@code null} if the animation is not ready.
+ */
+ @Nullable
+ public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+ PictureInPictureParams pictureInPictureParams, int launcherRotation,
+ Rect hotseatKeepClearArea) {
+ if (mPip != null) {
+ try {
+ return mPip.startSwipePipToHome(componentName, activityInfo,
+ pictureInPictureParams, launcherRotation, hotseatKeepClearArea);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startSwipePipToHome", e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to
+ * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell
+ * should be responsible for cleaning up the overlay.
+ */
+ public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
+ SurfaceControl overlay) {
+ if (mPip != null) {
+ try {
+ mPip.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stopSwipePipToHome");
+ }
+ }
+ }
+
+ /**
+ * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell
+ * can use this callback to clean up its internal states.
+ */
+ public void abortSwipePipToHome(int taskId, ComponentName componentName) {
+ if (mPip != null) {
+ try {
+ mPip.abortSwipePipToHome(taskId, componentName);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call abortSwipePipToHome");
+ }
+ }
+ }
+
+ /**
+ * Sets the next pip animation type to be the alpha animation.
+ */
+ public void setPipAnimationTypeToAlpha() {
+ if (mPip != null) {
+ try {
+ mPip.setPipAnimationTypeToAlpha();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setPipAnimationTypeToAlpha", e);
+ }
+ }
+ }
+
+ /**
+ * Sets the app icon size in pixel used by Launcher all apps.
+ */
+ public void setLauncherAppIconSize(int iconSizePx) {
+ if (mPip != null) {
+ try {
+ mPip.setLauncherAppIconSize(iconSizePx);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setLauncherAppIconSize", e);
+ }
+ }
+ }
+
+ //
+ // Bubbles
+ //
+
+ /**
+ * Sets the listener to be notified of bubble state changes.
+ */
+ public void setBubblesListener(IBubblesListener listener) {
+ if (mBubbles != null) {
+ try {
+ if (mBubblesListener != null) {
+ // Clear out any previous listener
+ mBubbles.unregisterBubbleListener(mBubblesListener);
+ }
+ if (listener != null) {
+ mBubbles.registerBubbleListener(listener);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerBubblesListener");
+ }
+ }
+ mBubblesListener = listener;
+ }
+
+ /**
+ * Tells SysUI to show the bubble with the provided key.
+ * @param key the key of the bubble to show.
+ * @param bubbleBarOffsetX the offset of the bubble bar from the edge of the screen on the X
+ * axis.
+ * @param bubbleBarOffsetY the offset of the bubble bar from the edge of the screen on the Y
+ * axis.
+ */
+ public void showBubble(String key, int bubbleBarOffsetX, int bubbleBarOffsetY) {
+ if (mBubbles != null) {
+ try {
+ mBubbles.showBubble(key, bubbleBarOffsetX, bubbleBarOffsetY);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call showBubble");
+ }
+ }
+ }
+
+ /**
+ * Tells SysUI to remove the bubble with the provided key.
+ * @param key the key of the bubble to show.
+ */
+ public void removeBubble(String key) {
+ if (mBubbles == null) return;
+ try {
+ mBubbles.removeBubble(key);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call removeBubble");
+ }
+ }
+
+ /**
+ * Tells SysUI to remove all bubbles.
+ */
+ public void removeAllBubbles() {
+ if (mBubbles == null) return;
+ try {
+ mBubbles.removeAllBubbles();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call removeAllBubbles");
+ }
+ }
+
+ /**
+ * Tells SysUI to collapse the bubbles.
+ */
+ public void collapseBubbles() {
+ if (mBubbles != null) {
+ try {
+ mBubbles.collapseBubbles();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call collapseBubbles");
+ }
+ }
+ }
+
+ /**
+ * Tells SysUI when the bubble is being dragged.
+ * Should be called only when the bubble bar is expanded.
+ * @param bubbleKey the key of the bubble to collapse/expand
+ * @param isBeingDragged whether the bubble is being dragged
+ */
+ public void onBubbleDrag(@Nullable String bubbleKey, boolean isBeingDragged) {
+ if (mBubbles == null) return;
+ try {
+ mBubbles.onBubbleDrag(bubbleKey, isBeingDragged);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onBubbleDrag");
+ }
+ }
+
+ /**
+ * Tells SysUI to show user education relative to the reference point provided.
+ * @param position the bubble bar top center position in Screen coordinates.
+ */
+ public void showUserEducation(Point position) {
+ try {
+ mBubbles.showUserEducation(position.x, position.y);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call showUserEducation");
+ }
+ }
+
+ //
+ // Splitscreen
+ //
+
+ public void registerSplitScreenListener(ISplitScreenListener listener) {
+ if (mSplitScreen != null) {
+ try {
+ mSplitScreen.registerSplitScreenListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerSplitScreenListener");
+ }
+ }
+ mSplitScreenListener = listener;
+ }
+
+ public void unregisterSplitScreenListener(ISplitScreenListener listener) {
+ if (mSplitScreen != null) {
+ try {
+ mSplitScreen.unregisterSplitScreenListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call unregisterSplitScreenListener");
+ }
+ }
+ mSplitScreenListener = null;
+ }
+
+ public void registerSplitSelectListener(ISplitSelectListener listener) {
+ if (mSplitScreen != null) {
+ try {
+ mSplitScreen.registerSplitSelectListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerSplitSelectListener");
+ }
+ }
+ mSplitSelectListener = listener;
+ }
+
+ public void unregisterSplitSelectListener(ISplitSelectListener listener) {
+ if (mSplitScreen != null) {
+ try {
+ mSplitScreen.unregisterSplitSelectListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call unregisterSplitSelectListener");
+ }
+ }
+ mSplitSelectListener = null;
+ }
+
+ /** Start multiple tasks in split-screen simultaneously. */
+ public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2,
+ @StagePosition int splitPosition, @PersistentSnapPosition int snapPosition,
+ RemoteTransition remoteTransition, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition,
+ snapPosition, remoteTransition, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, splitFailureMessage("startTasks", "RemoteException"), e);
+ }
+ }
+ }
+
+ public void startIntentAndTask(PendingIntent pendingIntent, int userId1, Bundle options1,
+ int taskId, Bundle options2, @StagePosition int splitPosition,
+ @PersistentSnapPosition int snapPosition, RemoteTransition remoteTransition,
+ InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startIntentAndTask(pendingIntent, userId1, options1, taskId, options2,
+ splitPosition, snapPosition, remoteTransition, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, splitFailureMessage("startIntentAndTask", "RemoteException"), e);
+ }
+ }
+ }
+
+ public void startIntents(PendingIntent pendingIntent1, int userId1,
+ @Nullable ShortcutInfo shortcutInfo1, Bundle options1, PendingIntent pendingIntent2,
+ int userId2, @Nullable ShortcutInfo shortcutInfo2, Bundle options2,
+ @StagePosition int splitPosition, @PersistentSnapPosition int snapPosition,
+ RemoteTransition remoteTransition, InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startIntents(pendingIntent1, userId1, shortcutInfo1, options1,
+ pendingIntent2, userId2, shortcutInfo2, options2, splitPosition,
+ snapPosition, remoteTransition, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, splitFailureMessage("startIntents", "RemoteException"), e);
+ }
+ }
+ }
+
+ public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId,
+ Bundle options2, @StagePosition int splitPosition,
+ @PersistentSnapPosition int snapPosition, RemoteTransition remoteTransition,
+ InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
+ splitPosition, snapPosition, remoteTransition, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, splitFailureMessage("startShortcutAndTask", "RemoteException"), e);
+ }
+ }
+ }
+
+ /**
+ * Start multiple tasks in split-screen simultaneously.
+ */
+ public void startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2,
+ Bundle options2, @StagePosition int splitPosition,
+ @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2,
+ splitPosition, snapPosition, adapter, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, splitFailureMessage(
+ "startTasksWithLegacyTransition", "RemoteException"), e);
+ }
+ }
+ }
+
+ public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
+ Bundle options1, int taskId, Bundle options2, @StagePosition int splitPosition,
+ @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, userId1,
+ options1, taskId, options2, splitPosition, snapPosition, adapter,
+ instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, splitFailureMessage(
+ "startIntentAndTaskWithLegacyTransition", "RemoteException"), e);
+ }
+ }
+ }
+
+ public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, Bundle options1,
+ int taskId, Bundle options2, @StagePosition int splitPosition,
+ @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, options1,
+ taskId, options2, splitPosition, snapPosition, adapter, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, splitFailureMessage(
+ "startShortcutAndTaskWithLegacyTransition", "RemoteException"), e);
+ }
+ }
+ }
+
+ /**
+ * Starts a pair of intents or shortcuts in split-screen using legacy transition. Passing a
+ * non-null shortcut info means to start the app as a shortcut.
+ */
+ public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
+ @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
+ PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
+ @Nullable Bundle options2, @StagePosition int sidePosition,
+ @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, userId1,
+ shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, options2,
+ sidePosition, snapPosition, adapter, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, splitFailureMessage(
+ "startIntentsWithLegacyTransition", "RemoteException"), e);
+ }
+ }
+ }
+
+ public void startShortcut(String packageName, String shortcutId, int position,
+ Bundle options, UserHandle user, InstanceId instanceId) {
+ if (mSplitScreen != null) {
+ try {
+ mSplitScreen.startShortcut(packageName, shortcutId, position, options,
+ user, instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, splitFailureMessage("startShortcut", "RemoteException"), e);
+ }
+ }
+ }
+
+ public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position,
+ Bundle options, InstanceId instanceId) {
+ if (mSplitScreen != null) {
+ try {
+ mSplitScreen.startIntent(intent, userId, fillInIntent, position, options,
+ instanceId);
+ } catch (RemoteException e) {
+ Log.w(TAG, splitFailureMessage("startIntent", "RemoteException"), e);
+ }
+ }
+ }
+
+ public void removeFromSideStage(int taskId) {
+ if (mSplitScreen != null) {
+ try {
+ mSplitScreen.removeFromSideStage(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call removeFromSideStage");
+ }
+ }
+ }
+
+ /**
+ * Call this when going to recents so that shell can set-up and provide appropriate leashes
+ * for animation (eg. DividerBar).
+ *
+ * @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
+ */
+ @Nullable
+ public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
+ if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS && mSplitScreen != null) {
+ try {
+ return mSplitScreen.onGoingToRecentsLegacy(apps);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onGoingToRecentsLegacy");
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
+ if (mSplitScreen != null) {
+ try {
+ return mSplitScreen.onStartingSplitLegacy(apps);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onStartingSplitLegacy");
+ }
+ }
+ return null;
+ }
+
+ //
+ // One handed
+ //
+
+ public void startOneHandedMode() {
+ if (mOneHanded != null) {
+ try {
+ mOneHanded.startOneHanded();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startOneHandedMode", e);
+ }
+ }
+ }
+
+ public void stopOneHandedMode() {
+ if (mOneHanded != null) {
+ try {
+ mOneHanded.stopOneHanded();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stopOneHandedMode", e);
+ }
+ }
+ }
+
+ //
+ // Remote transitions
+ //
+
+ public void registerRemoteTransition(
+ RemoteTransition remoteTransition, TransitionFilter filter) {
+ if (mShellTransitions != null) {
+ try {
+ mShellTransitions.registerRemote(filter, remoteTransition);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerRemoteTransition");
+ }
+ }
+ if (!mRemoteTransitions.containsKey(remoteTransition)) {
+ mRemoteTransitions.put(remoteTransition, filter);
+ }
+ }
+
+ public void unregisterRemoteTransition(RemoteTransition remoteTransition) {
+ if (mShellTransitions != null) {
+ try {
+ mShellTransitions.unregisterRemote(remoteTransition);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerRemoteTransition");
+ }
+ }
+ mRemoteTransitions.remove(remoteTransition);
+ }
+
+ public void setHomeTransitionListener(IHomeTransitionListener listener) {
+ if (!FeatureFlags.enableHomeTransitionListener()) {
+ return;
+ }
+
+ mHomeTransitionListener = listener;
+
+ if (mShellTransitions != null) {
+ try {
+ mShellTransitions.setHomeTransitionListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setHomeTransitionListener", e);
+ }
+ } else {
+ Log.w(TAG, "Unable to call setHomeTransitionListener because ShellTransitions is null");
+ }
+ }
+
+ /**
+ * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary
+ * if Launcher and SystemUI need to coordinate transactions (eg. for shell transitions).
+ */
+ public void shareTransactionQueue() {
+ if (mOriginalTransactionToken == null) {
+ mOriginalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken();
+ }
+ setupTransactionQueue();
+ }
+
+ /**
+ * Switch back to using Launcher's independent transaction queue.
+ */
+ public void unshareTransactionQueue() {
+ if (mOriginalTransactionToken == null) {
+ return;
+ }
+ SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
+ mOriginalTransactionToken = null;
+ }
+
+ private void setupTransactionQueue() {
+ if (mOriginalTransactionToken == null) {
+ return;
+ }
+ if (mShellTransitions == null) {
+ SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
+ return;
+ }
+ final IBinder shellApplyToken;
+ try {
+ shellApplyToken = mShellTransitions.getShellApplyToken();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting Shell's apply token", e);
+ return;
+ }
+ if (shellApplyToken == null) {
+ Log.e(TAG, "Didn't receive apply token from Shell");
+ return;
+ }
+ SurfaceControl.Transaction.setDefaultApplyToken(shellApplyToken);
+ }
+
+ //
+ // Starting window
+ //
+
+ /**
+ * Sets listener to get callbacks when launching a task.
+ */
+ public void setStartingWindowListener(IStartingWindowListener listener) {
+ if (mStartingWindow != null) {
+ try {
+ mStartingWindow.setStartingWindowListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setStartingWindowListener", e);
+ }
+ }
+ mStartingWindowListener = listener;
+ }
+
+ //
+ // SmartSpace transitions
+ //
+
+ /**
+ * Sets the instance of {@link ILauncherUnlockAnimationController} that System UI should use to
+ * control the launcher side of the unlock animation. This will also cause us to dispatch the
+ * current state of the smartspace to System UI (this will subsequently happen if the state
+ * changes).
+ */
+ public void setLauncherUnlockAnimationController(
+ String activityClass, ILauncherUnlockAnimationController controller) {
+ if (mSysuiUnlockAnimationController != null) {
+ try {
+ mSysuiUnlockAnimationController.setLauncherUnlockController(
+ activityClass, controller);
+ if (controller != null) {
+ controller.dispatchSmartspaceStateToSysui();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setLauncherUnlockAnimationController", e);
+ }
+ }
+ mLauncherActivityClass = activityClass;
+ mLauncherUnlockAnimationController = controller;
+ }
+
+ /**
+ * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
+ * the unlock animation accordingly.
+ */
+ public void notifySysuiSmartspaceStateUpdated(SmartspaceState state) {
+ if (mSysuiUnlockAnimationController != null) {
+ try {
+ mSysuiUnlockAnimationController.onLauncherSmartspaceStateUpdated(state);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call notifySysuiSmartspaceStateUpdated", e);
+ e.printStackTrace();
+ }
+ }
+ }
+
+ //
+ // Recents
+ //
+
+ public void registerRecentTasksListener(IRecentTasksListener listener) {
+ if (mRecentTasks != null) {
+ try {
+ mRecentTasks.registerRecentTasksListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerRecentTasksListener", e);
+ }
+ }
+ mRecentTasksListener = listener;
+ }
+
+ public void unregisterRecentTasksListener(IRecentTasksListener listener) {
+ if (mRecentTasks != null) {
+ try {
+ mRecentTasks.unregisterRecentTasksListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call unregisterRecentTasksListener");
+ }
+ }
+ mRecentTasksListener = null;
+ }
+
+ //
+ // Back navigation transitions
+ //
+
+ /** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */
+ public void setBackToLauncherCallback(IOnBackInvokedCallback callback,
+ IRemoteAnimationRunner runner) {
+ mBackToLauncherCallback = callback;
+ mBackToLauncherRunner = runner;
+ if (mBackAnimation == null || mBackToLauncherCallback == null) {
+ return;
+ }
+ try {
+ mBackAnimation.setBackToLauncherCallback(callback, runner);
+ } catch (RemoteException | SecurityException e) {
+ Log.e(TAG, "Failed call setBackToLauncherCallback", e);
+ }
+ }
+
+ /** Clears the previously registered {@link IOnBackInvokedCallback}.
+ *
+ * @param callback The previously registered callback instance.
+ */
+ public void clearBackToLauncherCallback(IOnBackInvokedCallback callback) {
+ if (mBackToLauncherCallback != callback) {
+ return;
+ }
+ mBackToLauncherCallback = null;
+ mBackToLauncherRunner = null;
+ if (mBackAnimation == null) {
+ return;
+ }
+ try {
+ mBackAnimation.clearBackToLauncherCallback();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed call clearBackToLauncherCallback", e);
+ }
+ }
+
+ /**
+ * Called when the status bar color needs to be customized when back navigation.
+ */
+ public void customizeStatusBarAppearance(AppearanceRegion appearance) {
+ if (mBackAnimation == null) {
+ return;
+ }
+ try {
+ mBackAnimation.customizeStatusBarAppearance(appearance);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed call useLauncherSysBarFlags", e);
+ }
+ }
+
+ public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
+ if (mRecentTasks == null) {
+ Log.w(TAG, "getRecentTasks() failed due to null mRecentTasks");
+ return new ArrayList<>();
+ }
+ try {
+ final GroupedRecentTaskInfo[] rawTasks = mRecentTasks.getRecentTasks(numTasks,
+ RECENT_IGNORE_UNAVAILABLE, userId);
+ if (rawTasks == null) {
+ return new ArrayList<>();
+ }
+ return new ArrayList<>(Arrays.asList(rawTasks));
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call getRecentTasks", e);
+ return new ArrayList<>();
+ }
+ }
+
+ /**
+ * Gets the set of running tasks.
+ */
+ public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
+ if (mRecentTasks != null
+ && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
+ try {
+ return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call getRunningTasks", e);
+ }
+ }
+ return new ArrayList<>();
+ }
+
+ private boolean handleMessageAsync(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_SHELF_HEIGHT:
+ setShelfHeightAsync(msg.arg1, msg.arg2);
+ return true;
+ case MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT:
+ setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2);
+ return true;
+ }
+
+ return false;
+ }
+
+ //
+ // Desktop Mode
+ //
+
+ /** Call shell to show all apps active on the desktop */
+ public void showDesktopApps(int displayId, @Nullable RemoteTransition transition) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.showDesktopApps(displayId, transition);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call showDesktopApps", e);
+ }
+ }
+ }
+
+ /** Call shell to stash desktop apps */
+ public void stashDesktopApps(int displayId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.stashDesktopApps(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stashDesktopApps", e);
+ }
+ }
+ }
+
+ /** Call shell to hide desktop apps that may be stashed */
+ public void hideStashedDesktopApps(int displayId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.hideStashedDesktopApps(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call hideStashedDesktopApps", e);
+ }
+ }
+ }
+
+ /**
+ * If task with the given id is on the desktop, bring it to front
+ */
+ public void showDesktopApp(int taskId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.showDesktopApp(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call showDesktopApp", e);
+ }
+ }
+ }
+
+ /** Call shell to get number of visible freeform tasks */
+ public int getVisibleDesktopTaskCount(int displayId) {
+ if (mDesktopMode != null) {
+ try {
+ return mDesktopMode.getVisibleTaskCount(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call getVisibleDesktopTaskCount", e);
+ }
+ }
+ return 0;
+ }
+
+ /** Set a listener on shell to get updates about desktop task state */
+ public void setDesktopTaskListener(@Nullable IDesktopTaskListener listener) {
+ mDesktopTaskListener = listener;
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.setTaskListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setDesktopTaskListener", e);
+ }
+ }
+ }
+
+ /** Perform cleanup transactions after animation to split select is complete */
+ public void onDesktopSplitSelectAnimComplete(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.onDesktopSplitSelectAnimComplete(taskInfo);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call onDesktopSplitSelectAnimComplete", e);
+ }
+ }
+ }
+
+ //
+ // Unfold transition
+ //
+
+ /** Sets the unfold animation lister to sysui. */
+ public void setUnfoldAnimationListener(IUnfoldTransitionListener callback) {
+ mUnfoldAnimationListener = callback;
+ if (mUnfoldAnimation == null) {
+ return;
+ }
+ try {
+ Log.d(TAG, "Registering unfold animation receiver");
+ mUnfoldAnimation.setListener(callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed call setUnfoldAnimationListener", e);
+ }
+ }
+
+ //
+ // Recents
+ //
+
+ /**
+ * Starts the recents activity. The caller should manage the thread on which this is called.
+ */
+ public boolean startRecentsActivity(Intent intent, ActivityOptions options,
+ RecentsAnimationListener listener) {
+ if (mRecentTasks == null) {
+ ActiveGestureLog.INSTANCE.addLog("Null mRecentTasks", RECENT_TASKS_MISSING);
+ return false;
+ }
+ final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(IRecentsAnimationController controller,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras) {
+ // Aidl bundles need to explicitly set class loader
+ // https://developer.android.com/guide/components/aidl#Bundles
+ if (extras != null) {
+ extras.setClassLoader(getClass().getClassLoader());
+ }
+ listener.onAnimationStart(new RecentsAnimationControllerCompat(controller), apps,
+ wallpapers, homeContentInsets, minimizedHomeBounds, extras);
+ }
+
+ @Override
+ public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
+ listener.onAnimationCanceled(
+ ThumbnailData.wrap(taskIds, taskSnapshots));
+ }
+
+ @Override
+ public void onTasksAppeared(RemoteAnimationTarget[] apps) {
+ listener.onTasksAppeared(apps);
+ }
+ };
+ final Bundle optsBundle = options.toBundle();
+ try {
+ mRecentTasks.startRecentsTransition(mRecentsPendingIntent, intent, optsBundle,
+ mContext.getIApplicationThread(), runner);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error starting recents via shell", e);
+ return false;
+ }
+ }
+
+ //
+ // Drag and drop
+ //
+
+ /**
+ * For testing purposes. Returns `true` only if the shell drop target has shown and
+ * drawn and is ready to handle drag events and the subsequent drop.
+ */
+ public boolean isDragAndDropReady() {
+ if (mDragAndDrop == null) {
+ return false;
+ }
+ try {
+ return mDragAndDrop.isReadyToHandleDrag();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying drag state", e);
+ return false;
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println(TAG + ":");
+
+ pw.println("\tmSystemUiProxy=" + mSystemUiProxy);
+ pw.println("\tmPip=" + mPip);
+ pw.println("\tmPipAnimationListener=" + mPipAnimationListener);
+ pw.println("\tmBubbles=" + mBubbles);
+ pw.println("\tmBubblesListener=" + mBubblesListener);
+ pw.println("\tmSplitScreen=" + mSplitScreen);
+ pw.println("\tmSplitScreenListener=" + mSplitScreenListener);
+ pw.println("\tmSplitSelectListener=" + mSplitSelectListener);
+ pw.println("\tmOneHanded=" + mOneHanded);
+ pw.println("\tmShellTransitions=" + mShellTransitions);
+ pw.println("\tmHomeTransitionListener=" + mHomeTransitionListener);
+ pw.println("\tmStartingWindow=" + mStartingWindow);
+ pw.println("\tmStartingWindowListener=" + mStartingWindowListener);
+ pw.println("\tmSysuiUnlockAnimationController=" + mSysuiUnlockAnimationController);
+ pw.println("\tmLauncherActivityClass=" + mLauncherActivityClass);
+ pw.println("\tmLauncherUnlockAnimationController=" + mLauncherUnlockAnimationController);
+ pw.println("\tmRecentTasks=" + mRecentTasks);
+ pw.println("\tmRecentTasksListener=" + mRecentTasksListener);
+ pw.println("\tmBackAnimation=" + mBackAnimation);
+ pw.println("\tmBackToLauncherCallback=" + mBackToLauncherCallback);
+ pw.println("\tmBackToLauncherRunner=" + mBackToLauncherRunner);
+ pw.println("\tmDesktopMode=" + mDesktopMode);
+ pw.println("\tmDesktopTaskListener=" + mDesktopTaskListener);
+ pw.println("\tmUnfoldAnimation=" + mUnfoldAnimation);
+ pw.println("\tmUnfoldAnimationListener=" + mUnfoldAnimationListener);
+ pw.println("\tmDragAndDrop=" + mDragAndDrop);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
deleted file mode 100644
index c70642a..0000000
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.kt
+++ /dev/null
@@ -1,1232 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep
-
-import android.app.ActivityManager
-import android.app.ActivityManager.RunningTaskInfo
-import android.app.ActivityOptions
-import android.app.PendingIntent
-import android.app.PictureInPictureParams
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.content.pm.PackageManager
-import android.content.pm.ShortcutInfo
-import android.graphics.Point
-import android.graphics.Rect
-import android.os.Bundle
-import android.os.Handler
-import android.os.IBinder
-import android.os.Message
-import android.os.RemoteException
-import android.os.UserHandle
-import android.util.Log
-import android.view.IRecentsAnimationController
-import android.view.IRecentsAnimationRunner
-import android.view.IRemoteAnimationRunner
-import android.view.MotionEvent
-import android.view.RemoteAnimationAdapter
-import android.view.RemoteAnimationTarget
-import android.view.SurfaceControl
-import android.window.IOnBackInvokedCallback
-import android.window.RemoteTransition
-import android.window.TaskSnapshot
-import android.window.TransitionFilter
-import androidx.annotation.MainThread
-import androidx.annotation.WorkerThread
-import com.android.internal.logging.InstanceId
-import com.android.internal.util.ScreenshotRequest
-import com.android.internal.view.AppearanceRegion
-import com.android.launcher3.config.FeatureFlags
-import com.android.launcher3.util.Executors
-import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
-import com.android.launcher3.util.MainThreadInitializedObject
-import com.android.launcher3.util.Preconditions
-import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
-import com.android.quickstep.util.ActiveGestureErrorDetector
-import com.android.quickstep.util.ActiveGestureLog
-import com.android.quickstep.util.AssistUtils
-import com.android.quickstep.util.LogUtils.splitFailureMessage
-import com.android.systemui.shared.recents.ISystemUiProxy
-import com.android.systemui.shared.recents.model.ThumbnailData
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat
-import com.android.systemui.shared.system.RecentsAnimationListener
-import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
-import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController
-import com.android.systemui.shared.system.smartspace.SmartspaceState
-import com.android.systemui.unfold.progress.IUnfoldAnimation
-import com.android.systemui.unfold.progress.IUnfoldTransitionListener
-import com.android.wm.shell.back.IBackAnimation
-import com.android.wm.shell.bubbles.IBubbles
-import com.android.wm.shell.bubbles.IBubblesListener
-import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition
-import com.android.wm.shell.desktopmode.IDesktopMode
-import com.android.wm.shell.desktopmode.IDesktopTaskListener
-import com.android.wm.shell.draganddrop.IDragAndDrop
-import com.android.wm.shell.onehanded.IOneHanded
-import com.android.wm.shell.pip.IPip
-import com.android.wm.shell.pip.IPipAnimationListener
-import com.android.wm.shell.recents.IRecentTasks
-import com.android.wm.shell.recents.IRecentTasksListener
-import com.android.wm.shell.splitscreen.ISplitScreen
-import com.android.wm.shell.splitscreen.ISplitScreenListener
-import com.android.wm.shell.splitscreen.ISplitSelectListener
-import com.android.wm.shell.startingsurface.IStartingWindow
-import com.android.wm.shell.startingsurface.IStartingWindowListener
-import com.android.wm.shell.transition.IHomeTransitionListener
-import com.android.wm.shell.transition.IShellTransitions
-import com.android.wm.shell.util.GroupedRecentTaskInfo
-import java.io.PrintWriter
-
-/** Holds the reference to SystemUI. */
-class SystemUiProxy(private val context: Context) {
-
- private val asyncHandler = Handler(UI_HELPER_EXECUTOR.looper, this::handleMessageAsync)
-
- /**
- * This is a singleton pending intent that is used to start recents via Shell (which is a
- * different process). It is bare-bones, so it's expected that the component and options will be
- * provided via fill-in intent.
- */
- private val recentsPendingIntent =
- PendingIntent.getActivity(
- context,
- 0,
- Intent().setPackage(context.packageName),
- PendingIntent.FLAG_MUTABLE or
- PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or
- Intent.FILL_IN_COMPONENT,
- ActivityOptions.makeBasic()
- .setPendingIntentCreatorBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
- )
- .toBundle()
- )
-
- private val systemUiProxyDeathRecipient =
- IBinder.DeathRecipient { Executors.MAIN_EXECUTOR.execute { clearProxy() } }
-
- private var systemUiProxy: ISystemUiProxy? = null
-
- private var pip: IPip? = null
- private var bubbles: IBubbles? = null
- private var sysuiUnlockAnimationController: ISysuiUnlockAnimationController? = null
- private var splitScreen: ISplitScreen? = null
- private var oneHanded: IOneHanded? = null
- private var shellTransitions: IShellTransitions? = null
- private var startingWindow: IStartingWindow? = null
- private var recentTasks: IRecentTasks? = null
- private var backAnimation: IBackAnimation? = null
- private var desktopMode: IDesktopMode? = null
- private var unfoldAnimation: IUnfoldAnimation? = null
-
- // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
- // yet, and we'll need to set/register these listeners with SysUI when they do. Note that it is
- // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
- // in case SysUI needs to rebind.
- private var pipAnimationListener: IPipAnimationListener? = null
- private var bubblesListener: IBubblesListener? = null
- private var splitScreenListener: ISplitScreenListener? = null
- private var splitSelectListener: ISplitSelectListener? = null
- private var startingWindowListener: IStartingWindowListener? = null
- private var launcherUnlockAnimationController: ILauncherUnlockAnimationController? = null
- private var launcherActivityClass: String? = null
- private var recentTasksListener: IRecentTasksListener? = null
- private var unfoldAnimationListener: IUnfoldTransitionListener? = null
- private var desktopTaskListener: IDesktopTaskListener? = null
- private val remoteTransitions = LinkedHashMap<RemoteTransition, TransitionFilter>()
- private var originalTransactionToken: IBinder? = null
- private var backToLauncherCallback: IOnBackInvokedCallback? = null
- private var backToLauncherRunner: IRemoteAnimationRunner? = null
- private var dragAndDrop: IDragAndDrop? = null
- private var homeTransitionListener: IHomeTransitionListener? = null
-
- // Used to dedupe calls to SystemUI
- private var lastShelfHeight = 0
- private var lastShelfVisible = false
-
- // Used to dedupe calls to SystemUI
- private var lastLauncherKeepClearAreaHeight = 0
- private var lastLauncherKeepClearAreaHeightVisible = false
-
- // TODO(141886704): Find a way to remove this
- var lastSystemUiStateFlags = 0
-
- /**
- * Sets proxy state, including death linkage, various listeners, and other configuration objects
- */
- @MainThread
- fun setProxy(
- proxy: ISystemUiProxy?,
- pip: IPip?,
- bubbles: IBubbles?,
- splitScreen: ISplitScreen?,
- oneHanded: IOneHanded?,
- shellTransitions: IShellTransitions?,
- startingWindow: IStartingWindow?,
- recentTasks: IRecentTasks?,
- sysuiUnlockAnimationController: ISysuiUnlockAnimationController?,
- backAnimation: IBackAnimation?,
- desktopMode: IDesktopMode?,
- unfoldAnimation: IUnfoldAnimation?,
- dragAndDrop: IDragAndDrop?
- ) {
- Preconditions.assertUIThread()
- unlinkToDeath()
- systemUiProxy = proxy
- this.pip = pip
- this.bubbles = bubbles
- this.splitScreen = splitScreen
- this.oneHanded = oneHanded
- this.shellTransitions = shellTransitions
- this.startingWindow = startingWindow
- this.sysuiUnlockAnimationController = sysuiUnlockAnimationController
- this.recentTasks = recentTasks
- this.backAnimation = backAnimation
- this.desktopMode = desktopMode
- this.unfoldAnimation = unfoldAnimation
- this.dragAndDrop = dragAndDrop
- linkToDeath()
- // re-attach the listeners once missing due to setProxy has not been initialized yet.
- setPipAnimationListener(pipAnimationListener)
- setBubblesListener(bubblesListener)
- registerSplitScreenListener(splitScreenListener)
- registerSplitSelectListener(splitSelectListener)
- setHomeTransitionListener(homeTransitionListener)
- setStartingWindowListener(startingWindowListener)
- setLauncherUnlockAnimationController(
- launcherActivityClass,
- launcherUnlockAnimationController
- )
- LinkedHashMap(remoteTransitions).forEach(this::registerRemoteTransition)
- setupTransactionQueue()
- registerRecentTasksListener(recentTasksListener)
- setBackToLauncherCallback(backToLauncherCallback, backToLauncherRunner)
- setUnfoldAnimationListener(unfoldAnimationListener)
- setDesktopTaskListener(desktopTaskListener)
- setAssistantOverridesRequested(
- AssistUtils.newInstance(context).sysUiAssistOverrideInvocationTypes
- )
- }
-
- /**
- * Clear the proxy to release held resources and turn the majority of its operations into no-ops
- */
- @MainThread
- fun clearProxy() {
- setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null)
- }
-
- val isActive: Boolean
- get() = systemUiProxy != null
-
- private fun linkToDeath() {
- tryOrLog("Failed to link sysui proxy death recipient") {
- systemUiProxy?.asBinder()?.linkToDeath(systemUiProxyDeathRecipient, 0 /* flags */)
- }
- }
-
- private fun unlinkToDeath() {
- systemUiProxy?.asBinder()?.unlinkToDeath(systemUiProxyDeathRecipient, 0 /* flags */)
- }
-
- fun onBackPressed() {
- tryOrLog("Failed call onBackPressed") { systemUiProxy?.onBackPressed() }
- }
-
- fun onImeSwitcherPressed() {
- tryOrLog("Failed call onImeSwitcherPressed") { systemUiProxy?.onImeSwitcherPressed() }
- }
-
- fun setHomeRotationEnabled(enabled: Boolean) {
- tryOrLog("Failed call setHomeRotationEnabled") {
- systemUiProxy?.setHomeRotationEnabled(enabled)
- }
- }
-
- fun startScreenPinning(taskId: Int) {
- tryOrLog("Failed call startScreenPinning") { systemUiProxy?.startScreenPinning(taskId) }
- }
-
- fun onOverviewShown(fromHome: Boolean, tag: String?) {
- try {
- systemUiProxy?.onOverviewShown(fromHome)
- } catch (e: RemoteException) {
- Log.w(tag, "Failed call onOverviewShown from: ${if (fromHome) "home" else "app"}", e)
- }
- }
-
- @MainThread
- fun onStatusBarTouchEvent(event: MotionEvent) {
- Preconditions.assertUIThread()
- tryOrLog("Failed call onStatusBarTouchEvent with arg: $event") {
- systemUiProxy?.onStatusBarTouchEvent(event)
- }
- }
-
- fun onStatusBarTrackpadEvent(event: MotionEvent) {
- tryOrLog("Failed call onStatusBarTrackpadEvent with arg: $event") {
- systemUiProxy?.onStatusBarTrackpadEvent(event)
- }
- }
-
- fun onAssistantProgress(progress: Float) {
- tryOrLog("Failed call onAssistantProgress with progress: $progress") {
- systemUiProxy?.onAssistantProgress(progress)
- }
- }
-
- fun onAssistantGestureCompletion(velocity: Float) {
- tryOrLog("Failed call onAssistantGestureCompletion") {
- systemUiProxy?.onAssistantGestureCompletion(velocity)
- }
- }
-
- fun startAssistant(args: Bundle?) {
- tryOrLog("Failed call startAssistant") { systemUiProxy?.startAssistant(args) }
- }
-
- fun setAssistantOverridesRequested(invocationTypes: IntArray?) {
- tryOrLog("Failed call setAssistantOverridesRequested") {
- systemUiProxy?.setAssistantOverridesRequested(invocationTypes)
- }
- }
-
- fun animateNavBarLongPress(isTouchDown: Boolean, shrink:Boolean, durationMs: Long) {
- tryOrLog("Failed call animateNavBarLongPress") {
- systemUiProxy?.animateNavBarLongPress(isTouchDown, shrink, durationMs)
- }
- }
-
- fun notifyAccessibilityButtonClicked(displayId: Int) {
- tryOrLog("Failed call notifyAccessibilityButtonClicked") {
- systemUiProxy?.notifyAccessibilityButtonClicked(displayId)
- }
- }
-
- fun notifyAccessibilityButtonLongClicked() {
- tryOrLog("Failed call notifyAccessibilityButtonLongClicked") {
- systemUiProxy?.notifyAccessibilityButtonLongClicked()
- }
- }
-
- fun stopScreenPinning() {
- tryOrLog("Failed call stopScreenPinning") { systemUiProxy?.stopScreenPinning() }
- }
-
- fun notifyPrioritizedRotation(rotation: Int) {
- tryOrLog("Failed call notifyPrioritizedRotation with arg: $rotation") {
- systemUiProxy?.notifyPrioritizedRotation(rotation)
- }
- }
-
- fun notifyTaskbarStatus(visible: Boolean, stashed: Boolean) {
- tryOrLog("Failed call notifyTaskbarStatus with arg: $visible, $stashed") {
- systemUiProxy?.notifyTaskbarStatus(visible, stashed)
- }
- }
-
- /**
- * NOTE: If called to suspend, caller MUST call this method to also un-suspend
- *
- * @param suspend should be true to stop auto-hide, false to resume normal behavior
- */
- fun notifyTaskbarAutohideSuspend(suspend: Boolean) {
- tryOrLog("Failed call notifyTaskbarAutohideSuspend with arg: $suspend") {
- systemUiProxy?.notifyTaskbarAutohideSuspend(suspend)
- }
- }
-
- fun takeScreenshot(request: ScreenshotRequest?) {
- tryOrLog("Failed call takeScreenshot") { systemUiProxy?.takeScreenshot(request) }
- }
-
- fun expandNotificationPanel() {
- tryOrLog("Failed call expandNotificationPanel") { systemUiProxy?.expandNotificationPanel() }
- }
-
- fun toggleNotificationPanel() {
- tryOrLog("Failed call toggleNotificationPanel") { systemUiProxy?.toggleNotificationPanel() }
- }
-
- //
- // Pip
- //
- /** Sets the shelf height. */
- fun setShelfHeight(visible: Boolean, shelfHeight: Int) {
- Message.obtain(asyncHandler, MSG_SET_SHELF_HEIGHT, if (visible) 1 else 0, shelfHeight)
- .sendToTarget()
- }
-
- @WorkerThread
- private fun setShelfHeightAsync(visibleInt: Int, shelfHeight: Int) {
- val visible = visibleInt != 0
- if (visible == lastShelfVisible && shelfHeight == lastShelfHeight) return
-
- pip?.let {
- tryOrLog("Failed call setShelfHeight visible: $visible height: $shelfHeight") {
- it.setShelfHeight(visible, shelfHeight)
- lastShelfVisible = visible
- lastShelfHeight = shelfHeight
- }
- }
- }
-
- /**
- * Sets the height of the keep clear area that is going to be reported by the Launcher for the
- * Hotseat.
- */
- fun setLauncherKeepClearAreaHeight(visible: Boolean, height: Int) {
- Message.obtain(
- asyncHandler,
- MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT,
- if (visible) 1 else 0,
- height
- )
- .sendToTarget()
- }
-
- @WorkerThread
- private fun setLauncherKeepClearAreaHeight(visibleInt: Int, height: Int) {
- val visible = visibleInt != 0
- if (
- visible == lastLauncherKeepClearAreaHeightVisible &&
- height == lastLauncherKeepClearAreaHeight
- )
- return
-
- pip?.let {
- tryOrLog("Failed call setLauncherKeepClearAreaHeight vis: $visible height: $height") {
- it.setLauncherKeepClearAreaHeight(visible, height)
- lastLauncherKeepClearAreaHeightVisible = visible
- lastLauncherKeepClearAreaHeight = height
- }
- }
- }
-
- /** Sets listener to get pip animation callbacks. */
- fun setPipAnimationListener(listener: IPipAnimationListener?) {
- tryOrLog("Failed call setPinnedStackAnimationListener") {
- pip?.setPipAnimationListener(listener)
- }
- pipAnimationListener = listener
- }
-
- /** @return Destination bounds of auto-pip animation, `null` if the animation is not ready. */
- fun startSwipePipToHome(
- componentName: ComponentName?,
- activityInfo: ActivityInfo?,
- pictureInPictureParams: PictureInPictureParams?,
- launcherRotation: Int,
- hotseatKeepClearArea: Rect?
- ): Rect? {
- return tryOrElse("Failed call startSwipePipToHome", null) {
- return pip?.startSwipePipToHome(
- componentName,
- activityInfo,
- pictureInPictureParams,
- launcherRotation,
- hotseatKeepClearArea
- )
- }
- }
-
- /**
- * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to
- * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell
- * should be responsible for cleaning up the overlay.
- */
- fun stopSwipePipToHome(
- taskId: Int,
- componentName: ComponentName?,
- destinationBounds: Rect?,
- overlay: SurfaceControl?
- ) {
- tryOrLog("Failed call stopSwipePipToHome") {
- pip?.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay)
- }
- }
-
- /**
- * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell can
- * use this callback to clean up its internal states.
- */
- fun abortSwipePipToHome(taskId: Int, componentName: ComponentName?) {
- tryOrLog("Failed call abortSwipePipToHome") {
- pip?.abortSwipePipToHome(taskId, componentName)
- }
- }
-
- /** Sets the next pip animation type to be the alpha animation. */
- fun setPipAnimationTypeToAlpha() {
- tryOrLog("Failed call setPipAnimationTypeToAlpha") { pip?.setPipAnimationTypeToAlpha() }
- }
-
- /** Sets the app icon size in pixel used by Launcher all apps. */
- fun setLauncherAppIconSize(iconSizePx: Int) {
- tryOrLog("Failed call setLauncherAppIconSize") { pip?.setLauncherAppIconSize(iconSizePx) }
- }
-
- //
- // Bubbles
- //
- /** Sets the listener to be notified of bubble state changes. */
- fun setBubblesListener(listener: IBubblesListener?) {
- bubbles?.let {
- tryOrLog("Failed call setLauncherAppIconSize") {
- bubblesListener?.let(it::unregisterBubbleListener)
- listener?.let(it::registerBubbleListener)
- }
- }
- bubblesListener = listener
- }
-
- /**
- * Tells SysUI to show the bubble with the provided key.
- *
- * @param key the key of the bubble to show.
- * @param bubbleBarOffsetX the offset of the bubble bar from the edge of the screen on the X
- * axis.
- * @param bubbleBarOffsetY the offset of the bubble bar from the edge of the screen on the Y
- * axis.
- */
- fun showBubble(key: String?, bubbleBarOffsetX: Int, bubbleBarOffsetY: Int) {
- tryOrLog("Failed call showBubble") {
- bubbles?.showBubble(key, bubbleBarOffsetX, bubbleBarOffsetY)
- }
- }
-
- /**
- * Tells SysUI to remove the bubble with the provided key.
- *
- * @param key the key of the bubble to show.
- */
- fun removeBubble(key: String?) {
- tryOrLog("Failed call removeBubble") { bubbles?.removeBubble(key) }
- }
-
- /** Tells SysUI to remove all bubbles. */
- fun removeAllBubbles() {
- tryOrLog("Failed call removeAllBubbles") { bubbles?.removeAllBubbles() }
- }
-
- /** Tells SysUI to collapse the bubbles. */
- fun collapseBubbles() {
- tryOrLog("Failed call collapseBubbles") { bubbles?.collapseBubbles() }
- }
-
- /**
- * Tells SysUI when the bubble is being dragged. Should be called only when the bubble bar is
- * expanded.
- *
- * @param bubbleKey the key of the bubble to collapse/expand
- * @param isBeingDragged whether the bubble is being dragged
- */
- fun onBubbleDrag(bubbleKey: String?, isBeingDragged: Boolean) {
- tryOrLog("Failed call onBubbleDrag") { bubbles?.onBubbleDrag(bubbleKey, isBeingDragged) }
- }
-
- /**
- * Tells SysUI to show user education relative to the reference point provided.
- *
- * @param position the bubble bar top center position in Screen coordinates.
- */
- fun showUserEducation(position: Point) {
- tryOrLog("Failed call showUserEducation") {
- bubbles?.showUserEducation(position.x, position.y)
- }
- }
-
- //
- // Splitscreen
- //
- fun registerSplitScreenListener(listener: ISplitScreenListener?) {
- tryOrLog("Failed call registerSplitScreenListener") {
- splitScreen?.registerSplitScreenListener(listener)
- }
- splitScreenListener = listener
- }
-
- fun registerSplitSelectListener(listener: ISplitSelectListener?) {
- tryOrLog("Failed call registerSplitScreenListener") {
- splitScreen?.registerSplitSelectListener(listener)
- }
- splitSelectListener = listener
- }
-
- /** Start multiple tasks in split-screen simultaneously. */
- fun startTasks(
- taskId1: Int,
- options1: Bundle?,
- taskId2: Int,
- options2: Bundle?,
- @StagePosition splitPosition: Int,
- @PersistentSnapPosition snapPosition: Int,
- remoteTransition: RemoteTransition?,
- instanceId: InstanceId?
- ) {
- tryOrLog(splitFailureMessage("startTasks", "RemoteException")) {
- splitScreen?.startTasks(
- taskId1,
- options1,
- taskId2,
- options2,
- splitPosition,
- snapPosition,
- remoteTransition,
- instanceId
- )
- }
- }
-
- fun startIntentAndTask(
- pendingIntent: PendingIntent?,
- userId1: Int,
- options1: Bundle?,
- taskId: Int,
- options2: Bundle?,
- @StagePosition splitPosition: Int,
- @PersistentSnapPosition snapPosition: Int,
- remoteTransition: RemoteTransition?,
- instanceId: InstanceId?
- ) {
- tryOrLog(splitFailureMessage("startIntentAndTask", "RemoteException")) {
- splitScreen?.startIntentAndTask(
- pendingIntent,
- userId1,
- options1,
- taskId,
- options2,
- splitPosition,
- snapPosition,
- remoteTransition,
- instanceId
- )
- }
- }
-
- fun startIntents(
- pendingIntent1: PendingIntent?,
- userId1: Int,
- shortcutInfo1: ShortcutInfo?,
- options1: Bundle?,
- pendingIntent2: PendingIntent?,
- userId2: Int,
- shortcutInfo2: ShortcutInfo?,
- options2: Bundle?,
- @StagePosition splitPosition: Int,
- @PersistentSnapPosition snapPosition: Int,
- remoteTransition: RemoteTransition?,
- instanceId: InstanceId?
- ) {
- tryOrLog(splitFailureMessage("startIntents", "RemoteException")) {
- splitScreen?.startIntents(
- pendingIntent1,
- userId1,
- shortcutInfo1,
- options1,
- pendingIntent2,
- userId2,
- shortcutInfo2,
- options2,
- splitPosition,
- snapPosition,
- remoteTransition,
- instanceId
- )
- }
- }
-
- fun startShortcutAndTask(
- shortcutInfo: ShortcutInfo?,
- options1: Bundle?,
- taskId: Int,
- options2: Bundle?,
- @StagePosition splitPosition: Int,
- @PersistentSnapPosition snapPosition: Int,
- remoteTransition: RemoteTransition?,
- instanceId: InstanceId?
- ) {
- tryOrLog(splitFailureMessage("startShortcutAndTask", "RemoteException")) {
- splitScreen?.startShortcutAndTask(
- shortcutInfo,
- options1,
- taskId,
- options2,
- splitPosition,
- snapPosition,
- remoteTransition,
- instanceId
- )
- }
- }
-
- /** Start multiple tasks in split-screen simultaneously. */
- fun startTasksWithLegacyTransition(
- taskId1: Int,
- options1: Bundle?,
- taskId2: Int,
- options2: Bundle?,
- @StagePosition splitPosition: Int,
- @PersistentSnapPosition snapPosition: Int,
- adapter: RemoteAnimationAdapter?,
- instanceId: InstanceId?
- ) {
- tryOrLog(splitFailureMessage("startTasksWithLegacyTransition", "RemoteException")) {
- splitScreen?.startTasksWithLegacyTransition(
- taskId1,
- options1,
- taskId2,
- options2,
- splitPosition,
- snapPosition,
- adapter,
- instanceId
- )
- }
- }
-
- fun startIntentAndTaskWithLegacyTransition(
- pendingIntent: PendingIntent?,
- userId1: Int,
- options1: Bundle?,
- taskId: Int,
- options2: Bundle?,
- @StagePosition splitPosition: Int,
- @PersistentSnapPosition snapPosition: Int,
- adapter: RemoteAnimationAdapter?,
- instanceId: InstanceId?
- ) {
- tryOrLog(splitFailureMessage("startIntentAndTaskWithLegacyTransition", "RemoteException")) {
- splitScreen?.startIntentAndTaskWithLegacyTransition(
- pendingIntent,
- userId1,
- options1,
- taskId,
- options2,
- splitPosition,
- snapPosition,
- adapter,
- instanceId
- )
- }
- }
-
- fun startShortcutAndTaskWithLegacyTransition(
- shortcutInfo: ShortcutInfo?,
- options1: Bundle?,
- taskId: Int,
- options2: Bundle?,
- @StagePosition splitPosition: Int,
- @PersistentSnapPosition snapPosition: Int,
- adapter: RemoteAnimationAdapter?,
- instanceId: InstanceId?
- ) {
- tryOrLog(
- splitFailureMessage("startShortcutAndTaskWithLegacyTransition", "RemoteException")
- ) {
- splitScreen?.startShortcutAndTaskWithLegacyTransition(
- shortcutInfo,
- options1,
- taskId,
- options2,
- splitPosition,
- snapPosition,
- adapter,
- instanceId
- )
- }
- }
-
- /**
- * Starts a pair of intents or shortcuts in split-screen using legacy transition. Passing a
- * non-null shortcut info means to start the app as a shortcut.
- */
- fun startIntentsWithLegacyTransition(
- pendingIntent1: PendingIntent?,
- userId1: Int,
- shortcutInfo1: ShortcutInfo?,
- options1: Bundle?,
- pendingIntent2: PendingIntent?,
- userId2: Int,
- shortcutInfo2: ShortcutInfo?,
- options2: Bundle?,
- @StagePosition sidePosition: Int,
- @PersistentSnapPosition snapPosition: Int,
- adapter: RemoteAnimationAdapter?,
- instanceId: InstanceId?
- ) {
- tryOrLog(splitFailureMessage("startIntentsWithLegacyTransition", "RemoteException")) {
- splitScreen?.startIntentsWithLegacyTransition(
- pendingIntent1,
- userId1,
- shortcutInfo1,
- options1,
- pendingIntent2,
- userId2,
- shortcutInfo2,
- options2,
- sidePosition,
- snapPosition,
- adapter,
- instanceId
- )
- }
- }
-
- fun startShortcut(
- packageName: String?,
- shortcutId: String?,
- position: Int,
- options: Bundle?,
- user: UserHandle?,
- instanceId: InstanceId?
- ) {
- tryOrLog(splitFailureMessage("startShortcut", "RemoteException")) {
- splitScreen?.startShortcut(packageName, shortcutId, position, options, user, instanceId)
- }
- }
-
- fun startIntent(
- intent: PendingIntent?,
- userId: Int,
- fillInIntent: Intent?,
- position: Int,
- options: Bundle?,
- instanceId: InstanceId?
- ) {
- tryOrLog(splitFailureMessage("startIntent", "RemoteException")) {
- splitScreen?.startIntent(intent, userId, fillInIntent, position, options, instanceId)
- }
- }
-
- /**
- * Call this when going to recents so that shell can set-up and provide appropriate leashes for
- * animation (eg. DividerBar).
- *
- * @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
- */
- fun onGoingToRecentsLegacy(
- apps: Array<RemoteAnimationTarget?>?
- ): Array<RemoteAnimationTarget>? {
- if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) return null
- return tryOrElse("Failed call onGoingToRecentsLegacy", null) {
- return splitScreen?.onGoingToRecentsLegacy(apps)
- }
- }
-
- fun onStartingSplitLegacy(apps: Array<RemoteAnimationTarget?>?): Array<RemoteAnimationTarget>? {
- return tryOrElse("Failed call onStartingSplitLegacy", null) {
- return splitScreen?.onStartingSplitLegacy(apps)
- }
- }
-
- //
- // One handed
- //
- fun startOneHandedMode() {
- tryOrLog("Failed call startOneHandedMode") { oneHanded?.startOneHanded() }
- }
-
- fun stopOneHandedMode() {
- tryOrLog("Failed call startOneHandedstopOneHandedModeMode") { oneHanded?.stopOneHanded() }
- }
-
- //
- // Remote transitions
- //
- fun registerRemoteTransition(remoteTransition: RemoteTransition, filter: TransitionFilter) {
- tryOrLog("Failed call registerRemoteTransition") {
- shellTransitions?.registerRemote(filter, remoteTransition)
- }
- if (!remoteTransitions.containsKey(remoteTransition)) {
- remoteTransitions[remoteTransition] = filter
- }
- }
-
- fun unregisterRemoteTransition(remoteTransition: RemoteTransition) {
- tryOrLog("Failed call unregisterRemoteTransition") {
- shellTransitions?.unregisterRemote(remoteTransition)
- }
- remoteTransitions.remove(remoteTransition)
- }
-
- fun setHomeTransitionListener(listener: IHomeTransitionListener?) {
- if (!FeatureFlags.enableHomeTransitionListener()) return
-
- homeTransitionListener = listener
-
- tryOrLog("Failed call unregisterRemoteTransition") {
- shellTransitions?.setHomeTransitionListener(listener)
- ?: Log.w(
- TAG,
- "Unable to call setHomeTransitionListener because ShellTransitions is null"
- )
- }
- }
-
- /**
- * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary if
- * Launcher and SystemUI need to coordinate transactions (eg. for shell transitions).
- */
- fun shareTransactionQueue() {
- if (originalTransactionToken == null) {
- originalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken()
- }
- setupTransactionQueue()
- }
-
- /** Switch back to using Launcher's independent transaction queue. */
- fun unshareTransactionQueue() {
- originalTransactionToken?.let { SurfaceControl.Transaction.setDefaultApplyToken(it) }
- originalTransactionToken = null
- }
-
- private fun setupTransactionQueue() {
- originalTransactionToken ?: return
-
- var transitions = shellTransitions ?: run {
- SurfaceControl.Transaction.setDefaultApplyToken(originalTransactionToken)
- return
- }
- tryOrLog("Error getting Shell's apply token") {
- transitions.getShellApplyToken()?.apply {
- SurfaceControl.Transaction.setDefaultApplyToken(this)
- } ?: Log.e(TAG, "Didn't receive apply token from Shell")
- }
- }
-
- //
- // Starting window
- //
- /** Sets listener to get callbacks when launching a task. */
- fun setStartingWindowListener(listener: IStartingWindowListener?) {
- tryOrLog("Failed call setStartingWindowListener") {
- startingWindow?.setStartingWindowListener(listener)
- }
- startingWindowListener = listener
- }
-
- //
- // SmartSpace transitions
- //
- /**
- * Sets the instance of [ILauncherUnlockAnimationController] that System UI should use to
- * control the launcher side of the unlock animation. This will also cause us to dispatch the
- * current state of the smartspace to System UI (this will subsequently happen if the state
- * changes).
- */
- fun setLauncherUnlockAnimationController(
- activityClass: String?,
- controller: ILauncherUnlockAnimationController?
- ) {
- tryOrLog("Failed call setLauncherUnlockAnimationController") {
- sysuiUnlockAnimationController?.let {
- it.setLauncherUnlockController(activityClass, controller)
- controller?.dispatchSmartspaceStateToSysui()
- }
- }
-
- launcherActivityClass = activityClass
- launcherUnlockAnimationController = controller
- }
-
- /**
- * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
- * the unlock animation accordingly.
- */
- fun notifySysuiSmartspaceStateUpdated(state: SmartspaceState?) {
- tryOrLog("Failed call notifySysuiSmartspaceStateUpdated") {
- sysuiUnlockAnimationController?.onLauncherSmartspaceStateUpdated(state)
- }
- }
-
- //
- // Recents
- //
- fun registerRecentTasksListener(listener: IRecentTasksListener?) {
- tryOrLog("Failed call registerRecentTasksListener") {
- recentTasks?.registerRecentTasksListener(listener)
- }
- recentTasksListener = listener
- }
-
- //
- // Back navigation transitions
- //
- /** Sets the launcher [android.window.IOnBackInvokedCallback] to shell */
- fun setBackToLauncherCallback(
- callback: IOnBackInvokedCallback?,
- runner: IRemoteAnimationRunner?
- ) {
- backToLauncherCallback = callback
- backToLauncherRunner = runner
-
- callback ?: return
- tryOrLog("Failed call setBackToLauncherCallback") {
- backAnimation?.setBackToLauncherCallback(callback, runner)
- }
- }
-
- /**
- * Clears the previously registered [IOnBackInvokedCallback].
- *
- * @param callback The previously registered callback instance.
- */
- fun clearBackToLauncherCallback(callback: IOnBackInvokedCallback) {
- if (backToLauncherCallback != callback) {
- return
- }
- backToLauncherCallback = null
- backToLauncherRunner = null
- tryOrLog("Failed call clearBackToLauncherCallback") {
- backAnimation?.clearBackToLauncherCallback()
- }
- }
-
- /** Called when the status bar color needs to be customized when back navigation. */
- fun customizeStatusBarAppearance(appearance: AppearanceRegion?) {
- tryOrLog("Failed call customizeStatusBarAppearance") {
- backAnimation?.customizeStatusBarAppearance(appearance)
- }
- }
-
- fun getRecentTasks(numTasks: Int, userId: Int): ArrayList<GroupedRecentTaskInfo> {
- return tryOrElse("Failed call getRecentTasks", ArrayList()) {
- return recentTasks
- ?.getRecentTasks(numTasks, ActivityManager.RECENT_IGNORE_UNAVAILABLE, userId)
- ?.let { ArrayList(it.asList()) }
- ?: ArrayList()
- }
- }
-
- /** Gets the set of running tasks. */
- fun getRunningTasks(numTasks: Int): ArrayList<RunningTaskInfo> {
- if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_PC)) {
- return ArrayList()
- }
-
- return tryOrElse("Failed call getRunningTasks", ArrayList()) {
- recentTasks?.getRunningTasks(numTasks)?.let { ArrayList(it.asList()) } ?: ArrayList()
- }
- }
-
- private fun handleMessageAsync(msg: Message): Boolean {
- when (msg.what) {
- MSG_SET_SHELF_HEIGHT -> {
- setShelfHeightAsync(msg.arg1, msg.arg2)
- return true
- }
- MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT -> {
- setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2)
- return true
- }
- }
- return false
- }
-
- //
- // Desktop Mode
- //
- /** Call shell to show all apps active on the desktop */
- fun showDesktopApps(displayId: Int, transition: RemoteTransition?) {
- tryOrLog("Failed call showDesktopApps") {
- desktopMode?.showDesktopApps(displayId, transition)
- }
- }
-
- /** Call shell to stash desktop apps */
- fun stashDesktopApps(displayId: Int) {
- tryOrLog("Failed call stashDesktopApps") { desktopMode?.stashDesktopApps(displayId) }
- }
-
- /** Call shell to hide desktop apps that may be stashed */
- fun hideStashedDesktopApps(displayId: Int) {
- tryOrLog("Failed call hideStashedDesktopApps") {
- desktopMode?.hideStashedDesktopApps(displayId)
- }
- }
-
- /** If task with the given id is on the desktop, bring it to front */
- fun showDesktopApp(taskId: Int) {
- tryOrLog("Failed call showDesktopApp") { desktopMode?.showDesktopApp(taskId) }
- }
-
- /** Call shell to get number of visible freeform tasks */
- fun getVisibleDesktopTaskCount(displayId: Int): Int {
- return tryOrElse("Failed call getVisibleDesktopTaskCount", 0) {
- return desktopMode?.getVisibleTaskCount(displayId) ?: 0
- }
- }
-
- /** Set a listener on shell to get updates about desktop task state */
- fun setDesktopTaskListener(listener: IDesktopTaskListener?) {
- desktopTaskListener = listener
- tryOrLog("Failed call setDesktopTaskListener") { desktopMode?.setTaskListener(listener) }
- }
-
- /** Perform cleanup transactions after animation to split select is complete */
- fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo?) {
- tryOrLog("Failed call onDesktopSplitSelectAnimComplete") {
- desktopMode?.onDesktopSplitSelectAnimComplete(taskInfo)
- }
- }
- //
- // Unfold transition
- //
- /** Sets the unfold animation lister to sysui. */
- fun setUnfoldAnimationListener(callback: IUnfoldTransitionListener?) {
- unfoldAnimationListener = callback
- tryOrLog("Failed call setUnfoldAnimationListener") {
- unfoldAnimation?.setListener(callback)
- }
- }
-
- //
- // Recents
- //
- /** Starts the recents activity. The caller should manage the thread on which this is called. */
- fun startRecentsActivity(
- intent: Intent?,
- options: ActivityOptions,
- listener: RecentsAnimationListener
- ): Boolean {
-
- val runner: IRecentsAnimationRunner =
- object : IRecentsAnimationRunner.Stub() {
- override fun onAnimationStart(
- controller: IRecentsAnimationController,
- apps: Array<RemoteAnimationTarget>,
- wallpapers: Array<RemoteAnimationTarget>,
- homeContentInsets: Rect,
- minimizedHomeBounds: Rect,
- extras: Bundle?
- ) {
- // Aidl bundles need to explicitly set class loader
- // https://developer.android.com/guide/components/aidl#Bundles
- extras?.classLoader = javaClass.classLoader
- listener.onAnimationStart(
- RecentsAnimationControllerCompat(controller),
- apps,
- wallpapers,
- homeContentInsets,
- minimizedHomeBounds,
- extras
- )
- }
-
- override fun onAnimationCanceled(
- taskIds: IntArray,
- taskSnapshots: Array<TaskSnapshot>
- ) {
- listener.onAnimationCanceled(ThumbnailData.wrap(taskIds, taskSnapshots))
- }
-
- override fun onTasksAppeared(apps: Array<RemoteAnimationTarget>) {
- listener.onTasksAppeared(apps)
- }
- }
-
- return tryOrElse("Error starting recents via shell", false) {
- recentTasks?.let {
- it.startRecentsTransition(
- recentsPendingIntent,
- intent,
- options.toBundle(),
- context.iApplicationThread,
- runner
- )
- return true
- }
- ?: run {
- ActiveGestureLog.INSTANCE.addLog(
- "Null recentTasks",
- ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING
- )
- false
- }
- }
- }
-
- //
- // Drag and drop
- //
-
- /**
- * For testing purposes. Returns `true` only if the shell drop target has shown and drawn and is
- * ready to handle drag events and the subsequent drop.
- */
- val isDragAndDropReady: Boolean
- get() =
- tryOrElse("Error querying drag state", false) {
- dragAndDrop?.isReadyToHandleDrag ?: false
- }
-
- fun dump(pw: PrintWriter) {
- pw.println("$TAG:")
- pw.println("\tsystemUiProxy=$systemUiProxy")
- pw.println("\tpip=$pip")
- pw.println("\tpipAnimationListener=$pipAnimationListener")
- pw.println("\tbubbles=$bubbles")
- pw.println("\tbubblesListener=$bubblesListener")
- pw.println("\tsplitScreen=$splitScreen")
- pw.println("\tsplitScreenListener=$splitScreenListener")
- pw.println("\tsplitSelectListener=$splitSelectListener")
- pw.println("\toneHanded=$oneHanded")
- pw.println("\tshellTransitions=$shellTransitions")
- pw.println("\thomeTransitionListener=$homeTransitionListener")
- pw.println("\tstartingWindow=$startingWindow")
- pw.println("\tstartingWindowListener=$startingWindowListener")
- pw.println("\tsysuiUnlockAnimationController=$sysuiUnlockAnimationController")
- pw.println("\tlauncherActivityClass=$launcherActivityClass")
- pw.println("\tlauncherUnlockAnimationController=$launcherUnlockAnimationController")
- pw.println("\trecentTasks=$recentTasks")
- pw.println("\trecentTasksListener=$recentTasksListener")
- pw.println("\tbackAnimation=$backAnimation")
- pw.println("\tbackToLauncherCallback=$backToLauncherCallback")
- pw.println("\tbackToLauncherRunner=$backToLauncherRunner")
- pw.println("\tdesktopMode=$desktopMode")
- pw.println("\tdesktopTaskListener=$desktopTaskListener")
- pw.println("\tunfoldAnimation=$unfoldAnimation")
- pw.println("\tunfoldAnimationListener=$unfoldAnimationListener")
- pw.println("\tdragAndDrop=$dragAndDrop")
- }
-
- companion object {
- private const val TAG = "SystemUiProxy"
- private const val MSG_SET_SHELF_HEIGHT = 1
- private const val MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2
-
- @JvmField
- val INSTANCE = MainThreadInitializedObject { context: Context -> SystemUiProxy(context) }
-
- private inline fun tryOrLog(msg: String, f: () -> Unit) =
- try {
- f()
- } catch (e: RemoteException) {
- Log.w(TAG, msg, e)
- }
-
- private inline fun <T> tryOrElse(msg: String, fallback: T, f: () -> T): T =
- try {
- f()
- } catch (e: RemoteException) {
- Log.w(TAG, msg, e)
- fallback
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 20a751b..1b3f598 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -31,7 +31,6 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.SparseArray;
-import android.view.accessibility.AccessibilityManager;
import androidx.annotation.WorkerThread;
@@ -45,6 +44,7 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.TaskKeyLruCache;
@@ -62,7 +62,6 @@
public class TaskIconCache implements DisplayInfoChangeListener {
private final Executor mBgExecutor;
- private final AccessibilityManager mAccessibilityManager;
private final Context mContext;
private final TaskKeyLruCache<TaskCacheEntry> mIconCache;
@@ -79,7 +78,6 @@
public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) {
mContext = context;
mBgExecutor = bgExecutor;
- mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mIconProvider = iconProvider;
Resources res = context.getResources();
@@ -238,14 +236,11 @@
if ((index = mDefaultIcons.indexOfKey(userId)) >= 0) {
return mDefaultIcons.valueAt(index).newIcon(mContext);
} else {
- try (BaseIconFactory li = getIconFactory()) {
- BitmapInfo info = mDefaultIconBase.withFlags(
- li.getBitmapFlagOp(new IconOptions()
- .setUser(UserCache.INSTANCE.get(mContext)
- .getUserInfo(UserHandle.of(userId)))));
- mDefaultIcons.put(userId, info);
- return info.newIcon(mContext);
- }
+ BitmapInfo info = mDefaultIconBase.withFlags(
+ UserCache.INSTANCE.get(mContext).getUserInfo(UserHandle.of(userId))
+ .applyBitmapInfoFlags(FlagOp.NO_OP));
+ mDefaultIcons.put(userId, info);
+ return info.newIcon(mContext);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/util/AssistStateManager.java b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
index a0bb76d..f7437eb 100644
--- a/quickstep/src/com/android/quickstep/util/AssistStateManager.java
+++ b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
@@ -31,6 +31,11 @@
public AssistStateManager() {}
+ /** Whether search supports haptic on invocation. */
+ public boolean supportsCommitHaptic() {
+ return false;
+ }
+
/** Whether search is available. */
public boolean isSearchAvailable() {
return false;
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
index 423ba43..c013483 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
@@ -93,6 +93,7 @@
private var secondTaskId: Int = INVALID_TASK_ID
private var initialIntent: Intent? = null
private var secondIntent: Intent? = null
+ private var widgetSecondIntent: Intent? = null
private var initialUser: UserHandle? = null
private var secondUser: UserHandle? = null
private var initialPendingIntent: PendingIntent? = null
@@ -167,6 +168,16 @@
secondUser = pendingIntent.creatorUserHandle
}
+ /**
+ * Similar to [setSecondTask] except this is to be called for widgets which can pass through
+ * an extra intent from their RemoteResponse.
+ * See [android.widget.RemoteViews.RemoteResponse.getLaunchOptions].first
+ */
+ fun setSecondWidget(pendingIntent: PendingIntent, widgetIntent: Intent?) {
+ setSecondTask(pendingIntent)
+ widgetSecondIntent = widgetIntent
+ }
+
private fun getShortcutInfo(intent: Intent?, user: UserHandle?): ShortcutInfo? {
val intentPackage = intent?.getPackage() ?: return null
val shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID)
@@ -241,6 +252,7 @@
secondTaskId,
initialPendingIntent,
secondPendingIntent,
+ widgetSecondIntent,
initialUser?.identifier ?: -1,
secondUser?.identifier ?: -1,
initialShortcut,
@@ -257,7 +269,8 @@
* Note that both [initialIntent] and [secondIntent] will be nullified on method return
*
* One caveat is that if [secondPendingIntent] is set, we will use that and *not* attempt to
- * convert [secondIntent]
+ * convert [secondIntent].
+ * This also leaves [widgetSecondIntent] untouched.
*/
private fun convertIntentsToFinalTypes() {
initialShortcut = getShortcutInfo(initialIntent, initialUser)
@@ -343,6 +356,7 @@
var secondTaskId: Int = INVALID_TASK_ID,
var initialPendingIntent: PendingIntent? = null,
var secondPendingIntent: PendingIntent? = null,
+ var widgetSecondIntent: Intent? = null,
var initialUserId: Int = -1,
var secondUserId: Int = -1,
var initialShortcut: ShortcutInfo? = null,
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 145707b..8b27a85 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -32,6 +32,7 @@
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_SHORTCUT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_TASK;
import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import android.animation.Animator;
@@ -355,6 +356,10 @@
mSplitSelectDataHolder.setSecondTask(pendingIntent);
}
+ public void setSecondWidget(PendingIntent pendingIntent, Intent widgetIntent) {
+ mSplitSelectDataHolder.setSecondWidget(pendingIntent, widgetIntent);
+ }
+
/**
* To be called when we want to launch split pairs from Overview. Split can be initiated from
* either Overview or home, or all apps. Either both taskIds are set, or a pending intent + a
@@ -380,11 +385,13 @@
ShortcutInfo secondShortcut = launchData.getSecondShortcut();
PendingIntent firstPI = launchData.getInitialPendingIntent();
PendingIntent secondPI = launchData.getSecondPendingIntent();
+ Intent widgetIntent = launchData.getWidgetSecondIntent();
int firstUserId = launchData.getInitialUserId();
int secondUserId = launchData.getSecondUserId();
int initialStagePosition = launchData.getInitialStagePosition();
Bundle optionsBundle = options1.toBundle();
-
+ Bundle extrasBundle = new Bundle(1);
+ extrasBundle.putParcelable(KEY_EXTRA_WIDGET_INTENT, widgetIntent);
if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
final RemoteTransition remoteTransition = getShellRemoteTransition(firstTaskId,
secondTaskId, callback, "LaunchSplitPair");
@@ -396,7 +403,7 @@
case SPLIT_TASK_PENDINGINTENT ->
mSystemUiProxy.startIntentAndTask(secondPI, secondUserId, optionsBundle,
- firstTaskId, null /*options2*/, initialStagePosition, snapPosition,
+ firstTaskId, extrasBundle, initialStagePosition, snapPosition,
remoteTransition, shellInstanceId);
case SPLIT_TASK_SHORTCUT ->
@@ -411,9 +418,9 @@
case SPLIT_PENDINGINTENT_PENDINGINTENT ->
mSystemUiProxy.startIntents(firstPI, firstUserId, firstShortcut,
- optionsBundle, secondPI, secondUserId, secondShortcut,
- null /*options2*/, initialStagePosition, snapPosition,
- remoteTransition, shellInstanceId);
+ optionsBundle, secondPI, secondUserId, secondShortcut, extrasBundle,
+ initialStagePosition, snapPosition, remoteTransition,
+ shellInstanceId);
case SPLIT_SHORTCUT_TASK ->
mSystemUiProxy.startShortcutAndTask(firstShortcut, optionsBundle,
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index 9313342..e705285 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -72,7 +72,8 @@
* @return {@code true} if we can attempt launch the widget into split, {@code false} otherwise
* to allow launcher to handle the click
*/
- public boolean handleSecondWidgetSelectionForSplit(View view, PendingIntent pendingIntent) {
+ public boolean handleSecondWidgetSelectionForSplit(View view, PendingIntent pendingIntent,
+ Intent remoteResponseIntent) {
if (shouldIgnoreSecondSplitLaunch()) {
return false;
}
@@ -86,7 +87,7 @@
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
view.post(() -> {
- mController.setSecondTask(pendingIntent);
+ mController.setSecondWidget(pendingIntent, remoteResponseIntent);
// Convert original widgetView into bitmap to use for animation
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
index 9a2826d..c9e536a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
@@ -20,9 +20,10 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,17 +33,10 @@
@Test
@TaskbarModeSwitch(mode = PERSISTENT)
- public void testHideShowTaskbar() {
- getTaskbar().hide();
- mLauncher.getLaunchedAppState().showTaskbar();
- }
-
- @Test
- @TaskbarModeSwitch(mode = PERSISTENT)
- @Ignore // b/301575789
- public void testHideTaskbarPersistsOnRecreate() {
- getTaskbar().hide();
- mLauncher.recreateTaskbar();
- mLauncher.getLaunchedAppState().assertTaskbarHidden();
+ @PortraitLandscape
+ @NavigationModeSwitch
+ public void testTaskbarFillsWidth() {
+ // Width check is performed inside TAPL whenever getTaskbar() is called.
+ getTaskbar();
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index b3cc215..0bcdb19 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
-import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT;
import static org.junit.Assert.assertEquals;
@@ -295,7 +294,8 @@
}
@Test
- @TaskbarModeSwitch(mode = PERSISTENT)
+ @TaskbarModeSwitch
+ @Ignore // b/314873201
public void testQuickSwitchToPreviousAppForTablet() throws Exception {
assumeTrue(mLauncher.isTablet());
startTestActivity(2);
@@ -304,22 +304,33 @@
// Set ignoreTaskbarVisibility to true to verify the task bar visibility explicitly.
mLauncher.setIgnoreTaskbarVisibility(true);
- // Expect task bar invisible when the launched app was the IME activity.
- LaunchedAppState launchedAppState = getAndAssertLaunchedApp();
- if (isHardwareKeyboard()) {
- launchedAppState.assertTaskbarVisible();
- } else {
- launchedAppState.assertTaskbarHidden();
+
+ try {
+ boolean isTransientTaskbar = mLauncher.isTransientTaskbar();
+ // Expect task bar invisible when the launched app was the IME activity.
+ LaunchedAppState launchedAppState = getAndAssertLaunchedApp();
+ if (!isTransientTaskbar && isHardwareKeyboard()) {
+ launchedAppState.assertTaskbarVisible();
+ } else {
+ launchedAppState.assertTaskbarHidden();
+ }
+
+ // Quick-switch to the test app with swiping to right.
+ quickSwitchToPreviousAppAndAssert(true /* toRight */);
+
+ assertTestActivityIsRunning(2,
+ "The first app we should have quick switched to is not running");
+ launchedAppState = getAndAssertLaunchedApp();
+ if (isTransientTaskbar) {
+ launchedAppState.assertTaskbarHidden();
+ } else {
+ // Expect taskbar visible when the launched app was the test activity.
+ launchedAppState.assertTaskbarVisible();
+ }
+ } finally {
+ // Reset ignoreTaskbarVisibility to ensure other tests still verify it.
+ mLauncher.setIgnoreTaskbarVisibility(false);
}
-
- // Quick-switch to the test app with swiping to right.
- quickSwitchToPreviousAppAndAssert(true /* toRight */);
-
- assertTestActivityIsRunning(2,
- "The first app we should have quick switched to is not running");
- // Expect task bar visible when the launched app was the test activity.
- launchedAppState = getAndAssertLaunchedApp();
- launchedAppState.assertTaskbarVisible();
}
private boolean isHardwareKeyboard() {
@@ -358,7 +369,7 @@
@Test
@PortraitLandscape
- @TaskbarModeSwitch(mode = PERSISTENT)
+ @TaskbarModeSwitch()
@PlatinumTest(focusArea = "launcher")
@TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/309820115
@ScreenRecord // b/309820115
@@ -454,6 +465,7 @@
@Test
@PortraitLandscape
+ @TaskbarModeSwitch
public void testTaskbarDeadzonesForTablet() throws Exception {
assumeTrue(mLauncher.isTablet());
@@ -466,15 +478,29 @@
launcher -> assertTrue("Should have at least 3 tasks",
getTaskCount(launcher) >= 3));
- // On persistent taskbar, it should not dismiss when tapping the taskbar
- overview.touchTaskbarBottomCorner(/* tapRight= */ false);
- assertTrue("Launcher internal state should be Overview",
- isInState(() -> LauncherState.OVERVIEW));
+ if (mLauncher.isTransientTaskbar()) {
+ // On transient taskbar, it should dismiss when tapping outside taskbar bounds.
+ overview.touchTaskbarBottomCorner(/* tapRight= */ false);
+ assertTrue("Launcher internal state should be Normal",
+ isInState(() -> LauncherState.NORMAL));
- // On persistent taskbar, it should not dismiss when tapping the taskbar
- overview.touchTaskbarBottomCorner(/* tapRight= */ true);
- assertTrue("Launcher internal state should be Overview",
- isInState(() -> LauncherState.OVERVIEW));
+ overview = mLauncher.getWorkspace().switchToOverview();
+
+ // On transient taskbar, it should dismiss when tapping outside taskbar bounds.
+ overview.touchTaskbarBottomCorner(/* tapRight= */ true);
+ assertTrue("Launcher internal state should be Normal",
+ isInState(() -> LauncherState.NORMAL));
+ } else {
+ // On persistent taskbar, it should not dismiss when tapping the taskbar
+ overview.touchTaskbarBottomCorner(/* tapRight= */ false);
+ assertTrue("Launcher internal state should be Overview",
+ isInState(() -> LauncherState.OVERVIEW));
+
+ // On persistent taskbar, it should not dismiss when tapping the taskbar
+ overview.touchTaskbarBottomCorner(/* tapRight= */ true);
+ assertTrue("Launcher internal state should be Overview",
+ isInState(() -> LauncherState.OVERVIEW));
+ }
}
@Test
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
index db23cc0..7109bbf 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
@@ -24,6 +24,7 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
import org.junit.Test;
@@ -64,4 +65,12 @@
getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
mLauncher.getLaunchedAppState().clickStashedTaskbarToGoHome();
}
+
+ @Test
+ @TaskbarModeSwitch(mode = TRANSIENT)
+ @PortraitLandscape
+ public void testSwipeToStashAndUnstash() {
+ getTaskbar().swipeDownToStash();
+ mLauncher.getLaunchedAppState().swipeUpToUnstashTaskbar();
+ }
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 7d15f7b..5443ff9 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -70,6 +70,7 @@
import com.android.launcher3.celllayout.DelegatedCellDrawing;
import com.android.launcher3.celllayout.ItemConfiguration;
import com.android.launcher3.celllayout.ReorderAlgorithm;
+import com.android.launcher3.celllayout.ReorderParameters;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
@@ -1748,8 +1749,11 @@
protected ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
ItemConfiguration solution) {
- return createReorderAlgorithm().findReorderSolution(pixelX, pixelY, minSpanX, minSpanY,
- spanX, spanY, direction, dragView, decX, solution);
+ ItemConfiguration configuration = new ItemConfiguration();
+ copyCurrentStateToSolution(configuration);
+ ReorderParameters parameters = new ReorderParameters(pixelX, pixelY, spanX, spanY, minSpanX,
+ minSpanY, dragView, configuration);
+ return createReorderAlgorithm().findReorderSolution(parameters, decX);
}
public void copyCurrentStateToSolution(ItemConfiguration solution) {
@@ -1779,8 +1783,12 @@
*/
public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX, int minSpanY,
int spanX, int spanY, View dragView) {
- return createReorderAlgorithm().calculateReorder(pixelX, pixelY, minSpanX, minSpanY,
- spanX, spanY, dragView);
+ ItemConfiguration configuration = new ItemConfiguration();
+ copyCurrentStateToSolution(configuration);
+ return createReorderAlgorithm().calculateReorder(
+ new ReorderParameters(pixelX, pixelY, spanX, spanY, minSpanX, minSpanY, dragView,
+ configuration)
+ );
}
int[] performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0278e4f..126efbb 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -22,7 +22,6 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import static com.android.app.animation.Interpolators.EMPHASIZED;
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER;
import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
@@ -351,14 +350,9 @@
// UI and state for the overview panel
private View mOverviewPanel;
- @Thunk
- boolean mWorkspaceLoading = true;
-
// Used to notify when an activity launch has been deferred because launcher is not yet resumed
// TODO: See if we can remove this later
private Runnable mOnDeferredActivityLaunchCallback;
-
- private ViewOnDrawExecutor mPendingExecutor;
private OnPreDrawListener mOnInitialBindListener;
private LauncherModel mModel;
@@ -1075,7 +1069,7 @@
}
private void logStopAndResume(boolean isResume) {
- if (mPendingExecutor != null) return;
+ if (mModelCallbacks.getPendingExecutor() != null) return;
int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage();
int statsLogOrdinal = mStateManager.getState().statsLogOrdinal;
@@ -1715,7 +1709,7 @@
mAppWidgetHolder.destroy();
TextKeyListener.getInstance().release();
- clearPendingBinds();
+ mModelCallbacks.clearPendingBinds();
LauncherAppState.getIDP(this).removeOnChangeListener(this);
mOverlayManager.onActivityDestroyed();
@@ -2077,48 +2071,9 @@
return mModelCallbacks.getPagesToBindSynchronously(orderedScreenIds);
}
- /**
- * Clear any pending bind callbacks. This is called when is loader is planning to
- * perform a full rebind from scratch.
- */
- @Override
- public void clearPendingBinds() {
- if (mPendingExecutor != null) {
- mPendingExecutor.cancel();
- mPendingExecutor = null;
-
- // We might have set this flag previously and forgot to clear it.
- mAppsView.getAppsStore()
- .disableDeferUpdatesSilently(AllAppsStore.DEFER_UPDATES_NEXT_DRAW);
- }
- }
-
- /**
- * Refreshes the shortcuts shown on the workspace.
- * <p>
- * Implementation of the method from LauncherModel.Callbacks.
- */
@Override
public void startBinding() {
- TraceHelper.INSTANCE.beginSection("startBinding");
- // Floating panels (except the full widget sheet) are associated with individual icons. If
- // we are starting a fresh bind, close all such panels as all the icons are about
- // to go away.
- AbstractFloatingView.closeOpenViews(this, true, TYPE_ALL & ~TYPE_REBIND_SAFE);
-
- setWorkspaceLoading(true);
-
- // Clear the workspace because it's going to be rebound
- mDragController.cancelDrag();
-
- mWorkspace.clearDropTargets();
- mWorkspace.removeAllWorkspaceScreens();
- mAppWidgetHolder.clearViews();
-
- if (mHotseat != null) {
- mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout());
- }
- TraceHelper.INSTANCE.endSection();
+ mModelCallbacks.startBinding();
}
@Override
@@ -2500,8 +2455,8 @@
}
public void clearPendingExecutor(ViewOnDrawExecutor executor) {
- if (mPendingExecutor == executor) {
- mPendingExecutor = null;
+ if (mModelCallbacks.getPendingExecutor() == executor) {
+ mModelCallbacks.setPendingExecutor(null);
}
}
@@ -2512,9 +2467,9 @@
mModelCallbacks.setSynchronouslyBoundPages(boundPages);
mModelCallbacks.setPagesToBindSynchronously(new IntSet());
- clearPendingBinds();
+ mModelCallbacks.clearPendingBinds();
ViewOnDrawExecutor executor = new ViewOnDrawExecutor(pendingTasks);
- mPendingExecutor = executor;
+ mModelCallbacks.setPendingExecutor(executor);
if (!isInState(ALL_APPS)) {
mAppsView.getAppsStore().enableDeferUpdates(AllAppsStore.DEFER_UPDATES_NEXT_DRAW);
pendingTasks.add(() -> mAppsView.getAppsStore().disableDeferUpdates(
@@ -2568,7 +2523,7 @@
TraceHelper.INSTANCE.beginSection("finishBindingItems");
mWorkspace.restoreInstanceStateForRemainingPages();
- setWorkspaceLoading(false);
+ mModelCallbacks.setWorkspaceLoading(false);
if (mPendingActivityResult != null) {
handleActivityResult(mPendingActivityResult.requestCode,
@@ -2851,7 +2806,7 @@
writer.println(prefix + "Misc:");
dumpMisc(prefix + "\t", writer);
- writer.println(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading);
+ writer.println(prefix + "\tmWorkspaceLoading=" + mModelCallbacks.getWorkspaceLoading());
writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs
+ " mPendingActivityResult=" + mPendingActivityResult);
writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
@@ -3079,21 +3034,17 @@
// Getters and Setters
- private void setWorkspaceLoading(boolean value) {
- mWorkspaceLoading = value;
- }
-
public boolean isWorkspaceLocked() {
- return mWorkspaceLoading || mPendingRequestArgs != null;
+ return isWorkspaceLoading() || mPendingRequestArgs != null;
}
public boolean isWorkspaceLoading() {
- return mWorkspaceLoading;
+ return mModelCallbacks.getWorkspaceLoading();
}
@Override
public boolean isBindingItems() {
- return mWorkspaceLoading;
+ return isWorkspaceLoading();
}
/**
diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt
index bcd30d3..51d7690 100644
--- a/src/com/android/launcher3/ModelCallbacks.kt
+++ b/src/com/android/launcher3/ModelCallbacks.kt
@@ -2,6 +2,7 @@
import androidx.annotation.UiThread
import com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID
+import com.android.launcher3.allapps.AllAppsStore
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.config.FeatureFlags.shouldShowFirstPageWidget
import com.android.launcher3.model.BgDataModel
@@ -18,6 +19,8 @@
import com.android.launcher3.util.IntSet
import com.android.launcher3.util.PackageUserKey
import com.android.launcher3.util.Preconditions
+import com.android.launcher3.util.TraceHelper
+import com.android.launcher3.util.ViewOnDrawExecutor
import com.android.launcher3.widget.PendingAddWidgetInfo
import com.android.launcher3.widget.model.WidgetsListBaseEntry
import java.util.function.Predicate
@@ -27,11 +30,55 @@
var synchronouslyBoundPages = LIntSet()
var pagesToBindSynchronously = LIntSet()
- var isFirstPagePinnedItemEnabled =
+ private var isFirstPagePinnedItemEnabled =
(BuildConfig.QSB_ON_FIRST_SCREEN && !FeatureFlags.ENABLE_SMARTSPACE_REMOVAL.get())
var stringCache: StringCache? = null
+ var pendingExecutor: ViewOnDrawExecutor? = null
+
+ var workspaceLoading = true
+
+ /**
+ * Refreshes the shortcuts shown on the workspace.
+ *
+ * Implementation of the method from LauncherModel.Callbacks.
+ */
+ override fun startBinding() {
+ TraceHelper.INSTANCE.beginSection("startBinding")
+ // Floating panels (except the full widget sheet) are associated with individual icons. If
+ // we are starting a fresh bind, close all such panels as all the icons are about
+ // to go away.
+ AbstractFloatingView.closeOpenViews(
+ launcher,
+ true,
+ AbstractFloatingView.TYPE_ALL and AbstractFloatingView.TYPE_REBIND_SAFE.inv()
+ )
+ workspaceLoading = true
+
+ // Clear the workspace because it's going to be rebound
+ launcher.dragController.cancelDrag()
+ launcher.workspace.clearDropTargets()
+ launcher.workspace.removeAllWorkspaceScreens()
+ launcher.appWidgetHolder.clearViews()
+ launcher.hotseat?.resetLayout(launcher.deviceProfile.isVerticalBarLayout)
+ TraceHelper.INSTANCE.endSection()
+ }
+
+ /**
+ * Clear any pending bind callbacks. This is called when is loader is planning to perform a full
+ * rebind from scratch.
+ */
+ override fun clearPendingBinds() {
+ pendingExecutor?.cancel() ?: return
+ pendingExecutor = null
+
+ // We might have set this flag previously and forgot to clear it.
+ launcher.appsView.appsStore.disableDeferUpdatesSilently(
+ AllAppsStore.DEFER_UPDATES_NEXT_DRAW
+ )
+ }
+
override fun preAddApps() {
// If there's an undo snackbar, force it to complete to ensure empty screens are removed
// before trying to add new items.
@@ -119,7 +166,7 @@
val visibleIds =
when {
!pagesToBindSynchronously.isEmpty -> pagesToBindSynchronously
- !launcher.isWorkspaceLoading -> launcher.workspace.currentPageScreenIds
+ !workspaceLoading -> launcher.workspace.currentPageScreenIds
else -> synchronouslyBoundPages
}
// Launcher IntArray has the same name as Kotlin IntArray
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index b74699a..e0f6101 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -79,7 +79,6 @@
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.TintedDrawableSpan;
-import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShortcutCachingLogic;
@@ -91,6 +90,7 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.testing.shared.ResourceUtils;
+import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.Themes;
@@ -676,12 +676,11 @@
}
if (badge == null) {
- try (LauncherIcons li = LauncherIcons.obtain(context)) {
- badge = BitmapInfo.LOW_RES_INFO.withFlags(
- li.getBitmapFlagOp(new BaseIconFactory.IconOptions().setUser(
- UserCache.INSTANCE.get(context).getUserInfo(info.user))))
- .getBadgeDrawable(context, useTheme);
- }
+ badge = BitmapInfo.LOW_RES_INFO.withFlags(
+ UserCache.INSTANCE.get(context)
+ .getUserInfo(info.user)
+ .applyBitmapInfoFlags(FlagOp.NO_OP))
+ .getBadgeDrawable(context, useTheme);
if (badge == null) {
badge = new ColorDrawable(Color.TRANSPARENT);
}
diff --git a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
index 7deb653..8d0cf13 100644
--- a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
+++ b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
@@ -48,28 +48,24 @@
}
@Override
- public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY) {
+ public ItemConfiguration closestEmptySpaceReorder(ReorderParameters reorderParameters) {
return removeSeamFromSolution(simulateSeam(
- () -> super.closestEmptySpaceReorder(pixelX, pixelY, minSpanX, minSpanY, spanX,
- spanY)));
+ () -> super.closestEmptySpaceReorder(reorderParameters))
+ );
}
@Override
- public ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
- ItemConfiguration solution) {
+ public ItemConfiguration findReorderSolution(ReorderParameters reorderParameters,
+ boolean decX) {
return removeSeamFromSolution(simulateSeam(
- () -> super.findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
- direction, dragView, decX, solution)));
+ () -> super.findReorderSolution(reorderParameters, decX)));
}
@Override
- public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
- int spanY,
- View dragView) {
- return removeSeamFromSolution(simulateSeam(
- () -> super.dropInPlaceSolution(pixelX, pixelY, spanX, spanY, dragView)));
+ public ItemConfiguration dropInPlaceSolution(ReorderParameters reorderParameters) {
+ return removeSeamFromSolution(
+ simulateSeam(() -> super.dropInPlaceSolution(reorderParameters))
+ );
}
void addSeam() {
diff --git a/src/com/android/launcher3/celllayout/ReorderAlgorithm.java b/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
index 7385c0a..42b6991 100644
--- a/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
+++ b/src/com/android/launcher3/celllayout/ReorderAlgorithm.java
@@ -45,36 +45,28 @@
* This method differs from closestEmptySpaceReorder and dropInPlaceSolution because this method
* will move items around and will change the shape of the item if possible to try to find a
* solution.
- *
+ * <p>
* When changing the size of the widget this method will try first subtracting -1 in the x
* dimension and then subtracting -1 in the y dimension until finding a possible solution or
* until it no longer can reduce the span.
*
- * @param pixelX X coordinate in pixels in the screen
- * @param pixelY Y coordinate in pixels in the screen
- * @param minSpanX minimum possible horizontal span it will try to find a solution for.
- * @param minSpanY minimum possible vertical span it will try to find a solution for.
- * @param spanX horizontal cell span
- * @param spanY vertical cell span
- * @param direction direction in which it will try to push the items intersecting the desired
- * view
- * @param dragView view being dragged in reorder
- * @param decX whether it will decrease the horizontal or vertical span if it can't find a
- * solution for the current span.
- * @param solution variable to store the solution
+ * @param decX whether it will decrease the horizontal or vertical span if it can't find a
+ * solution for the current span.
* @return the same solution variable
*/
- public ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
- ItemConfiguration solution) {
- return findReorderSolutionRecursive(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
- direction, dragView, decX, solution);
+ public ItemConfiguration findReorderSolution(ReorderParameters reorderParameters,
+ boolean decX) {
+ return findReorderSolutionRecursive(reorderParameters.getPixelX(),
+ reorderParameters.getPixelY(), reorderParameters.getMinSpanX(),
+ reorderParameters.getMinSpanY(), reorderParameters.getSpanX(),
+ reorderParameters.getSpanY(), mCellLayout.mDirectionVector,
+ reorderParameters.getDragView(), decX, reorderParameters.getSolution());
}
- private ItemConfiguration findReorderSolutionRecursive(int pixelX, int pixelY,
- int minSpanX, int minSpanY, int spanX, int spanY, int[] direction, View dragView,
- boolean decX, ItemConfiguration solution) {
+ private ItemConfiguration findReorderSolutionRecursive(int pixelX, int pixelY, int minSpanX,
+ int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
+ ItemConfiguration solution) {
// Copy the current state into the solution. This solution will be manipulated as necessary.
mCellLayout.copyCurrentStateToSolution(solution);
// Copy the current occupied array into the temporary occupied array. This array will be
@@ -89,8 +81,8 @@
boolean success;
// First we try the exact nearest position of the item being dragged,
// we will then want to try to move this around to other neighbouring positions
- success = rearrangementExists(result[0], result[1], spanX, spanY, direction,
- dragView, solution);
+ success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
+ solution);
if (!success) {
// We try shrinking the widget down to size in an alternating pattern, shrink 1 in
@@ -135,10 +127,11 @@
// and not by the views hash which is "random".
// The views are sorted twice, once for the X position and a second time for the Y position
// to ensure same order everytime.
- Comparator comparator = Comparator.comparing(view ->
- ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellX())
- .thenComparing(view ->
- ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY());
+ Comparator comparator = Comparator.comparing(
+ view -> ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellX()
+ ).thenComparing(
+ view -> ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY()
+ );
List<View> views = solution.map.keySet().stream().sorted(comparator).toList();
for (View child : views) {
if (child == ignoreView) continue;
@@ -158,15 +151,13 @@
// First we try to find a solution which respects the push mechanic. That is,
// we try to find a solution such that no displaced item travels through another item
// without also displacing that item.
- if (attemptPushInDirection(intersectingViews, occupiedRect, direction,
- ignoreView,
+ if (attemptPushInDirection(intersectingViews, occupiedRect, direction, ignoreView,
solution)) {
return true;
}
// Next we try moving the views as a block, but without requiring the push mechanic.
- if (addViewsToTempLocation(intersectingViews, occupiedRect, direction,
- ignoreView,
+ if (addViewsToTempLocation(intersectingViews, occupiedRect, direction, ignoreView,
solution)) {
return true;
}
@@ -180,8 +171,8 @@
return true;
}
- private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
- int[] direction, ItemConfiguration currentState) {
+ private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop, int[] direction,
+ ItemConfiguration currentState) {
CellAndSpan c = currentState.map.get(v);
boolean success = false;
mCellLayout.mTmpOccupied.markCells(c, false);
@@ -305,16 +296,16 @@
int temp = direction[1];
direction[1] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
direction[1] = temp;
temp = direction[0];
direction[0] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// Revert the direction
@@ -325,16 +316,16 @@
direction[1] *= -1;
temp = direction[1];
direction[1] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
direction[1] = temp;
temp = direction[0];
direction[0] = 0;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// revert the direction
@@ -345,15 +336,15 @@
} else {
// If the direction vector has a single non-zero component, we push first in the
// direction of the vector
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// Then we try the opposite direction
direction[0] *= -1;
direction[1] *= -1;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// Switch the direction back
@@ -367,16 +358,16 @@
int temp = direction[1];
direction[1] = direction[0];
direction[0] = temp;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// Then we try the opposite direction
direction[0] *= -1;
direction[1] *= -1;
- if (pushViewsToTempLocation(intersectingViews, occupied, direction,
- ignoreView, solution)) {
+ if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
+ solution)) {
return true;
}
// Switch the direction back
@@ -446,63 +437,59 @@
/**
* Returns a "reorder" if there is empty space without rearranging anything.
*
- * @param pixelX X coordinate in pixels in the screen
- * @param pixelY Y coordinate in pixels in the screen
- * @param spanX horizontal cell span
- * @param spanY vertical cell span
- * @param dragView view being dragged in reorder
* @return the configuration that represents the found reorder
*/
- public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
- int spanY, View dragView) {
- int[] result = mCellLayout.findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY,
- new int[2]);
+ public ItemConfiguration dropInPlaceSolution(ReorderParameters reorderParameters) {
+ int[] result = mCellLayout.findNearestAreaIgnoreOccupied(reorderParameters.getPixelX(),
+ reorderParameters.getPixelY(), reorderParameters.getSpanX(),
+ reorderParameters.getSpanY(), new int[2]);
ItemConfiguration solution = new ItemConfiguration();
mCellLayout.copyCurrentStateToSolution(solution);
solution.isSolution = !isConfigurationRegionOccupied(
- new Rect(result[0], result[1], result[0] + spanX, result[1] + spanY),
- solution,
- dragView
- );
+ new Rect(result[0], result[1], result[0] + reorderParameters.getSpanX(),
+ result[1] + reorderParameters.getSpanY()), solution,
+ reorderParameters.getDragView());
if (!solution.isSolution) {
return solution;
}
solution.cellX = result[0];
solution.cellY = result[1];
- solution.spanX = spanX;
- solution.spanY = spanY;
+ solution.spanX = reorderParameters.getSpanX();
+ solution.spanY = reorderParameters.getSpanY();
return solution;
}
- private boolean isConfigurationRegionOccupied(Rect region,
- ItemConfiguration configuration, View ignoreView) {
- return configuration.map.entrySet()
+ private boolean isConfigurationRegionOccupied(Rect region, ItemConfiguration configuration,
+ View ignoreView) {
+ return configuration.map
+ .entrySet()
.stream()
.filter(entry -> entry.getKey() != ignoreView)
.map(Entry::getValue)
- .anyMatch(cellAndSpan -> region.intersect(cellAndSpan.cellX, cellAndSpan.cellY,
+ .anyMatch(cellAndSpan -> region.intersect(
+ cellAndSpan.cellX,
+ cellAndSpan.cellY,
cellAndSpan.cellX + cellAndSpan.spanX,
- cellAndSpan.cellY + cellAndSpan.spanY));
+ cellAndSpan.cellY + cellAndSpan.spanY
+ )
+ );
}
/**
* Returns a "reorder" where we simply drop the item in the closest empty space, without moving
* any other item in the way.
*
- * @param pixelX X coordinate in pixels in the screen
- * @param pixelY Y coordinate in pixels in the screen
- * @param spanX horizontal cell span
- * @param spanY vertical cell span
* @return the configuration that represents the found reorder
*/
- public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY,
- int minSpanX, int minSpanY, int spanX, int spanY) {
+ public ItemConfiguration closestEmptySpaceReorder(ReorderParameters reorderParameters) {
ItemConfiguration solution = new ItemConfiguration();
int[] result = new int[2];
int[] resultSpan = new int[2];
- mCellLayout.findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, result,
- resultSpan);
+ mCellLayout.findNearestVacantArea(reorderParameters.getPixelX(),
+ reorderParameters.getPixelY(), reorderParameters.getMinSpanX(),
+ reorderParameters.getMinSpanY(), reorderParameters.getSpanX(),
+ reorderParameters.getSpanY(), result, resultSpan);
if (result[0] >= 0 && result[1] >= 0) {
mCellLayout.copyCurrentStateToSolution(solution);
solution.cellX = result[0];
@@ -521,32 +508,19 @@
* the workspace to make space for the new item, this function return a solution for that
* reorder.
*
- * @param pixelX X coordinate in the screen of the dragView in pixels
- * @param pixelY Y coordinate in the screen of the dragView in pixels
- * @param minSpanX minimum horizontal span the item can be shrunk to
- * @param minSpanY minimum vertical span the item can be shrunk to
- * @param spanX occupied horizontal span
- * @param spanY occupied vertical span
- * @param dragView the view of the item being draged
* @return returns a solution for the given parameters, the solution contains all the icons and
* the locations they should be in the given solution.
*/
- public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX,
- int minSpanY, int spanX, int spanY, View dragView) {
- getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView,
- mCellLayout.mDirectionVector);
+ public ItemConfiguration calculateReorder(ReorderParameters reorderParameters) {
+ getDirectionVectorForDrop(reorderParameters, mCellLayout.mDirectionVector);
- ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(pixelX, pixelY, spanX, spanY,
- dragView);
+ ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(reorderParameters);
// Find a solution involving pushing / displacing any items in the way
- ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX,
- minSpanY, spanX, spanY, mCellLayout.mDirectionVector, dragView, true,
- new ItemConfiguration());
+ ItemConfiguration swapSolution = findReorderSolution(reorderParameters, true);
// We attempt the approach which doesn't shuffle views at all
- ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(pixelX, pixelY, minSpanX,
- minSpanY, spanX, spanY);
+ ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(reorderParameters);
// If the reorder solution requires resizing (shrinking) the item being dropped, we instead
// favor a solution in which the item is not resized, but
@@ -586,21 +560,26 @@
* those cells. Instead we use some heuristics to often lock the vector to up, down, left
* or right, which helps make pushing feel right.
*/
- private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
- int spanY, View dragView, int[] resultDirection) {
+ public void getDirectionVectorForDrop(ReorderParameters reorderParameters,
+ int[] resultDirection) {
//TODO(adamcohen) b/151776141 use the items visual center for the direction vector
int[] targetDestination = new int[2];
- mCellLayout.findNearestAreaIgnoreOccupied(dragViewCenterX, dragViewCenterY, spanX, spanY,
- targetDestination);
+ mCellLayout.findNearestAreaIgnoreOccupied(reorderParameters.getPixelX(),
+ reorderParameters.getPixelY(), reorderParameters.getSpanX(),
+ reorderParameters.getSpanY(), targetDestination);
Rect dragRect = new Rect();
- mCellLayout.cellToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
- dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
+ mCellLayout.cellToRect(targetDestination[0], targetDestination[1],
+ reorderParameters.getSpanX(), reorderParameters.getSpanY(), dragRect);
+ dragRect.offset(reorderParameters.getPixelX() - dragRect.centerX(),
+ reorderParameters.getPixelY() - dragRect.centerY());
Rect region = new Rect(targetDestination[0], targetDestination[1],
- targetDestination[0] + spanX, targetDestination[1] + spanY);
- Rect dropRegionRect = mCellLayout.getIntersectingRectanglesInRegion(region, dragView);
+ targetDestination[0] + reorderParameters.getSpanX(),
+ targetDestination[1] + reorderParameters.getSpanY());
+ Rect dropRegionRect = mCellLayout.getIntersectingRectanglesInRegion(region,
+ reorderParameters.getDragView());
if (dropRegionRect == null) dropRegionRect = new Rect(region);
int dropRegionSpanX = dropRegionRect.width();
@@ -609,13 +588,17 @@
mCellLayout.cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
dropRegionRect.height(), dropRegionRect);
- int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
- int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY;
+ int deltaX = (dropRegionRect.centerX() - reorderParameters.getPixelX())
+ / reorderParameters.getSpanX();
+ int deltaY = (dropRegionRect.centerY() - reorderParameters.getPixelY())
+ / reorderParameters.getSpanY();
- if (dropRegionSpanX == mCellLayout.getCountX() || spanX == mCellLayout.getCountX()) {
+ if (dropRegionSpanX == mCellLayout.getCountX()
+ || reorderParameters.getSpanX() == mCellLayout.getCountX()) {
deltaX = 0;
}
- if (dropRegionSpanY == mCellLayout.getCountY() || spanY == mCellLayout.getCountY()) {
+ if (dropRegionSpanY == mCellLayout.getCountY()
+ || reorderParameters.getSpanY() == mCellLayout.getCountY()) {
deltaY = 0;
}
diff --git a/src/com/android/launcher3/celllayout/ReorderParameters.kt b/src/com/android/launcher3/celllayout/ReorderParameters.kt
new file mode 100644
index 0000000..3fdf35c
--- /dev/null
+++ b/src/com/android/launcher3/celllayout/ReorderParameters.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.celllayout
+
+import android.view.View
+
+class ReorderParameters(
+ val pixelX: Int,
+ val pixelY: Int,
+ val spanX: Int,
+ val spanY: Int,
+ val minSpanX: Int,
+ val minSpanY: Int,
+ val dragView: View?,
+ val solution: ItemConfiguration
+) {}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index bed6efb..746f1cf 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -270,10 +270,6 @@
public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag(270391693,
"IME_STICKY_SNACKBAR_EDU", ENABLED, "Show sticky IME edu in AllApps");
- public static final BooleanFlag ENABLE_PEOPLE_TILE_PREVIEW = getDebugFlag(270391653,
- "ENABLE_PEOPLE_TILE_PREVIEW", DISABLED,
- "Experimental: Shows conversation shortcuts on home screen as search results");
-
public static final BooleanFlag FOLDER_NAME_MAJORITY_RANKING = getDebugFlag(270391638,
"FOLDER_NAME_MAJORITY_RANKING", ENABLED,
"Suggests folder names based on majority based ranking.");
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 40bb6ad..2c834bd 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -15,8 +15,8 @@
*/
package com.android.launcher3.testing;
-import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
+import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -47,6 +47,7 @@
import com.android.launcher3.testing.shared.HotseatCellCenterRequest;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
@@ -179,6 +180,11 @@
mDeviceProfile.numShownAllAppsColumns);
return response;
+ case TestProtocol.REQUEST_IS_TRANSIENT_TASKBAR:
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ DisplayController.isTransientTaskbar(mContext));
+ return response;
+
case TestProtocol.REQUEST_IS_TWO_PANELS:
response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
FOLDABLE_SINGLE_PAGE.get() ? false : mDeviceProfile.isTwoPanels);
@@ -297,10 +303,8 @@
}
protected boolean isLauncherInitialized() {
- Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
- return launcher == null
- || (LauncherAppState.getInstance(mContext).getModel().isModelLoaded()
- && !launcher.isBindingItems());
+ return Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null
+ || LauncherAppState.getInstance(mContext).getModel().isModelLoaded();
}
protected Activity getCurrentActivity() {
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 4c83668..045ca79 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -71,7 +71,7 @@
private static final String TAG = "DisplayController";
private static final boolean DEBUG = false;
- private static boolean sTransientTaskbarStatusForTests;
+ private static boolean sTransientTaskbarStatusForTests = true;
// TODO(b/254119092) remove all logs with this tag
public static final String TASKBAR_NOT_DESTROYED_TAG = "b/254119092";
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
index e32fec2..f283fb6 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -240,7 +240,7 @@
/** Indicates that search has been invoked. */
public void vibrateForSearch() {
- if (mSearchEffect != null && FeatureFlags.ENABLE_SEARCH_HAPTIC_COMMIT.get()) {
+ if (mSearchEffect != null) {
vibrate(mSearchEffect);
}
}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index e4df413..a1cd697 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -41,6 +41,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.shared.ResourceUtils;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.TouchController;
@@ -559,7 +560,8 @@
DeviceProfile dp = mActivity.getDeviceProfile();
if (dp.isTaskbarPresent) {
// Ignore taskbar gesture insets to avoid interfering with TouchControllers.
- gestureInsetBottom = Math.max(0, gestureInsetBottom - dp.taskbarHeight);
+ gestureInsetBottom = ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, getResources());
}
mSystemGestureRegion.set(
Math.max(gestureInsets.left, imeInset.left),
diff --git a/tests/assets/ReorderWidgets/full_reorder_case b/tests/assets/ReorderWidgets/full_reorder_case
index 33ebaae..850e4fd 100644
--- a/tests/assets/ReorderWidgets/full_reorder_case
+++ b/tests/assets/ReorderWidgets/full_reorder_case
@@ -20,7 +20,7 @@
bbmm
iimm
iiaa
-arguments: 0 3
+arguments: 0 2
board: 4x4
xxxx
bbii
diff --git a/tests/assets/ReorderWidgets/simple_reorder_case b/tests/assets/ReorderWidgets/simple_reorder_case
index f5eb7b6..2c50ce4 100644
--- a/tests/assets/ReorderWidgets/simple_reorder_case
+++ b/tests/assets/ReorderWidgets/simple_reorder_case
@@ -34,7 +34,7 @@
--mm
--mm
----
-arguments: 3 3
+arguments: 2 2
board: 4x4
xxxx
----
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index d5a645e..e19905f 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -30,6 +30,8 @@
public static final String FOLDER_OPENED_MESSAGE = "TAPL_FOLDER_OPENED";
public static final String SEARCH_RESULT_COMPLETE = "SEARCH_RESULT_COMPLETE";
public static final String LAUNCHER_ACTIVITY_STOPPED_MESSAGE = "TAPL_LAUNCHER_ACTIVITY_STOPPED";
+ public static final String WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE =
+ "TAPL_WALLPAPER_OPEN_ANIMATION_FINISHED";
public static final int NORMAL_STATE_ORDINAL = 0;
public static final int SPRING_LOADED_STATE_ORDINAL = 1;
public static final int OVERVIEW_STATE_ORDINAL = 2;
@@ -96,8 +98,9 @@
public static final String REQUEST_DISABLE_BLOCK_TIMEOUT = "disable-block-timeout";
public static final String REQUEST_ENABLE_TRANSIENT_TASKBAR = "enable-transient-taskbar";
public static final String REQUEST_DISABLE_TRANSIENT_TASKBAR = "disable-transient-taskbar";
+ public static final String REQUEST_IS_TRANSIENT_TASKBAR = "is-transient-taskbar";
public static final String REQUEST_UNSTASH_TASKBAR_IF_STASHED = "unstash-taskbar-if-stashed";
- public static final String REQUEST_STASHED_TASKBAR_HEIGHT = "stashed-taskbar-height";
+ public static final String REQUEST_TASKBAR_FROM_NAV_THRESHOLD = "taskbar-from-nav-threshold";
public static final String REQUEST_STASHED_TASKBAR_SCALE = "taskbar-stash-handle-scale";
public static final String REQUEST_RECREATE_TASKBAR = "recreate-taskbar";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
index 28471f6..e1af774 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
@@ -191,9 +191,21 @@
int[] testCaseXYinPixels = new int[2];
cl.regionToCenterPoint(x, y, spanX, spanY, testCaseXYinPixels);
- ItemConfiguration solution = cl.createReorderAlgorithm().calculateReorder(
- testCaseXYinPixels[0], testCaseXYinPixels[1], minSpanX, minSpanY, spanX, spanY,
- null);
+ ItemConfiguration configuration = new ItemConfiguration();
+ cl.copyCurrentStateToSolution(configuration);
+ ItemConfiguration solution = cl.createReorderAlgorithm()
+ .calculateReorder(
+ new ReorderParameters(
+ testCaseXYinPixels[0],
+ testCaseXYinPixels[1],
+ spanX,
+ spanY,
+ minSpanX,
+ minSpanY,
+ null,
+ configuration
+ )
+ );
if (solution == null) {
solution = new ItemConfiguration();
solution.isSolution = false;
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index b2ce400..cce4c5b 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -113,9 +113,11 @@
}
private void setResult(boolean success) {
- getInstrumentation().getTargetContext().sendBroadcast(
- WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
- success ? "clickOK" : "clickCancel"));
+ mLauncher.executeAndWaitForWallpaperAnimation(() ->
+ getInstrumentation().getTargetContext().sendBroadcast(
+ WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
+ success ? "clickOK" : "clickCancel")),
+ "setting widget coinfig result");
}
/**
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 7d25121..0e78565 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -54,6 +54,7 @@
private static final int MAX_SCROLL_ATTEMPTS = 40;
private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
+ private static final String FAST_SCROLLER_RES_ID = "fast_scroller";
private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
@@ -369,9 +370,12 @@
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to tap outside AllApps bottom sheet on the "
+ (tapRight ? "right" : "left"))) {
- final UiObject2 allAppsBottomSheet =
+
+ final UiObject2 container = (tapRight)
+ ? mLauncher.waitForLauncherObject(FAST_SCROLLER_RES_ID) :
mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID);
- mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
+
+ mLauncher.touchOutsideContainer(container, tapRight, false);
try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
"tapped outside AllApps bottom sheet")) {
verifyVisibleContainerOnDismiss();
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 44869be..b6b4a47 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -186,7 +186,14 @@
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
Taskbar taskbar = new Taskbar(mLauncher);
taskbar.touchBottomCorner(tapRight);
- verifyActiveContainer();
+ if (mLauncher.isTransientTaskbar()) {
+ // Tapping outside Transient Taskbar returns to Workspace, wait for that state.
+ new Workspace(mLauncher);
+ } else {
+ // Should stay in Overview.
+ verifyActiveContainer();
+ verifyActionsViewVisibility();
+ }
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index efeb5f6..6d58a35 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -20,12 +20,11 @@
import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT;
-import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT;
-import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_SHELL_DRAG_READY;
-import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT;
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_SCALE;
+import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD;
+import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
import android.graphics.Point;
import android.graphics.Rect;
@@ -84,8 +83,6 @@
public Taskbar getTaskbar() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get the taskbar")) {
- mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
-
return new Taskbar(mLauncher);
}
}
@@ -113,37 +110,32 @@
/**
* Returns the Taskbar in a visible state.
*
- * The taskbar must already be hidden when calling this method.
+ * The taskbar must already be hidden and in transient mode when calling this method.
*/
- public Taskbar showTaskbar() {
- mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+ public Taskbar swipeUpToUnstashTaskbar() {
+ mLauncher.assertTrue("Taskbar is not transient, swipe up not supported",
+ mLauncher.isTransientTaskbar());
+
mLauncher.getTestInfo(REQUEST_ENABLE_BLOCK_TIMEOUT);
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
- "want to show the taskbar")) {
+ "want to swipe up to unstash the taskbar")) {
mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
- final long downTime = SystemClock.uptimeMillis();
- final int unstashTargetY = mLauncher.getRealDisplaySize().y
- - (mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_HEIGHT)
- .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) / 2);
- final Point unstashTarget = new Point(
- mLauncher.getRealDisplaySize().x / 2, unstashTargetY);
+ int taskbarFromNavThreshold = mLauncher.getTestInfo(REQUEST_TASKBAR_FROM_NAV_THRESHOLD)
+ .getInt(TEST_INFO_RESPONSE_FIELD);
+ int startX = mLauncher.getRealDisplaySize().x / 2;
+ int startY = mLauncher.getRealDisplaySize().y - 1;
+ int endX = startX;
+ int endY = startY - taskbarFromNavThreshold;
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, unstashTarget,
+ mLauncher.linearGesture(startX, startY, endX, endY, 10, /* slowDown= */ true,
LauncherInstrumentation.GestureScope.EXPECT_PILFER);
- LauncherInstrumentation.log("showTaskbar: sent down");
+ LauncherInstrumentation.log("swipeUpToUnstashTaskbar: sent linear swipe up gesture");
- try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
- mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget,
- LauncherInstrumentation.GestureScope.EXPECT_PILFER);
-
- return new Taskbar(mLauncher);
- }
+ return new Taskbar(mLauncher);
} finally {
- mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
mLauncher.getTestInfo(REQUEST_DISABLE_BLOCK_TIMEOUT);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index fb81700..0bc00a3 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -824,12 +824,7 @@
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
-
- if (is3PLauncher() && isTablet()) {
- waitForSystemLauncherObject(TASKBAR_RES_ID);
- } else {
- waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
- }
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
return waitForLauncherObject(WORKSPACE_RES_ID);
}
@@ -839,12 +834,7 @@
waitUntilSystemLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
-
- if (is3PLauncher() && isTablet()) {
- waitForSystemLauncherObject(TASKBAR_RES_ID);
- } else {
- waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
- }
+ waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
return waitForLauncherObject(WIDGETS_RES_ID);
}
@@ -865,7 +855,7 @@
waitUntilSystemLauncherObjectGone(SPLIT_PLACEHOLDER_RES_ID);
waitUntilLauncherObjectGone(KEYBOARD_QUICK_SWITCH_RES_ID);
- if (is3PLauncher() && isTablet()) {
+ if (is3PLauncher() && isTablet() && !isTransientTaskbar()) {
waitForSystemLauncherObject(TASKBAR_RES_ID);
} else {
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
@@ -878,7 +868,7 @@
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
- if (isTablet()) {
+ if (isTablet() && !is3PLauncher()) {
waitForSystemLauncherObject(TASKBAR_RES_ID);
} else {
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
@@ -915,7 +905,11 @@
}
if (isTablet()) {
- waitForSystemLauncherObject(TASKBAR_RES_ID);
+ // Only check that Persistent Taskbar is visible, since Transient Taskbar
+ // may or may not be visible by design.
+ if (!isTransientTaskbar()) {
+ waitForSystemLauncherObject(TASKBAR_RES_ID);
+ }
} else {
waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
}
@@ -995,7 +989,7 @@
/**
* Using swiping up gesture to dismiss closable floating views, such as Menu or Folder Content.
*/
- private void swipeUpToCloseFloatingView(boolean gestureStartFromLauncher) {
+ private void swipeUpToCloseFloatingView() {
final Point displaySize = getRealDisplaySize();
final Optional<String> floatingRes = getFloatingResId();
@@ -1004,16 +998,11 @@
return;
}
- GestureScope gestureScope = gestureStartFromLauncher
- // Without the navigation bar layer, the gesture scope on tablets remains inside the
- // launcher process.
- ? (isTablet() ? GestureScope.DONT_EXPECT_PILFER : GestureScope.EXPECT_PILFER)
- : GestureScope.EXPECT_PILFER;
linearGesture(
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
- false, gestureScope);
+ false, GestureScope.EXPECT_PILFER);
try (LauncherInstrumentation.Closable c1 = addContextLayer(
String.format("Swiped up from floating view %s to home", floatingRes.get()))) {
@@ -1081,11 +1070,8 @@
final Point displaySize = getRealDisplaySize();
- boolean gestureStartFromLauncher =
- isTablet() ? !isLauncher3() : isLauncherVisible();
-
// CLose floating views before going back to home.
- swipeUpToCloseFloatingView(gestureStartFromLauncher);
+ swipeUpToCloseFloatingView();
if (hasLauncherObject(WORKSPACE_RES_ID)) {
log(action = "already at home");
@@ -2039,6 +2025,7 @@
}
}
+ /** Returns the bounds of the display as a Point where x is width and y is height. */
Point getRealDisplaySize() {
final Rect displayBounds = getContext().getSystemService(WindowManager.class)
.getMaximumWindowMetrics()
@@ -2101,6 +2088,11 @@
: TestProtocol.REQUEST_DISABLE_BLOCK_TIMEOUT);
}
+ public boolean isTransientTaskbar() {
+ return getTestInfo(TestProtocol.REQUEST_IS_TRANSIENT_TASKBAR)
+ .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
/** Enables transient taskbar for testing purposes only. */
public void enableTransientTaskbar(boolean enable) {
getTestInfo(enable
@@ -2313,4 +2305,14 @@
}
return result;
}
+
+ /** Executes a runnable and waits for the wallpaper-open animation completion. */
+ public void executeAndWaitForWallpaperAnimation(Runnable r, String actionName) {
+ executeAndWaitForLauncherEvent(
+ () -> r.run(),
+ event -> TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE
+ .equals(event.getClassName().toString()),
+ () -> "Didn't detect finishing wallpaper-open animation",
+ actionName);
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
index 38cc321..4a0131b 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -20,6 +20,8 @@
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.shared.TestProtocol;
+
/** Represents the menu of an overview task. */
public class OverviewTaskMenu {
@@ -59,8 +61,13 @@
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"before tapping the app info menu item")) {
- mLauncher.clickLauncherObject(
- mLauncher.findObjectInContainer(mMenu, By.text("App info")));
+ mLauncher.executeAndWaitForLauncherEvent(
+ () -> mLauncher.clickLauncherObject(
+ mLauncher.findObjectInContainer(mMenu, By.text("App info"))),
+ event -> TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE
+ .equals(event.getClassName().toString()),
+ () -> "Launcher activity didn't stop",
+ "tapped app info menu item");
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"tapped app info menu item")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
index da26694..a202c53 100644
--- a/tests/tapl/com/android/launcher3/tapl/Taskbar.java
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -18,8 +18,6 @@
import static android.view.KeyEvent.KEYCODE_META_RIGHT;
import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
-import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
-import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
import android.graphics.Point;
import android.graphics.Rect;
@@ -33,6 +31,8 @@
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
+import org.junit.Assert;
+
import java.util.List;
import java.util.stream.Collectors;
@@ -45,6 +45,15 @@
Taskbar(LauncherInstrumentation launcher) {
mLauncher = launcher;
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "expect new taskbar to be visible")) {
+ mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
+ }
+
+ if (!mLauncher.isTransientTaskbar()) {
+ Assert.assertEquals("Persistent taskbar should fill screen width",
+ getVisibleBounds().width(), mLauncher.getRealDisplaySize().x);
+ }
}
/**
@@ -61,33 +70,32 @@
}
/**
- * Hides this taskbar.
- *
- * The taskbar must already be visible when calling this method.
+ * Stashes this taskbar.
+ * <p>
+ * The taskbar must already be unstashed and in transient mode when calling this method.
*/
- public void hide() {
- mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+ public void swipeDownToStash() {
+ mLauncher.assertTrue("Taskbar is not transient, swipe down not supported",
+ mLauncher.isTransientTaskbar());
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to hide the taskbar");
LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID);
- final long downTime = SystemClock.uptimeMillis();
- Point stashTarget = new Point(
- mLauncher.getRealDisplaySize().x - 1, mLauncher.getRealDisplaySize().y - 1);
+ Rect taskbarBounds = getVisibleBounds();
+ int startX = taskbarBounds.centerX();
+ int startY = taskbarBounds.centerY();
+ int endX = startX;
+ int endY = mLauncher.getRealDisplaySize().y - 1;
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, stashTarget,
+ mLauncher.linearGesture(startX, startY, endX, endY, 10, false,
LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
- LauncherInstrumentation.log("hideTaskbar: sent down");
-
- try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
+ LauncherInstrumentation.log("swipeDownToStash: sent linear swipe down gesture");
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "expect transient taskbar to be hidden after swipe down")) {
mLauncher.waitUntilSystemLauncherObjectGone(TASKBAR_RES_ID);
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget,
- LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
}
- } finally {
- mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
}
}