From 5df843476c3491de62d387dee998dbab4d426c29 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 18 Dec 2024 16:09:02 -0800 Subject: [PATCH] Moving sysproxy to kotlin Bug: 385171800 Test: Presubmit Flag: EXEMPT refactor Change-Id: I9db2d4a2f7ff6e739e389ce0e076d5ea88adad5c --- .../android/quickstep/RecentTasksList.java | 2 +- .../com/android/quickstep/SystemUiProxy.java | 1660 ----------------- .../com/android/quickstep/SystemUiProxy.kt | 1270 +++++++++++++ .../TaskbarAutohideSuspendControllerTest.kt | 14 +- .../launcher3/taskbar/TaskbarOverflowTest.kt | 13 +- .../taskbar/TaskbarScrimViewControllerTest.kt | 13 +- 6 files changed, 1296 insertions(+), 1676 deletions(-) delete mode 100644 quickstep/src/com/android/quickstep/SystemUiProxy.java create mode 100644 quickstep/src/com/android/quickstep/SystemUiProxy.kt diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 85e2b6ebf0..08d43c044c 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -257,7 +257,7 @@ public class RecentTasksList { mRecentTasksChangedListener = null; } - private void initRunningTasks(ArrayList runningTasks) { + private void initRunningTasks(List runningTasks) { // Tasks are retrieved in order of most recently launched/used to least recently launched. mRunningTasks = new ArrayList<>(runningTasks); Collections.reverse(mRunningTasks); diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java deleted file mode 100644 index 6a25ecbc6d..0000000000 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ /dev/null @@ -1,1660 +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 static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; - -import static com.android.launcher3.Flags.enableUnfoldStateAnimation; -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.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.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.IRemoteAnimationRunner; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.window.DesktopModeFlags; -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.VisibleForTesting; -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.dagger.ApplicationContext; -import com.android.launcher3.dagger.LauncherAppSingleton; -import com.android.launcher3.util.DaggerSingletonObject; -import com.android.launcher3.util.Preconditions; -import com.android.quickstep.dagger.QuickstepBaseAppComponent; -import com.android.quickstep.util.ActiveGestureProtoLogProxy; -import com.android.quickstep.util.ContextualSearchInvoker; -import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider; -import com.android.systemui.shared.recents.ISystemUiProxy; -import com.android.systemui.shared.recents.model.ThumbnailData; -import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; -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.config.ResourceUnfoldTransitionConfig; -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.pip.IPip; -import com.android.wm.shell.common.pip.IPipAnimationListener; -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.recents.IRecentTasks; -import com.android.wm.shell.recents.IRecentTasksListener; -import com.android.wm.shell.recents.IRecentsAnimationController; -import com.android.wm.shell.recents.IRecentsAnimationRunner; -import com.android.wm.shell.shared.GroupedTaskInfo; -import com.android.wm.shell.shared.IShellTransitions; -import com.android.wm.shell.shared.bubbles.BubbleBarLocation; -import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; -import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; -import com.android.wm.shell.shared.split.SplitBounds; -import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition; -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 java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; - -import javax.inject.Inject; - -/** - * Holds the reference to SystemUI. - */ -@LauncherAppSingleton -public class SystemUiProxy implements ISystemUiProxy, NavHandle { - private static final String TAG = "SystemUiProxy"; - - public static final DaggerSingletonObject INSTANCE = - new DaggerSingletonObject<>(QuickstepBaseAppComponent::getSystemUiProxy); - - 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 mRemoteTransitions = - new LinkedHashMap<>(); - - private final List mStateChangeCallbacks = new ArrayList<>(); - - private IBinder mOriginalTransactionToken = null; - private IOnBackInvokedCallback mBackToLauncherCallback; - private IRemoteAnimationRunner mBackToLauncherRunner; - private IDragAndDrop mDragAndDrop; - private final HomeVisibilityState mHomeVisibilityState = new HomeVisibilityState(); - private final FocusState mFocusState = new FocusState(); - - // 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 - @SystemUiStateFlags - private long 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; - - @Nullable - private final ProxyUnfoldTransitionProvider mUnfoldTransitionProvider; - - @Inject - public SystemUiProxy(@ApplicationContext 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()); - - mUnfoldTransitionProvider = - (enableUnfoldStateAnimation() && new ResourceUnfoldTransitionConfig().isEnabled()) - ? new ProxyUnfoldTransitionProvider() : null; - } - - @Override - public void onBackEvent(KeyEvent backEvent) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onBackEvent(backEvent); - } 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 onImeSwitcherLongPress() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onImeSwitcherLongPress(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onImeSwitcherLongPress"); - } - } - } - - @Override - public void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.updateContextualEduStats(isTrackpadGesture, gestureType); - } catch (RemoteException e) { - Log.w(TAG, "Failed call updateContextualEduStats"); - } - } - } - - @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 = enableUnfoldStateAnimation() ? null : 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); - mHomeVisibilityState.init(mShellTransitions); - mFocusState.init(mShellTransitions); - setStartingWindowListener(mStartingWindowListener); - setLauncherUnlockAnimationController( - mLauncherActivityClass, mLauncherUnlockAnimationController); - new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition); - setupTransactionQueue(); - registerRecentTasksListener(mRecentTasksListener); - setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner); - setUnfoldAnimationListener(mUnfoldAnimationListener); - setDesktopTaskListener(mDesktopTaskListener); - setAssistantOverridesRequested(new ContextualSearchInvoker(mContext) - .getSysUiAssistOverrideInvocationTypes()); - mStateChangeCallbacks.forEach(Runnable::run); - - if (mUnfoldTransitionProvider != null) { - if (unfoldAnimation != null) { - try { - unfoldAnimation.setListener(mUnfoldTransitionProvider); - mUnfoldTransitionProvider.setActive(true); - } catch (RemoteException e) { - // Ignore - } - } else { - mUnfoldTransitionProvider.setActive(false); - } - } - } - - /** - * 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); - } - - /** - * Adds a callback to be notified whenever the active state changes - */ - public void addOnStateChangeListener(Runnable callback) { - mStateChangeCallbacks.add(callback); - } - - /** - * Removes a previously added state change callback - */ - public void removeOnStateChangeListener(Runnable callback) { - mStateChangeCallbacks.remove(callback); - } - - // TODO(141886704): Find a way to remove this - public void setLastSystemUiStateFlags(@SystemUiStateFlags long stateFlags) { - mLastSystemUiStateFlags = stateFlags; - } - - // TODO(141886704): Find a way to remove this - @SystemUiStateFlags - public long 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 setOverrideHomeButtonLongPress(long duration, float slopMultiplier, - boolean haptic) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.setOverrideHomeButtonLongPress(duration, slopMultiplier, haptic); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setOverrideHomeButtonLongPress", 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); - } - } - } - - @Override - public void toggleQuickSettingsPanel() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.toggleQuickSettingsPanel(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call toggleQuickSettingsPanel", 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, Rect appBounds, Rect sourceRectHint) { - if (mPip != null) { - try { - mPip.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay, - appBounds, sourceRectHint); - } 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 top top coordinate of bubble bar on screen - */ - public void showBubble(String key, int top) { - if (mBubbles != null) { - try { - mBubbles.showBubble(key, top); - } catch (RemoteException e) { - Log.w(TAG, "Failed call showBubble"); - } - } - } - - /** - * 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 key of the bubble being dragged - */ - public void startBubbleDrag(@Nullable String bubbleKey) { - if (mBubbles == null) return; - try { - mBubbles.startBubbleDrag(bubbleKey); - } catch (RemoteException e) { - Log.w(TAG, "Failed call startBubbleDrag"); - } - } - - /** - * Tells SysUI when the bubble stops being dragged. - * Should be called only when the bubble bar is expanded. - * - * @param location location of the bubble bar - * @param top new top coordinate for bubble bar on screen - */ - public void stopBubbleDrag(BubbleBarLocation location, int top) { - if (mBubbles == null) return; - try { - mBubbles.stopBubbleDrag(location, top); - } catch (RemoteException e) { - Log.w(TAG, "Failed call stopBubbleDrag"); - } - } - - /** - * Tells SysUI to dismiss the bubble with the provided key. - * - * @param key the key of the bubble to dismiss. - * @param timestamp the timestamp when the removal happened. - */ - public void dragBubbleToDismiss(String key, long timestamp) { - if (mBubbles == null) return; - try { - mBubbles.dragBubbleToDismiss(key, timestamp); - } catch (RemoteException e) { - Log.w(TAG, "Failed call dragBubbleToDismiss"); - } - } - - /** - * 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"); - } - } - - /** - * Tells SysUI to update the bubble bar location to the new location. - * @param location new location for the bubble bar - * @param source what triggered the location update - */ - public void setBubbleBarLocation(BubbleBarLocation location, - @BubbleBarLocation.UpdateSource int source) { - try { - mBubbles.setBubbleBarLocation(location, source); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setBubbleBarLocation"); - } - } - - /** - * Tells SysUI the top coordinate of bubble bar on screen - * - * @param topOnScreen top coordinate for bubble bar on screen - */ - public void updateBubbleBarTopOnScreen(int topOnScreen) { - try { - if (mBubbles != null) { - mBubbles.updateBubbleBarTopOnScreen(topOnScreen); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed call updateBubbleBarTopOnScreen"); - } - } - - /** - * Tells SysUI to show a shortcut bubble. - * - * @param info the shortcut info used to create or identify the bubble. - */ - public void showShortcutBubble(ShortcutInfo info) { - try { - if (mBubbles != null) { - mBubbles.showShortcutBubble(info); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed call show bubble for shortcut"); - } - } - - /** - * Tells SysUI to show a bubble of an app. - * - * @param intent the intent used to create the bubble. - */ - public void showAppBubble(Intent intent) { - try { - if (mBubbles != null) { - mBubbles.showAppBubble(intent); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed call show bubble for app"); - } - } - - /** Tells SysUI to show the expanded view. */ - public void showExpandedView() { - try { - if (mBubbles != null) { - mBubbles.showExpandedView(); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed to call showExpandedView"); - } - } - - // - // 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); - } - } - } - - 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); - } - } - } - - // - // 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 HomeVisibilityState getHomeVisibilityState() { - return mHomeVisibilityState; - } - - public FocusState getFocusState() { - return mFocusState; - } - - /** - * Returns a surface which can be used to attach overlays to home task or null if - * the task doesn't exist or sysui is not connected - */ - @Nullable - public SurfaceControl getHomeTaskOverlayContainer() { - // Use a local reference as this method can be called on a worker thread, which can lead - // to NullPointer exceptions if mShellTransitions is modified on the main thread. - IShellTransitions shellTransitions = mShellTransitions; - if (shellTransitions != null) { - try { - return mShellTransitions.getHomeTaskOverlayContainer(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call getOverlayContainerForTask", e); - } - } - return 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 static class GetRecentTasksException extends Exception { - public GetRecentTasksException(String message) { - super(message); - } - - public GetRecentTasksException(String message, Throwable cause) { - super(message, cause); - } - } - - /** - * Retrieves a list of Recent tasks from ActivityManager. - * @throws GetRecentTasksException if IRecentTasks is not initialized, or when we get - * RemoteException from server side - */ - public ArrayList getRecentTasks(int numTasks, - int userId) throws GetRecentTasksException { - if (mRecentTasks == null) { - Log.e(TAG, "getRecentTasks() failed due to null mRecentTasks"); - throw new GetRecentTasksException("null mRecentTasks"); - } - try { - final GroupedTaskInfo[] rawTasks = - mRecentTasks.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId); - if (rawTasks == null) { - return new ArrayList<>(); - } - return new ArrayList<>(Arrays.asList(rawTasks)); - } catch (RemoteException e) { - Log.e(TAG, "Failed call getRecentTasks", e); - throw new GetRecentTasksException("Failed call getRecentTasks", e); - } - } - - /** - * Gets the set of running tasks. - */ - public ArrayList getRunningTasks(int numTasks) { - if (mRecentTasks != null && shouldEnableRunningTasksForDesktopMode()) { - try { - return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks))); - } catch (RemoteException e) { - Log.w(TAG, "Failed call getRunningTasks", e); - } - } - return new ArrayList<>(); - } - - private boolean shouldEnableRunningTasksForDesktopMode() { - return DesktopModeStatus.canEnterDesktopMode(mContext) - && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue(); - } - - 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); - } - } - } - - /** - * If task with the given id is on the desktop, bring it to front - */ - public void showDesktopApp(int taskId, @Nullable RemoteTransition transition) { - if (mDesktopMode != null) { - try { - mDesktopMode.showDesktopApp(taskId, transition); - } 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); - } - } - } - - /** Call shell to move a task with given `taskId` to desktop */ - public void moveToDesktop(int taskId, DesktopModeTransitionSource transitionSource, - @Nullable RemoteTransition transition) { - if (mDesktopMode != null) { - try { - mDesktopMode.moveToDesktop(taskId, transitionSource, transition); - } catch (RemoteException e) { - Log.w(TAG, "Failed call moveToDesktop", e); - } - } - } - - /** Call shell to remove the desktop that is on given `displayId` */ - public void removeDesktop(int displayId) { - if (mDesktopMode != null) { - try { - mDesktopMode.removeDesktop(displayId); - } catch (RemoteException e) { - Log.w(TAG, "Failed call removeDesktop", e); - } - } - } - - /** Call shell to move a task with given `taskId` to external display. */ - public void moveToExternalDisplay(int taskId) { - if (mDesktopMode != null) { - try { - mDesktopMode.moveToExternalDisplay(taskId); - } catch (RemoteException e) { - Log.w(TAG, "Failed call moveToExternalDisplay", 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); - } - } - - @Nullable - public ProxyUnfoldTransitionProvider getUnfoldTransitionProvider() { - return mUnfoldTransitionProvider; - } - - // - // 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, boolean useSyntheticRecentsTransition) { - if (mRecentTasks == null) { - ActiveGestureProtoLogProxy.logRecentTasksMissing(); - 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(SplitBounds.class.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(); - if (useSyntheticRecentsTransition) { - optsBundle.putBoolean("is_synthetic_recents_transition", true); - } - 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("\tmHomeVisibilityState=" + mHomeVisibilityState); - pw.println("\tmFocusState=" + mFocusState); - 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); - } - - /** - * Adds all interfaces held by this proxy to the bundle - */ - @VisibleForTesting - public void addAllInterfaces(Bundle out) { - QuickStepContract.addInterface(mSystemUiProxy, out); - QuickStepContract.addInterface(mPip, out); - QuickStepContract.addInterface(mBubbles, out); - QuickStepContract.addInterface(mSysuiUnlockAnimationController, out); - QuickStepContract.addInterface(mSplitScreen, out); - QuickStepContract.addInterface(mOneHanded, out); - QuickStepContract.addInterface(mShellTransitions, out); - QuickStepContract.addInterface(mStartingWindow, out); - QuickStepContract.addInterface(mRecentTasks, out); - QuickStepContract.addInterface(mBackAnimation, out); - QuickStepContract.addInterface(mDesktopMode, out); - QuickStepContract.addInterface(mUnfoldAnimation, out); - QuickStepContract.addInterface(mDragAndDrop, out); - } -} diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt new file mode 100644 index 0000000000..75694af97d --- /dev/null +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt @@ -0,0 +1,1270 @@ +/* + * 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.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.IRemoteAnimationRunner +import android.view.KeyEvent +import android.view.MotionEvent +import android.view.RemoteAnimationTarget +import android.view.SurfaceControl +import android.view.SurfaceControl.Transaction +import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS +import android.window.IOnBackInvokedCallback +import android.window.RemoteTransition +import android.window.TaskSnapshot +import android.window.TransitionFilter +import androidx.annotation.MainThread +import androidx.annotation.VisibleForTesting +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.Flags +import com.android.launcher3.dagger.ApplicationContext +import com.android.launcher3.dagger.LauncherAppComponent +import com.android.launcher3.dagger.LauncherAppSingleton +import com.android.launcher3.util.DaggerSingletonObject +import com.android.launcher3.util.Executors +import com.android.launcher3.util.Preconditions +import com.android.launcher3.util.SplitConfigurationOptions.StagePosition +import com.android.quickstep.util.ActiveGestureProtoLogProxy +import com.android.quickstep.util.ContextualSearchInvoker +import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider +import com.android.systemui.shared.recents.ISystemUiProxy +import com.android.systemui.shared.recents.model.ThumbnailData.Companion.wrap +import com.android.systemui.shared.system.QuickStepContract +import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags +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.config.ResourceUnfoldTransitionConfig +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.pip.IPip +import com.android.wm.shell.common.pip.IPipAnimationListener +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.recents.IRecentTasks +import com.android.wm.shell.recents.IRecentTasksListener +import com.android.wm.shell.recents.IRecentsAnimationController +import com.android.wm.shell.recents.IRecentsAnimationRunner +import com.android.wm.shell.shared.GroupedTaskInfo +import com.android.wm.shell.shared.IShellTransitions +import com.android.wm.shell.shared.bubbles.BubbleBarLocation +import com.android.wm.shell.shared.bubbles.BubbleBarLocation.UpdateSource +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource +import com.android.wm.shell.shared.split.SplitBounds +import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition +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 java.io.PrintWriter +import javax.inject.Inject + +/** Holds the reference to SystemUI. */ +@LauncherAppSingleton +class SystemUiProxy @Inject constructor(@ApplicationContext private val context: Context) : + NavHandle { + + 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 + + private val systemUiProxyDeathRecipient = + IBinder.DeathRecipient { Executors.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 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() + + private val stateChangeCallbacks: MutableList = ArrayList() + + private var originalTransactionToken: IBinder? = null + private var backToLauncherCallback: IOnBackInvokedCallback? = null + private var backToLauncherRunner: IRemoteAnimationRunner? = null + private var dragAndDrop: IDragAndDrop? = null + val homeVisibilityState = HomeVisibilityState() + private val focusState = FocusState() + + // 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 + + private val asyncHandler = + Handler(Executors.UI_HELPER_EXECUTOR.looper) { handleMessageAsync(it) } + + // TODO(141886704): Find a way to remove this + @SystemUiStateFlags var lastSystemUiStateFlags: Long = 0 + + /** + * 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(), + ) + + val unfoldTransitionProvider: ProxyUnfoldTransitionProvider? = + if ((Flags.enableUnfoldStateAnimation() && ResourceUnfoldTransitionConfig().isEnabled)) + ProxyUnfoldTransitionProvider() + else null + + private inline fun executeWithErrorLog( + errorMsg: () -> String, + tag: String = TAG, + callback: () -> Any?, + ) { + try { + callback.invoke() + } catch (e: RemoteException) { + Log.w(tag, errorMsg.invoke(), e) + } + } + + fun onBackEvent(backEvent: KeyEvent?) = + executeWithErrorLog({ "Failed call onBackPressed" }) { + systemUiProxy?.onBackEvent(backEvent) + } + + fun onImeSwitcherPressed() = + executeWithErrorLog({ "Failed call onImeSwitcherPressed" }) { + systemUiProxy?.onImeSwitcherPressed() + } + + fun onImeSwitcherLongPress() = + executeWithErrorLog({ "Failed call onImeSwitcherLongPress" }) { + systemUiProxy?.onImeSwitcherLongPress() + } + + fun updateContextualEduStats(isTrackpadGesture: Boolean, gestureType: String) = + executeWithErrorLog({ "Failed call updateContextualEduStats" }) { + systemUiProxy?.updateContextualEduStats(isTrackpadGesture, gestureType) + } + + fun setHomeRotationEnabled(enabled: Boolean) = + executeWithErrorLog({ "Failed call setHomeRotationEnabled" }) { + systemUiProxy?.setHomeRotationEnabled(enabled) + } + + /** + * 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 = if (Flags.enableUnfoldStateAnimation()) null else 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) + homeVisibilityState.init(this.shellTransitions) + focusState.init(this.shellTransitions) + setStartingWindowListener(startingWindowListener) + setLauncherUnlockAnimationController( + launcherActivityClass, + launcherUnlockAnimationController, + ) + LinkedHashMap(remoteTransitions).forEach { (remoteTransition, filter) -> + registerRemoteTransition(remoteTransition, filter) + } + setupTransactionQueue() + registerRecentTasksListener(recentTasksListener) + setBackToLauncherCallback(backToLauncherCallback, backToLauncherRunner) + setUnfoldAnimationListener(unfoldAnimationListener) + setDesktopTaskListener(desktopTaskListener) + setAssistantOverridesRequested( + ContextualSearchInvoker(context).getSysUiAssistOverrideInvocationTypes() + ) + stateChangeCallbacks.forEach { it.run() } + + if (unfoldTransitionProvider != null) { + if (unfoldAnimation != null) { + try { + unfoldAnimation.setListener(unfoldTransitionProvider) + unfoldTransitionProvider.isActive = true + } catch (e: RemoteException) { + // Ignore + } + } else { + unfoldTransitionProvider.isActive = false + } + } + } + + /** + * 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) + + /** Adds a callback to be notified whenever the active state changes */ + fun addOnStateChangeListener(callback: Runnable) = stateChangeCallbacks.add(callback) + + /** Removes a previously added state change callback */ + fun removeOnStateChangeListener(callback: Runnable) = stateChangeCallbacks.remove(callback) + + fun isActive() = systemUiProxy != null + + private fun linkToDeath() = + executeWithErrorLog({ "Failed to link sysui proxy death recipient" }) { + systemUiProxy?.asBinder()?.linkToDeath(systemUiProxyDeathRecipient, 0 /* flags */) + } + + private fun unlinkToDeath() = + systemUiProxy?.asBinder()?.unlinkToDeath(systemUiProxyDeathRecipient, 0 /* flags */) + + fun startScreenPinning(taskId: Int) = + executeWithErrorLog({ "Failed call startScreenPinning" }) { + systemUiProxy?.startScreenPinning(taskId) + } + + fun onOverviewShown(fromHome: Boolean, tag: String = TAG) = + executeWithErrorLog( + { "Failed call onOverviewShown from: ${(if (fromHome) "home" else "app")}" }, + tag = tag, + ) { + systemUiProxy?.onOverviewShown(fromHome) + } + + @MainThread + fun onStatusBarTouchEvent(event: MotionEvent) { + Preconditions.assertUIThread() + executeWithErrorLog({ "Failed call onStatusBarTouchEvent with arg: $event" }) { + systemUiProxy?.onStatusBarTouchEvent(event) + } + } + + fun onStatusBarTrackpadEvent(event: MotionEvent) = + executeWithErrorLog({ "Failed call onStatusBarTrackpadEvent with arg: $event" }) { + systemUiProxy?.onStatusBarTrackpadEvent(event) + } + + fun onAssistantProgress(progress: Float) = + executeWithErrorLog({ "Failed call onAssistantProgress with progress: $progress" }) { + systemUiProxy?.onAssistantProgress(progress) + } + + fun onAssistantGestureCompletion(velocity: Float) = + executeWithErrorLog({ "Failed call onAssistantGestureCompletion" }) { + systemUiProxy?.onAssistantGestureCompletion(velocity) + } + + fun startAssistant(args: Bundle) = + executeWithErrorLog({ "Failed call startAssistant" }) { + systemUiProxy?.startAssistant(args) + } + + fun setAssistantOverridesRequested(invocationTypes: IntArray) = + executeWithErrorLog({ "Failed call setAssistantOverridesRequested" }) { + systemUiProxy?.setAssistantOverridesRequested(invocationTypes) + } + + override fun animateNavBarLongPress(isTouchDown: Boolean, shrink: Boolean, durationMs: Long) = + executeWithErrorLog({ "Failed call animateNavBarLongPress" }) { + systemUiProxy?.animateNavBarLongPress(isTouchDown, shrink, durationMs) + } + + fun setOverrideHomeButtonLongPress(duration: Long, slopMultiplier: Float, haptic: Boolean) = + executeWithErrorLog({ "Failed call setOverrideHomeButtonLongPress" }) { + systemUiProxy?.setOverrideHomeButtonLongPress(duration, slopMultiplier, haptic) + } + + fun notifyAccessibilityButtonClicked(displayId: Int) = + executeWithErrorLog({ "Failed call notifyAccessibilityButtonClicked" }) { + systemUiProxy?.notifyAccessibilityButtonClicked(displayId) + } + + fun notifyAccessibilityButtonLongClicked() = + executeWithErrorLog({ "Failed call notifyAccessibilityButtonLongClicked" }) { + systemUiProxy?.notifyAccessibilityButtonLongClicked() + } + + fun stopScreenPinning() = + executeWithErrorLog({ "Failed call stopScreenPinning" }) { + systemUiProxy?.stopScreenPinning() + } + + fun notifyPrioritizedRotation(rotation: Int) = + executeWithErrorLog({ "Failed call notifyPrioritizedRotation with arg: $rotation" }) { + systemUiProxy?.notifyPrioritizedRotation(rotation) + } + + fun notifyTaskbarStatus(visible: Boolean, stashed: Boolean) = + executeWithErrorLog({ "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. [suspend] should + * be `true` to stop auto-hide, `false` to resume normal behavior + */ + fun notifyTaskbarAutohideSuspend(suspend: Boolean) = + executeWithErrorLog({ "Failed call notifyTaskbarAutohideSuspend with arg: $suspend" }) { + systemUiProxy?.notifyTaskbarAutohideSuspend(suspend) + } + + fun takeScreenshot(request: ScreenshotRequest) = + executeWithErrorLog({ "Failed call takeScreenshot" }) { + systemUiProxy?.takeScreenshot(request) + } + + fun expandNotificationPanel() = + executeWithErrorLog({ "Failed call expandNotificationPanel" }) { + systemUiProxy?.expandNotificationPanel() + } + + fun toggleNotificationPanel() = + executeWithErrorLog({ "Failed call toggleNotificationPanel" }) { + systemUiProxy?.toggleNotificationPanel() + } + + fun toggleQuickSettingsPanel() = + executeWithErrorLog({ "Failed call toggleQuickSettingsPanel" }) { + systemUiProxy?.toggleQuickSettingsPanel() + } + + // + // 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 + val changed = visible != lastShelfVisible || shelfHeight != lastShelfHeight + val pip = pip + if (pip != null && changed) { + lastShelfVisible = visible + lastShelfHeight = shelfHeight + executeWithErrorLog({ + "Failed call setShelfHeight visible: $visible height: $shelfHeight" + }) { + pip.setShelfHeight(visible, 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 + val changed = + visible != lastLauncherKeepClearAreaHeightVisible || + height != lastLauncherKeepClearAreaHeight + val pip = pip + if (pip != null && changed) { + lastLauncherKeepClearAreaHeightVisible = visible + lastLauncherKeepClearAreaHeight = height + executeWithErrorLog({ + "Failed call setLauncherKeepClearAreaHeight visible: $visible height: $height" + }) { + pip.setLauncherKeepClearAreaHeight(visible, height) + } + } + } + + /** Sets listener to get pip animation callbacks. */ + fun setPipAnimationListener(listener: IPipAnimationListener?) { + executeWithErrorLog({ "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? { + executeWithErrorLog({ "Failed call startSwipePipToHome" }) { + return pip?.startSwipePipToHome( + componentName, + activityInfo, + pictureInPictureParams, + launcherRotation, + hotseatKeepClearArea, + ) + } + 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. + */ + fun stopSwipePipToHome( + taskId: Int, + componentName: ComponentName?, + destinationBounds: Rect?, + overlay: SurfaceControl?, + appBounds: Rect?, + sourceRectHint: Rect?, + ) = + executeWithErrorLog({ "Failed call stopSwipePipToHome" }) { + pip?.stopSwipePipToHome( + taskId, + componentName, + destinationBounds, + overlay, + appBounds, + sourceRectHint, + ) + } + + /** + * 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?) = + executeWithErrorLog({ "Failed call abortSwipePipToHome" }) { + pip?.abortSwipePipToHome(taskId, componentName) + } + + /** Sets the next pip animation type to be the alpha animation. */ + fun setPipAnimationTypeToAlpha() = + executeWithErrorLog({ "Failed call setPipAnimationTypeToAlpha" }) { + pip?.setPipAnimationTypeToAlpha() + } + + /** Sets the app icon size in pixel used by Launcher all apps. */ + fun setLauncherAppIconSize(iconSizePx: Int) = + executeWithErrorLog({ "Failed call setLauncherAppIconSize" }) { + pip?.setLauncherAppIconSize(iconSizePx) + } + + // + // Bubbles + // + /** Sets the listener to be notified of bubble state changes. */ + fun setBubblesListener(listener: IBubblesListener?) { + executeWithErrorLog({ "Failed call registerBubblesListener" }) { + bubbles?.apply { + bubblesListener?.let { unregisterBubbleListener(it) } + listener?.let { registerBubbleListener(it) } + } + } + bubblesListener = listener + } + + /** + * Tells SysUI to show the bubble with the provided key. + * + * @param key the key of the bubble to show. + * @param top top coordinate of bubble bar on screen + */ + fun showBubble(key: String?, top: Int) = + executeWithErrorLog({ "Failed call showBubble" }) { bubbles?.showBubble(key, top) } + + /** Tells SysUI to remove all bubbles. */ + fun removeAllBubbles() = + executeWithErrorLog({ "Failed call removeAllBubbles" }) { bubbles?.removeAllBubbles() } + + /** Tells SysUI to collapse the bubbles. */ + fun collapseBubbles() = + executeWithErrorLog({ "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 key of the bubble being dragged + */ + fun startBubbleDrag(bubbleKey: String?) = + executeWithErrorLog({ "Failed call startBubbleDrag" }) { + bubbles?.startBubbleDrag(bubbleKey) + } + + /** + * Tells SysUI when the bubble stops being dragged. Should be called only when the bubble bar is + * expanded. + * + * @param location location of the bubble bar + * @param top new top coordinate for bubble bar on screen + */ + fun stopBubbleDrag(location: BubbleBarLocation?, top: Int) = + executeWithErrorLog({ "Failed call stopBubbleDrag" }) { + bubbles?.stopBubbleDrag(location, top) + } + + /** + * Tells SysUI to dismiss the bubble with the provided key. + * + * @param key the key of the bubble to dismiss. + * @param timestamp the timestamp when the removal happened. + */ + fun dragBubbleToDismiss(key: String?, timestamp: Long) = + executeWithErrorLog({ "Failed call dragBubbleToDismiss" }) { + bubbles?.dragBubbleToDismiss(key, timestamp) + } + + /** + * 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) = + executeWithErrorLog({ "Failed call showUserEducation" }) { + bubbles?.showUserEducation(position.x, position.y) + } + + /** + * Tells SysUI to update the bubble bar location to the new location. + * + * @param location new location for the bubble bar + * @param source what triggered the location update + */ + fun setBubbleBarLocation(location: BubbleBarLocation?, @UpdateSource source: Int) = + executeWithErrorLog({ "Failed call setBubbleBarLocation" }) { + bubbles?.setBubbleBarLocation(location, source) + } + + /** + * Tells SysUI the top coordinate of bubble bar on screen + * + * @param topOnScreen top coordinate for bubble bar on screen + */ + fun updateBubbleBarTopOnScreen(topOnScreen: Int) = + executeWithErrorLog({ "Failed call updateBubbleBarTopOnScreen" }) { + bubbles?.updateBubbleBarTopOnScreen(topOnScreen) + } + + /** + * Tells SysUI to show a shortcut bubble. + * + * @param info the shortcut info used to create or identify the bubble. + */ + fun showShortcutBubble(info: ShortcutInfo?) = + executeWithErrorLog({ "Failed call showShortcutBubble" }) { + bubbles?.showShortcutBubble(info) + } + + /** + * Tells SysUI to show a bubble of an app. + * + * @param intent the intent used to create the bubble. + */ + fun showAppBubble(intent: Intent?) = + executeWithErrorLog({ "Failed call showAppBubble" }) { bubbles?.showAppBubble(intent) } + + /** Tells SysUI to show the expanded view. */ + fun showExpandedView() = + executeWithErrorLog({ "Failed call showExpandedView" }) { bubbles?.showExpandedView() } + + // + // Splitscreen + // + fun registerSplitScreenListener(listener: ISplitScreenListener?) { + executeWithErrorLog({ "Failed call registerSplitScreenListener" }) { + splitScreen?.registerSplitScreenListener(listener) + } + splitScreenListener = listener + } + + fun unregisterSplitScreenListener(listener: ISplitScreenListener?) { + executeWithErrorLog({ "Failed call unregisterSplitScreenListener" }) { + splitScreen?.unregisterSplitScreenListener(listener) + } + splitScreenListener = null + } + + fun registerSplitSelectListener(listener: ISplitSelectListener?) { + executeWithErrorLog({ "Failed call registerSplitSelectListener" }) { + splitScreen?.registerSplitSelectListener(listener) + } + splitSelectListener = listener + } + + fun unregisterSplitSelectListener(listener: ISplitSelectListener?) { + executeWithErrorLog({ "Failed call unregisterSplitSelectListener" }) { + splitScreen?.unregisterSplitSelectListener(listener) + } + splitSelectListener = null + } + + /** 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?, + ) = + executeWithErrorLog({ "Failed call startTasks" }) { + 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?, + ) = + executeWithErrorLog({ "Failed call startIntentAndTask" }) { + 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?, + ) = + executeWithErrorLog({ "Failed call startIntents" }) { + 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?, + ) = + executeWithErrorLog({ "Failed call startShortcutAndTask" }) { + splitScreen?.startShortcutAndTask( + shortcutInfo, + options1, + taskId, + options2, + splitPosition, + snapPosition, + remoteTransition, + instanceId, + ) + } + + fun startShortcut( + packageName: String?, + shortcutId: String?, + position: Int, + options: Bundle?, + user: UserHandle?, + instanceId: InstanceId?, + ) = + executeWithErrorLog({ "Failed call startShortcut" }) { + splitScreen?.startShortcut(packageName, shortcutId, position, options, user, instanceId) + } + + fun startIntent( + intent: PendingIntent?, + userId: Int, + fillInIntent: Intent?, + position: Int, + options: Bundle?, + instanceId: InstanceId?, + ) = + executeWithErrorLog({ "Failed call startIntent" }) { + splitScreen?.startIntent(intent, userId, fillInIntent, position, options, instanceId) + } + + // + // One handed + // + fun startOneHandedMode() = + executeWithErrorLog({ "Failed call startOneHandedMode" }) { oneHanded?.startOneHanded() } + + fun stopOneHandedMode() = + executeWithErrorLog({ "Failed call stopOneHandedMode" }) { oneHanded?.stopOneHanded() } + + // + // Remote transitions + // + fun registerRemoteTransition(remoteTransition: RemoteTransition?, filter: TransitionFilter) { + remoteTransition ?: return + executeWithErrorLog({ "Failed call registerRemoteTransition" }) { + shellTransitions?.registerRemote(filter, remoteTransition) + } + remoteTransitions.putIfAbsent(remoteTransition, filter) + } + + fun unregisterRemoteTransition(remoteTransition: RemoteTransition?) { + executeWithErrorLog({ "Failed call unregisterRemoteTransition" }) { + shellTransitions?.unregisterRemote(remoteTransition) + } + remoteTransitions.remove(remoteTransition) + } + + /** + * Returns a surface which can be used to attach overlays to home task or null if the task + * doesn't exist or sysui is not connected + */ + fun getHomeTaskOverlayContainer(): SurfaceControl? { + executeWithErrorLog({ "Failed call getHomeTaskOverlayContainer" }) { + return shellTransitions?.homeTaskOverlayContainer + } + return 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 = Transaction.getDefaultApplyToken() + } + setupTransactionQueue() + } + + /** Switch back to using Launcher's independent transaction queue. */ + fun unshareTransactionQueue() { + if (originalTransactionToken == null) { + return + } + Transaction.setDefaultApplyToken(originalTransactionToken) + originalTransactionToken = null + } + + private fun setupTransactionQueue() = + executeWithErrorLog({ "Error getting Shell's apply token" }) { + val token: IBinder = + shellTransitions?.shellApplyToken ?: originalTransactionToken ?: return + Transaction.setDefaultApplyToken(token) + } + + // + // Starting window + // + /** Sets listener to get callbacks when launching a task. */ + fun setStartingWindowListener(listener: IStartingWindowListener?) { + executeWithErrorLog({ "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?, + ) { + executeWithErrorLog({ "Failed call setLauncherUnlockAnimationController" }) { + sysuiUnlockAnimationController?.apply { + 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?) = + executeWithErrorLog({ "Failed call notifySysuiSmartspaceStateUpdated" }) { + sysuiUnlockAnimationController?.onLauncherSmartspaceStateUpdated(state) + } + + // + // Recents + // + fun registerRecentTasksListener(listener: IRecentTasksListener?) { + executeWithErrorLog({ "Failed call registerRecentTasksListener" }) { + recentTasks?.registerRecentTasksListener(listener) + } + recentTasksListener = listener + } + + fun unregisterRecentTasksListener(listener: IRecentTasksListener?) { + executeWithErrorLog({ "Failed call unregisterRecentTasksListener" }) { + recentTasks?.unregisterRecentTasksListener(listener) + } + recentTasksListener = null + } + + // + // Back navigation transitions + // + /** Sets the launcher [android.window.IOnBackInvokedCallback] to shell */ + fun setBackToLauncherCallback( + callback: IOnBackInvokedCallback?, + runner: IRemoteAnimationRunner?, + ) { + backToLauncherCallback = callback + backToLauncherRunner = runner + if (callback == null) return + try { + backAnimation?.setBackToLauncherCallback(callback, runner) + } catch (e: RemoteException) { + Log.e(TAG, "Failed call setBackToLauncherCallback", e) + } catch (e: SecurityException) { + Log.e(TAG, "Failed call setBackToLauncherCallback", e) + } + } + + /** + * Clears the previously registered [IOnBackInvokedCallback]. + * + * @param callback The previously registered callback instance. + */ + fun clearBackToLauncherCallback(callback: IOnBackInvokedCallback) { + if (backToLauncherCallback !== callback) { + return + } + backToLauncherCallback = null + backToLauncherRunner = null + executeWithErrorLog({ "Failed call clearBackToLauncherCallback" }) { + backAnimation?.clearBackToLauncherCallback() + } + } + + /** Called when the status bar color needs to be customized when back navigation. */ + fun customizeStatusBarAppearance(appearance: AppearanceRegion?) = + executeWithErrorLog({ "Failed call customizeStatusBarAppearance" }) { + backAnimation?.customizeStatusBarAppearance(appearance) + } + + class GetRecentTasksException : Exception { + constructor(message: String?) : super(message) + + constructor(message: String?, cause: Throwable?) : super(message, cause) + } + + /** + * Retrieves a list of Recent tasks from ActivityManager. + * + * @throws GetRecentTasksException if IRecentTasks is not initialized, or when we get + * RemoteException from server side + */ + @Throws(GetRecentTasksException::class) + fun getRecentTasks(numTasks: Int, userId: Int): ArrayList { + if (recentTasks == null) { + Log.e(TAG, "getRecentTasks() failed due to null mRecentTasks") + throw GetRecentTasksException("null mRecentTasks") + } + try { + val rawTasks = + recentTasks?.getRecentTasks( + numTasks, + ActivityManager.RECENT_IGNORE_UNAVAILABLE, + userId, + ) ?: return ArrayList() + return ArrayList(rawTasks.asList()) + } catch (e: RemoteException) { + Log.e(TAG, "Failed call getRecentTasks", e) + throw GetRecentTasksException("Failed call getRecentTasks", e) + } + } + + /** Gets the set of running tasks. */ + fun getRunningTasks(numTasks: Int): List { + if (!shouldEnableRunningTasksForDesktopMode()) return emptyList() + executeWithErrorLog({ "Failed call getRunningTasks" }) { + return recentTasks?.getRunningTasks(numTasks)?.asList() ?: emptyList() + } + return emptyList() + } + + private fun shouldEnableRunningTasksForDesktopMode(): Boolean = + DesktopModeStatus.canEnterDesktopMode(context) && + ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue + + private fun handleMessageAsync(msg: Message): Boolean { + return when (msg.what) { + MSG_SET_SHELF_HEIGHT -> { + setShelfHeightAsync(msg.arg1, msg.arg2) + true + } + + MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT -> { + setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2) + true + } + + else -> false + } + } + + // + // Desktop Mode + // + /** Call shell to show all apps active on the desktop */ + fun showDesktopApps(displayId: Int, transition: RemoteTransition?) = + executeWithErrorLog({ "Failed call showDesktopApps" }) { + desktopMode?.showDesktopApps(displayId, transition) + } + + /** If task with the given id is on the desktop, bring it to front */ + fun showDesktopApp(taskId: Int, transition: RemoteTransition?) = + executeWithErrorLog({ "Failed call showDesktopApp" }) { + desktopMode?.showDesktopApp(taskId, transition) + } + + /** Call shell to get number of visible freeform tasks */ + fun getVisibleDesktopTaskCount(displayId: Int): Int { + executeWithErrorLog({ "Failed call getVisibleDesktopTaskCount" }) { + return desktopMode?.getVisibleTaskCount(displayId) ?: 0 + } + return 0 + } + + /** Set a listener on shell to get updates about desktop task state */ + fun setDesktopTaskListener(listener: IDesktopTaskListener?) { + desktopTaskListener = listener + executeWithErrorLog({ "Failed call setDesktopTaskListener" }) { + desktopMode?.setTaskListener(listener) + } + } + + /** Perform cleanup transactions after animation to split select is complete */ + fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo?) = + executeWithErrorLog({ "Failed call onDesktopSplitSelectAnimComplete" }) { + desktopMode?.onDesktopSplitSelectAnimComplete(taskInfo) + } + + /** Call shell to move a task with given `taskId` to desktop */ + fun moveToDesktop( + taskId: Int, + transitionSource: DesktopModeTransitionSource?, + transition: RemoteTransition?, + ) = + executeWithErrorLog({ "Failed call moveToDesktop" }) { + desktopMode?.moveToDesktop(taskId, transitionSource, transition) + } + + /** Call shell to remove the desktop that is on given `displayId` */ + fun removeDesktop(displayId: Int) = + executeWithErrorLog({ "Failed call removeDesktop" }) { + desktopMode?.removeDesktop(displayId) + } + + /** Call shell to move a task with given `taskId` to external display. */ + fun moveToExternalDisplay(taskId: Int) = + executeWithErrorLog({ "Failed call moveToExternalDisplay" }) { + desktopMode?.moveToExternalDisplay(taskId) + } + + // + // Unfold transition + // + /** Sets the unfold animation lister to sysui. */ + fun setUnfoldAnimationListener(callback: IUnfoldTransitionListener?) { + unfoldAnimationListener = callback + executeWithErrorLog({ "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, + useSyntheticRecentsTransition: Boolean, + ): Boolean { + executeWithErrorLog({ "Error starting recents via shell" }) { + recentTasks?.startRecentsTransition( + recentsPendingIntent, + intent, + options.toBundle().apply { + if (useSyntheticRecentsTransition) { + putBoolean("is_synthetic_recents_transition", true) + } + }, + context.iApplicationThread, + RecentsAnimationListenerStub(listener), + ) + ?: run { + ActiveGestureProtoLogProxy.logRecentTasksMissing() + return false + } + return true + } + return false + } + + private class RecentsAnimationListenerStub(val listener: RecentsAnimationListener) : + IRecentsAnimationRunner.Stub() { + override fun onAnimationStart( + controller: IRecentsAnimationController, + apps: Array?, + wallpapers: Array?, + homeContentInsets: Rect?, + minimizedHomeBounds: Rect?, + extras: Bundle?, + ) = + listener.onAnimationStart( + RecentsAnimationControllerCompat(controller), + apps, + wallpapers, + homeContentInsets, + minimizedHomeBounds, + extras?.apply { + // Aidl bundles need to explicitly set class loader + // https://developer.android.com/guide/components/aidl#Bundles + classLoader = SplitBounds::class.java.classLoader + }, + ) + + override fun onAnimationCanceled(taskIds: IntArray?, taskSnapshots: Array?) = + listener.onAnimationCanceled(wrap(taskIds, taskSnapshots)) + + override fun onTasksAppeared(apps: Array?) = + listener.onTasksAppeared(apps) + } + + // + // 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. + */ + fun isDragAndDropReady(): Boolean { + executeWithErrorLog({ "Error querying drag state" }) { + return dragAndDrop?.isReadyToHandleDrag ?: false + } + return false + } + + fun dump(pw: PrintWriter) { + pw.println("$TAG:") + + pw.println("\tmSystemUiProxy=$systemUiProxy") + pw.println("\tmPip=$pip") + pw.println("\tmPipAnimationListener=$pipAnimationListener") + pw.println("\tmBubbles=$bubbles") + pw.println("\tmBubblesListener=$bubblesListener") + pw.println("\tmSplitScreen=$splitScreen") + pw.println("\tmSplitScreenListener=$splitScreenListener") + pw.println("\tmSplitSelectListener=$splitSelectListener") + pw.println("\tmOneHanded=$oneHanded") + pw.println("\tmShellTransitions=$shellTransitions") + pw.println("\tmHomeVisibilityState=" + homeVisibilityState) + pw.println("\tmFocusState=" + focusState) + pw.println("\tmStartingWindow=$startingWindow") + pw.println("\tmStartingWindowListener=$startingWindowListener") + pw.println("\tmSysuiUnlockAnimationController=$sysuiUnlockAnimationController") + pw.println("\tmLauncherActivityClass=$launcherActivityClass") + pw.println("\tmLauncherUnlockAnimationController=$launcherUnlockAnimationController") + pw.println("\tmRecentTasks=$recentTasks") + pw.println("\tmRecentTasksListener=$recentTasksListener") + pw.println("\tmBackAnimation=$backAnimation") + pw.println("\tmBackToLauncherCallback=$backToLauncherCallback") + pw.println("\tmBackToLauncherRunner=$backToLauncherRunner") + pw.println("\tmDesktopMode=$desktopMode") + pw.println("\tmDesktopTaskListener=$desktopTaskListener") + pw.println("\tmUnfoldAnimation=$unfoldAnimation") + pw.println("\tmUnfoldAnimationListener=$unfoldAnimationListener") + pw.println("\tmDragAndDrop=$dragAndDrop") + } + + /** Adds all interfaces held by this proxy to the bundle */ + @VisibleForTesting + fun addAllInterfaces(out: Bundle) { + QuickStepContract.addInterface(systemUiProxy, out) + QuickStepContract.addInterface(pip, out) + QuickStepContract.addInterface(bubbles, out) + QuickStepContract.addInterface(sysuiUnlockAnimationController, out) + QuickStepContract.addInterface(splitScreen, out) + QuickStepContract.addInterface(oneHanded, out) + QuickStepContract.addInterface(shellTransitions, out) + QuickStepContract.addInterface(startingWindow, out) + QuickStepContract.addInterface(recentTasks, out) + QuickStepContract.addInterface(backAnimation, out) + QuickStepContract.addInterface(desktopMode, out) + QuickStepContract.addInterface(unfoldAnimation, out) + QuickStepContract.addInterface(dragAndDrop, out) + } + + companion object { + private const val TAG = "SystemUiProxy" + + @JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getSystemUiProxy) + + private const val MSG_SET_SHELF_HEIGHT = 1 + private const val MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2 + } +} diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt index cfa12e256b..785e58576f 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt @@ -17,6 +17,7 @@ package com.android.launcher3.taskbar import android.animation.AnimatorTestRule +import androidx.test.core.app.ApplicationProvider import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER @@ -34,6 +35,10 @@ import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.spy +import org.mockito.kotlin.whenever @RunWith(LauncherMultivalentJUnit::class) @EmulatedDevices(["pixelTablet2023"]) @@ -43,11 +48,10 @@ class TaskbarAutohideSuspendControllerTest { val context = TaskbarWindowSandboxContext.create { builder -> builder.bindSystemUiProxy( - object : SystemUiProxy(this) { - override fun notifyTaskbarAutohideSuspend(suspend: Boolean) { - super.notifyTaskbarAutohideSuspend(suspend) - latestSuspendNotification = suspend - } + spy(SystemUiProxy(ApplicationProvider.getApplicationContext())) { proxy -> + doAnswer { latestSuspendNotification = it.getArgument(0) } + .whenever(proxy) + .notifyTaskbarAutohideSuspend(anyOrNull()) } ) } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt index eb13b55cee..36e8a82e1c 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt @@ -21,6 +21,7 @@ import android.content.ComponentName import android.content.Intent import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule +import androidx.test.core.app.ApplicationProvider import com.android.launcher3.Flags.FLAG_TASKBAR_OVERFLOW import com.android.launcher3.R import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync @@ -50,6 +51,10 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.spy +import org.mockito.kotlin.whenever @RunWith(LauncherMultivalentJUnit::class) @EmulatedDevices(["pixelTablet2023"]) @@ -66,10 +71,10 @@ class TaskbarOverflowTest { val context = TaskbarWindowSandboxContext.create { builder -> builder.bindSystemUiProxy( - object : SystemUiProxy(this) { - override fun setDesktopTaskListener(listener: IDesktopTaskListener?) { - desktopTaskListener = listener - } + spy(SystemUiProxy(ApplicationProvider.getApplicationContext())) { proxy -> + doAnswer { desktopTaskListener = it.getArgument(0) } + .whenever(proxy) + .setDesktopTaskListener(anyOrNull()) } ) } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt index 4c94067ca8..360f019205 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt @@ -20,9 +20,9 @@ import android.animation.AnimatorTestRule import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule -import android.view.KeyEvent import android.view.View.GONE import android.view.View.VISIBLE +import androidx.test.core.app.ApplicationProvider import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController import com.android.launcher3.taskbar.rules.TaskbarModeRule @@ -44,6 +44,10 @@ import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.spy +import org.mockito.kotlin.whenever @RunWith(LauncherMultivalentJUnit::class) @EmulatedDevices(["pixelTablet2023"]) @@ -53,11 +57,8 @@ class TaskbarScrimViewControllerTest { val context = TaskbarWindowSandboxContext.create { builder -> builder.bindSystemUiProxy( - object : SystemUiProxy(this) { - override fun onBackEvent(backEvent: KeyEvent?) { - super.onBackEvent(backEvent) - backPressed = true - } + spy(SystemUiProxy(ApplicationProvider.getApplicationContext())) { + doAnswer { backPressed = true }.whenever(it).onBackEvent(anyOrNull()) } ) }