diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java deleted file mode 100644 index 0d7ca07f3a..0000000000 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ /dev/null @@ -1,1515 +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.testing.shared.TestProtocol.testLogD; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; -import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition; -import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING; -import static com.android.quickstep.util.LogUtils.splitFailureMessage; - -import android.app.ActivityManager; -import android.app.ActivityOptions; -import android.app.PendingIntent; -import android.app.PictureInPictureParams; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.ShortcutInfo; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.IBinder.DeathRecipient; -import android.os.Message; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.Log; -import android.view.IRecentsAnimationController; -import android.view.IRecentsAnimationRunner; -import android.view.IRemoteAnimationRunner; -import android.view.MotionEvent; -import android.view.RemoteAnimationAdapter; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.window.IOnBackInvokedCallback; -import android.window.RemoteTransition; -import android.window.TaskSnapshot; -import android.window.TransitionFilter; - -import androidx.annotation.MainThread; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; - -import com.android.internal.logging.InstanceId; -import com.android.internal.util.ScreenshotRequest; -import com.android.internal.view.AppearanceRegion; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.util.MainThreadInitializedObject; -import com.android.launcher3.util.Preconditions; -import com.android.quickstep.util.ActiveGestureLog; -import com.android.quickstep.util.AssistUtils; -import com.android.systemui.shared.recents.ISystemUiProxy; -import com.android.systemui.shared.recents.model.ThumbnailData; -import com.android.systemui.shared.system.RecentsAnimationControllerCompat; -import com.android.systemui.shared.system.RecentsAnimationListener; -import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController; -import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController; -import com.android.systemui.shared.system.smartspace.SmartspaceState; -import com.android.systemui.unfold.progress.IUnfoldAnimation; -import com.android.systemui.unfold.progress.IUnfoldTransitionListener; -import com.android.wm.shell.back.IBackAnimation; -import com.android.wm.shell.bubbles.IBubbles; -import com.android.wm.shell.bubbles.IBubblesListener; -import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; -import com.android.wm.shell.desktopmode.IDesktopMode; -import com.android.wm.shell.desktopmode.IDesktopTaskListener; -import com.android.wm.shell.draganddrop.IDragAndDrop; -import com.android.wm.shell.onehanded.IOneHanded; -import com.android.wm.shell.pip.IPip; -import com.android.wm.shell.pip.IPipAnimationListener; -import com.android.wm.shell.recents.IRecentTasks; -import com.android.wm.shell.recents.IRecentTasksListener; -import com.android.wm.shell.splitscreen.ISplitScreen; -import com.android.wm.shell.splitscreen.ISplitScreenListener; -import com.android.wm.shell.splitscreen.ISplitSelectListener; -import com.android.wm.shell.startingsurface.IStartingWindow; -import com.android.wm.shell.startingsurface.IStartingWindowListener; -import com.android.wm.shell.transition.IHomeTransitionListener; -import com.android.wm.shell.transition.IShellTransitions; -import com.android.wm.shell.util.GroupedRecentTaskInfo; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; - -/** - * Holds the reference to SystemUI. - */ -public class SystemUiProxy implements ISystemUiProxy { - private static final String TAG = SystemUiProxy.class.getSimpleName(); - - public static final MainThreadInitializedObject INSTANCE = - new MainThreadInitializedObject<>(SystemUiProxy::new); - - private static final int MSG_SET_SHELF_HEIGHT = 1; - private static final int MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2; - - private ISystemUiProxy mSystemUiProxy; - private IPip mPip; - private IBubbles mBubbles; - private ISysuiUnlockAnimationController mSysuiUnlockAnimationController; - private ISplitScreen mSplitScreen; - private IOneHanded mOneHanded; - private IShellTransitions mShellTransitions; - private IStartingWindow mStartingWindow; - private IRecentTasks mRecentTasks; - private IBackAnimation mBackAnimation; - private IDesktopMode mDesktopMode; - private IUnfoldAnimation mUnfoldAnimation; - private final DeathRecipient mSystemUiProxyDeathRecipient = () -> { - MAIN_EXECUTOR.execute(() -> clearProxy()); - }; - - // Save the listeners passed into the proxy since OverviewProxyService may not have been bound - // yet, and we'll need to set/register these listeners with SysUI when they do. Note that it is - // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely - // in case SysUI needs to rebind. - private IPipAnimationListener mPipAnimationListener; - private IBubblesListener mBubblesListener; - private ISplitScreenListener mSplitScreenListener; - private ISplitSelectListener mSplitSelectListener; - private IStartingWindowListener mStartingWindowListener; - private ILauncherUnlockAnimationController mLauncherUnlockAnimationController; - private String mLauncherActivityClass; - private IRecentTasksListener mRecentTasksListener; - private IUnfoldTransitionListener mUnfoldAnimationListener; - private IDesktopTaskListener mDesktopTaskListener; - private final LinkedHashMap mRemoteTransitions = - new LinkedHashMap<>(); - private IBinder mOriginalTransactionToken = null; - private IOnBackInvokedCallback mBackToLauncherCallback; - private IRemoteAnimationRunner mBackToLauncherRunner; - private IDragAndDrop mDragAndDrop; - private IHomeTransitionListener mHomeTransitionListener; - - // Used to dedupe calls to SystemUI - private int mLastShelfHeight; - private boolean mLastShelfVisible; - - // Used to dedupe calls to SystemUI - private int mLastLauncherKeepClearAreaHeight; - private boolean mLastLauncherKeepClearAreaHeightVisible; - - private final Context mContext; - private final Handler mAsyncHandler; - - // TODO(141886704): Find a way to remove this - private int mLastSystemUiStateFlags; - - /** - * This is a singleton pending intent that is used to start recents via Shell (which is a - * different process). It is bare-bones, so it's expected that the component and options will - * be provided via fill-in intent. - */ - private final PendingIntent mRecentsPendingIntent; - - public SystemUiProxy(Context context) { - mContext = context; - mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync); - final Intent baseIntent = new Intent().setPackage(mContext.getPackageName()); - final ActivityOptions options = ActivityOptions.makeBasic() - .setPendingIntentCreatorBackgroundActivityStartMode( - ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); - mRecentsPendingIntent = PendingIntent.getActivity(mContext, 0, baseIntent, - PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT - | Intent.FILL_IN_COMPONENT, options.toBundle()); - } - - @Override - public void onBackPressed() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onBackPressed(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onBackPressed", e); - } - } - } - - @Override - public void onImeSwitcherPressed() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onImeSwitcherPressed(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onImeSwitcherPressed", e); - } - } - } - - @Override - public void setHomeRotationEnabled(boolean enabled) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.setHomeRotationEnabled(enabled); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onBackPressed", e); - } - } - } - - @Override - public IBinder asBinder() { - // Do nothing - return null; - } - - /** - * Sets proxy state, including death linkage, various listeners, and other configuration objects - */ - @MainThread - public void setProxy(ISystemUiProxy proxy, IPip pip, IBubbles bubbles, ISplitScreen splitScreen, - IOneHanded oneHanded, IShellTransitions shellTransitions, - IStartingWindow startingWindow, IRecentTasks recentTasks, - ISysuiUnlockAnimationController sysuiUnlockAnimationController, - IBackAnimation backAnimation, IDesktopMode desktopMode, - IUnfoldAnimation unfoldAnimation, IDragAndDrop dragAndDrop) { - Preconditions.assertUIThread(); - unlinkToDeath(); - mSystemUiProxy = proxy; - mPip = pip; - mBubbles = bubbles; - mSplitScreen = splitScreen; - mOneHanded = oneHanded; - mShellTransitions = shellTransitions; - mStartingWindow = startingWindow; - mSysuiUnlockAnimationController = sysuiUnlockAnimationController; - mRecentTasks = recentTasks; - mBackAnimation = backAnimation; - mDesktopMode = desktopMode; - mUnfoldAnimation = unfoldAnimation; - mDragAndDrop = dragAndDrop; - linkToDeath(); - // re-attach the listeners once missing due to setProxy has not been initialized yet. - setPipAnimationListener(mPipAnimationListener); - setBubblesListener(mBubblesListener); - registerSplitScreenListener(mSplitScreenListener); - registerSplitSelectListener(mSplitSelectListener); - setHomeTransitionListener(mHomeTransitionListener); - setStartingWindowListener(mStartingWindowListener); - setLauncherUnlockAnimationController( - mLauncherActivityClass, mLauncherUnlockAnimationController); - new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition); - setupTransactionQueue(); - registerRecentTasksListener(mRecentTasksListener); - setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner); - setUnfoldAnimationListener(mUnfoldAnimationListener); - setDesktopTaskListener(mDesktopTaskListener); - setAssistantOverridesRequested( - AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes()); - } - - /** - * Clear the proxy to release held resources and turn the majority of its operations into no-ops - */ - @MainThread - public void clearProxy() { - setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null); - } - - // TODO(141886704): Find a way to remove this - public void setLastSystemUiStateFlags(int stateFlags) { - mLastSystemUiStateFlags = stateFlags; - } - - // TODO(141886704): Find a way to remove this - public int getLastSystemUiStateFlags() { - return mLastSystemUiStateFlags; - } - - public boolean isActive() { - return mSystemUiProxy != null; - } - - private void linkToDeath() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.asBinder().linkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */); - } catch (RemoteException e) { - Log.e(TAG, "Failed to link sysui proxy death recipient"); - } - } - } - - private void unlinkToDeath() { - if (mSystemUiProxy != null) { - mSystemUiProxy.asBinder().unlinkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */); - } - } - - @Override - public void startScreenPinning(int taskId) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.startScreenPinning(taskId); - } catch (RemoteException e) { - Log.w(TAG, "Failed call startScreenPinning", e); - } - } - } - - @Override - public void onOverviewShown(boolean fromHome) { - onOverviewShown(fromHome, TAG); - } - - public void onOverviewShown(boolean fromHome, String tag) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onOverviewShown(fromHome); - } catch (RemoteException e) { - Log.w(tag, "Failed call onOverviewShown from: " + (fromHome ? "home" : "app"), e); - } - } - } - - @MainThread - @Override - public void onStatusBarTouchEvent(MotionEvent event) { - Preconditions.assertUIThread(); - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onStatusBarTouchEvent(event); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onStatusBarTouchEvent with arg: " + event, e); - } - } - } - - @Override - public void onStatusBarTrackpadEvent(MotionEvent event) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onStatusBarTrackpadEvent(event); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onStatusBarTrackpadEvent with arg: " + event, e); - } - } - } - - @Override - public void onAssistantProgress(float progress) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onAssistantProgress(progress); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onAssistantProgress with progress: " + progress, e); - } - } - } - - @Override - public void onAssistantGestureCompletion(float velocity) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.onAssistantGestureCompletion(velocity); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onAssistantGestureCompletion", e); - } - } - } - - @Override - public void startAssistant(Bundle args) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.startAssistant(args); - } catch (RemoteException e) { - Log.w(TAG, "Failed call startAssistant", e); - } - } - } - - @Override - public void setAssistantOverridesRequested(int[] invocationTypes) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.setAssistantOverridesRequested(invocationTypes); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setAssistantOverridesRequested", e); - } - } - } - - @Override - public void animateNavBarLongPress(boolean isTouchDown, long durationMs) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.animateNavBarLongPress(isTouchDown, durationMs); - } catch (RemoteException e) { - Log.w(TAG, "Failed call animateNavBarLongPress", e); - } - } - } - - @Override - public void notifyAccessibilityButtonClicked(int displayId) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.notifyAccessibilityButtonClicked(displayId); - } catch (RemoteException e) { - Log.w(TAG, "Failed call notifyAccessibilityButtonClicked", e); - } - } - } - - @Override - public void notifyAccessibilityButtonLongClicked() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.notifyAccessibilityButtonLongClicked(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call notifyAccessibilityButtonLongClicked", e); - } - } - } - - @Override - public void stopScreenPinning() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.stopScreenPinning(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call stopScreenPinning", e); - } - } - } - - @Override - public void notifyPrioritizedRotation(int rotation) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.notifyPrioritizedRotation(rotation); - } catch (RemoteException e) { - Log.w(TAG, "Failed call notifyPrioritizedRotation with arg: " + rotation, e); - } - } - } - - @Override - public void notifyTaskbarStatus(boolean visible, boolean stashed) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.notifyTaskbarStatus(visible, stashed); - } catch (RemoteException e) { - Log.w(TAG, "Failed call notifyTaskbarStatus with arg: " + - visible + ", " + stashed, e); - } - } - } - - /** - * NOTE: If called to suspend, caller MUST call this method to also un-suspend - * @param suspend should be true to stop auto-hide, false to resume normal behavior - */ - @Override - public void notifyTaskbarAutohideSuspend(boolean suspend) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.notifyTaskbarAutohideSuspend(suspend); - } catch (RemoteException e) { - Log.w(TAG, "Failed call notifyTaskbarAutohideSuspend with arg: " + - suspend, e); - } - } - } - - @Override - public void takeScreenshot(ScreenshotRequest request) { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.takeScreenshot(request); - } catch (RemoteException e) { - Log.w(TAG, "Failed call takeScreenshot"); - } - } - } - - @Override - public void expandNotificationPanel() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.expandNotificationPanel(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call expandNotificationPanel", e); - } - } - } - - @Override - public void toggleNotificationPanel() { - if (mSystemUiProxy != null) { - try { - mSystemUiProxy.toggleNotificationPanel(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call toggleNotificationPanel", e); - } - } - } - - // - // Pip - // - - /** - * Sets the shelf height. - */ - public void setShelfHeight(boolean visible, int shelfHeight) { - Message.obtain(mAsyncHandler, MSG_SET_SHELF_HEIGHT, - visible ? 1 : 0 , shelfHeight).sendToTarget(); - } - - @WorkerThread - private void setShelfHeightAsync(int visibleInt, int shelfHeight) { - boolean visible = visibleInt != 0; - boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight; - IPip pip = mPip; - if (pip != null && changed) { - mLastShelfVisible = visible; - mLastShelfHeight = shelfHeight; - try { - pip.setShelfHeight(visible, shelfHeight); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setShelfHeight visible: " + visible - + " height: " + shelfHeight, e); - } - } - } - - /** - * Sets the height of the keep clear area that is going to be reported by - * the Launcher for the Hotseat. - */ - public void setLauncherKeepClearAreaHeight(boolean visible, int height) { - Message.obtain(mAsyncHandler, MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT, - visible ? 1 : 0 , height).sendToTarget(); - } - - @WorkerThread - private void setLauncherKeepClearAreaHeight(int visibleInt, int height) { - boolean visible = visibleInt != 0; - boolean changed = visible != mLastLauncherKeepClearAreaHeightVisible - || height != mLastLauncherKeepClearAreaHeight; - IPip pip = mPip; - if (pip != null && changed) { - mLastLauncherKeepClearAreaHeightVisible = visible; - mLastLauncherKeepClearAreaHeight = height; - try { - pip.setLauncherKeepClearAreaHeight(visible, height); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setLauncherKeepClearAreaHeight visible: " + visible - + " height: " + height, e); - } - } - } - - /** - * Sets listener to get pip animation callbacks. - */ - public void setPipAnimationListener(IPipAnimationListener listener) { - if (mPip != null) { - try { - mPip.setPipAnimationListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setPinnedStackAnimationListener", e); - } - } - mPipAnimationListener = listener; - } - - /** - * @return Destination bounds of auto-pip animation, {@code null} if the animation is not ready. - */ - @Nullable - public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, - PictureInPictureParams pictureInPictureParams, int launcherRotation, - Rect hotseatKeepClearArea) { - if (mPip != null) { - try { - return mPip.startSwipePipToHome(componentName, activityInfo, - pictureInPictureParams, launcherRotation, hotseatKeepClearArea); - } catch (RemoteException e) { - Log.w(TAG, "Failed call startSwipePipToHome", e); - } - } - return null; - } - - /** - * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to - * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell - * should be responsible for cleaning up the overlay. - */ - public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, - SurfaceControl overlay) { - if (mPip != null) { - try { - mPip.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay); - } catch (RemoteException e) { - Log.w(TAG, "Failed call stopSwipePipToHome"); - } - } - } - - /** - * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell - * can use this callback to clean up its internal states. - */ - public void abortSwipePipToHome(int taskId, ComponentName componentName) { - if (mPip != null) { - try { - mPip.abortSwipePipToHome(taskId, componentName); - } catch (RemoteException e) { - Log.w(TAG, "Failed call abortSwipePipToHome"); - } - } - } - - /** - * Sets the next pip animation type to be the alpha animation. - */ - public void setPipAnimationTypeToAlpha() { - if (mPip != null) { - try { - mPip.setPipAnimationTypeToAlpha(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setPipAnimationTypeToAlpha", e); - } - } - } - - /** - * Sets the app icon size in pixel used by Launcher all apps. - */ - public void setLauncherAppIconSize(int iconSizePx) { - if (mPip != null) { - try { - mPip.setLauncherAppIconSize(iconSizePx); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setLauncherAppIconSize", e); - } - } - } - - // - // Bubbles - // - - /** - * Sets the listener to be notified of bubble state changes. - */ - public void setBubblesListener(IBubblesListener listener) { - if (mBubbles != null) { - try { - if (mBubblesListener != null) { - // Clear out any previous listener - mBubbles.unregisterBubbleListener(mBubblesListener); - } - if (listener != null) { - mBubbles.registerBubbleListener(listener); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed call registerBubblesListener"); - } - } - mBubblesListener = listener; - } - - /** - * Tells SysUI to show the bubble with the provided key. - * @param key the key of the bubble to show. - * @param bubbleBarOffsetX the offset of the bubble bar from the edge of the screen on the X - * axis. - * @param bubbleBarOffsetY the offset of the bubble bar from the edge of the screen on the Y - * axis. - */ - public void showBubble(String key, int bubbleBarOffsetX, int bubbleBarOffsetY) { - if (mBubbles != null) { - try { - mBubbles.showBubble(key, bubbleBarOffsetX, bubbleBarOffsetY); - } catch (RemoteException e) { - Log.w(TAG, "Failed call showBubble"); - } - } - } - - /** - * Tells SysUI to remove the bubble with the provided key. - * @param key the key of the bubble to show. - */ - public void removeBubble(String key) { - if (mBubbles == null) return; - try { - mBubbles.removeBubble(key); - } catch (RemoteException e) { - Log.w(TAG, "Failed call removeBubble"); - } - } - - /** - * Tells SysUI to remove all bubbles. - */ - public void removeAllBubbles() { - if (mBubbles == null) return; - try { - mBubbles.removeAllBubbles(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call removeAllBubbles"); - } - } - - /** - * Tells SysUI to collapse the bubbles. - */ - public void collapseBubbles() { - if (mBubbles != null) { - try { - mBubbles.collapseBubbles(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call collapseBubbles"); - } - } - } - - /** - * Tells SysUI when the bubble is being dragged. - * Should be called only when the bubble bar is expanded. - * @param bubbleKey the key of the bubble to collapse/expand - * @param isBeingDragged whether the bubble is being dragged - */ - public void onBubbleDrag(@Nullable String bubbleKey, boolean isBeingDragged) { - if (mBubbles == null) return; - try { - mBubbles.onBubbleDrag(bubbleKey, isBeingDragged); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onBubbleDrag"); - } - } - - /** - * Tells SysUI to show user education relative to the reference point provided. - * @param position the bubble bar top center position in Screen coordinates. - */ - public void showUserEducation(Point position) { - try { - mBubbles.showUserEducation(position.x, position.y); - } catch (RemoteException e) { - Log.w(TAG, "Failed call showUserEducation"); - } - } - - // - // Splitscreen - // - - public void registerSplitScreenListener(ISplitScreenListener listener) { - if (mSplitScreen != null) { - try { - mSplitScreen.registerSplitScreenListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call registerSplitScreenListener"); - } - } - mSplitScreenListener = listener; - } - - public void unregisterSplitScreenListener(ISplitScreenListener listener) { - if (mSplitScreen != null) { - try { - mSplitScreen.unregisterSplitScreenListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call unregisterSplitScreenListener"); - } - } - mSplitScreenListener = null; - } - - public void registerSplitSelectListener(ISplitSelectListener listener) { - if (mSplitScreen != null) { - try { - mSplitScreen.registerSplitSelectListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call registerSplitSelectListener"); - } - } - mSplitSelectListener = listener; - } - - public void unregisterSplitSelectListener(ISplitSelectListener listener) { - if (mSplitScreen != null) { - try { - mSplitScreen.unregisterSplitSelectListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call unregisterSplitSelectListener"); - } - } - mSplitSelectListener = null; - } - - /** Start multiple tasks in split-screen simultaneously. */ - public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2, - @StagePosition int splitPosition, @PersistentSnapPosition int snapPosition, - RemoteTransition remoteTransition, InstanceId instanceId) { - if (mSystemUiProxy != null) { - try { - mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition, - snapPosition, remoteTransition, instanceId); - } catch (RemoteException e) { - Log.w(TAG, splitFailureMessage("startTasks", "RemoteException"), e); - } - } - } - - public void startIntentAndTask(PendingIntent pendingIntent, int userId1, Bundle options1, - int taskId, Bundle options2, @StagePosition int splitPosition, - @PersistentSnapPosition int snapPosition, RemoteTransition remoteTransition, - InstanceId instanceId) { - if (mSystemUiProxy != null) { - try { - mSplitScreen.startIntentAndTask(pendingIntent, userId1, options1, taskId, options2, - splitPosition, snapPosition, remoteTransition, instanceId); - } catch (RemoteException e) { - Log.w(TAG, splitFailureMessage("startIntentAndTask", "RemoteException"), e); - } - } - } - - public void startIntents(PendingIntent pendingIntent1, int userId1, - @Nullable ShortcutInfo shortcutInfo1, Bundle options1, PendingIntent pendingIntent2, - int userId2, @Nullable ShortcutInfo shortcutInfo2, Bundle options2, - @StagePosition int splitPosition, @PersistentSnapPosition int snapPosition, - RemoteTransition remoteTransition, InstanceId instanceId) { - if (mSystemUiProxy != null) { - try { - mSplitScreen.startIntents(pendingIntent1, userId1, shortcutInfo1, options1, - pendingIntent2, userId2, shortcutInfo2, options2, splitPosition, - snapPosition, remoteTransition, instanceId); - } catch (RemoteException e) { - Log.w(TAG, splitFailureMessage("startIntents", "RemoteException"), e); - } - } - } - - public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId, - Bundle options2, @StagePosition int splitPosition, - @PersistentSnapPosition int snapPosition, RemoteTransition remoteTransition, - InstanceId instanceId) { - if (mSystemUiProxy != null) { - try { - mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2, - splitPosition, snapPosition, remoteTransition, instanceId); - } catch (RemoteException e) { - Log.w(TAG, splitFailureMessage("startShortcutAndTask", "RemoteException"), e); - } - } - } - - /** - * Start multiple tasks in split-screen simultaneously. - */ - public void startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2, - Bundle options2, @StagePosition int splitPosition, - @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, - InstanceId instanceId) { - if (mSystemUiProxy != null) { - try { - mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2, - splitPosition, snapPosition, adapter, instanceId); - } catch (RemoteException e) { - Log.w(TAG, splitFailureMessage( - "startTasksWithLegacyTransition", "RemoteException"), e); - } - } - } - - public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1, - Bundle options1, int taskId, Bundle options2, @StagePosition int splitPosition, - @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, - InstanceId instanceId) { - if (mSystemUiProxy != null) { - try { - mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, userId1, - options1, taskId, options2, splitPosition, snapPosition, adapter, - instanceId); - } catch (RemoteException e) { - Log.w(TAG, splitFailureMessage( - "startIntentAndTaskWithLegacyTransition", "RemoteException"), e); - } - } - } - - public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, Bundle options1, - int taskId, Bundle options2, @StagePosition int splitPosition, - @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, - InstanceId instanceId) { - if (mSystemUiProxy != null) { - try { - mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, options1, - taskId, options2, splitPosition, snapPosition, adapter, instanceId); - } catch (RemoteException e) { - Log.w(TAG, splitFailureMessage( - "startShortcutAndTaskWithLegacyTransition", "RemoteException"), e); - } - } - } - - /** - * Starts a pair of intents or shortcuts in split-screen using legacy transition. Passing a - * non-null shortcut info means to start the app as a shortcut. - */ - public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1, - @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1, - PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2, - @Nullable Bundle options2, @StagePosition int sidePosition, - @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter, - InstanceId instanceId) { - if (mSystemUiProxy != null) { - try { - mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, userId1, - shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, options2, - sidePosition, snapPosition, adapter, instanceId); - } catch (RemoteException e) { - Log.w(TAG, splitFailureMessage( - "startIntentsWithLegacyTransition", "RemoteException"), e); - } - } - } - - public void startShortcut(String packageName, String shortcutId, int position, - Bundle options, UserHandle user, InstanceId instanceId) { - if (mSplitScreen != null) { - try { - mSplitScreen.startShortcut(packageName, shortcutId, position, options, - user, instanceId); - } catch (RemoteException e) { - Log.w(TAG, splitFailureMessage("startShortcut", "RemoteException"), e); - } - } - } - - public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position, - Bundle options, InstanceId instanceId) { - if (mSplitScreen != null) { - try { - mSplitScreen.startIntent(intent, userId, fillInIntent, position, options, - instanceId); - } catch (RemoteException e) { - Log.w(TAG, splitFailureMessage("startIntent", "RemoteException"), e); - } - } - } - - public void removeFromSideStage(int taskId) { - if (mSplitScreen != null) { - try { - mSplitScreen.removeFromSideStage(taskId); - } catch (RemoteException e) { - Log.w(TAG, "Failed call removeFromSideStage"); - } - } - } - - /** - * Call this when going to recents so that shell can set-up and provide appropriate leashes - * for animation (eg. DividerBar). - * - * @return RemoteAnimationTargets of windows that need to animate but only exist in shell. - */ - @Nullable - public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) { - if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS && mSplitScreen != null) { - try { - return mSplitScreen.onGoingToRecentsLegacy(apps); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onGoingToRecentsLegacy"); - } - } - return null; - } - - @Nullable - public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) { - if (mSplitScreen != null) { - try { - return mSplitScreen.onStartingSplitLegacy(apps); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onStartingSplitLegacy"); - } - } - return null; - } - - // - // One handed - // - - public void startOneHandedMode() { - if (mOneHanded != null) { - try { - mOneHanded.startOneHanded(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call startOneHandedMode", e); - } - } - } - - public void stopOneHandedMode() { - if (mOneHanded != null) { - try { - mOneHanded.stopOneHanded(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call stopOneHandedMode", e); - } - } - } - - // - // Remote transitions - // - - public void registerRemoteTransition( - RemoteTransition remoteTransition, TransitionFilter filter) { - if (mShellTransitions != null) { - try { - mShellTransitions.registerRemote(filter, remoteTransition); - } catch (RemoteException e) { - Log.w(TAG, "Failed call registerRemoteTransition"); - } - } - if (!mRemoteTransitions.containsKey(remoteTransition)) { - mRemoteTransitions.put(remoteTransition, filter); - } - } - - public void unregisterRemoteTransition(RemoteTransition remoteTransition) { - if (mShellTransitions != null) { - try { - mShellTransitions.unregisterRemote(remoteTransition); - } catch (RemoteException e) { - Log.w(TAG, "Failed call registerRemoteTransition"); - } - } - mRemoteTransitions.remove(remoteTransition); - } - - public void setHomeTransitionListener(IHomeTransitionListener listener) { - if (!FeatureFlags.enableHomeTransitionListener()) { - return; - } - - mHomeTransitionListener = listener; - - if (mShellTransitions != null) { - try { - mShellTransitions.setHomeTransitionListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setHomeTransitionListener", e); - } - } else { - Log.w(TAG, "Unable to call setHomeTransitionListener because ShellTransitions is null"); - } - } - - /** - * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary - * if Launcher and SystemUI need to coordinate transactions (eg. for shell transitions). - */ - public void shareTransactionQueue() { - if (mOriginalTransactionToken == null) { - mOriginalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken(); - } - setupTransactionQueue(); - } - - /** - * Switch back to using Launcher's independent transaction queue. - */ - public void unshareTransactionQueue() { - if (mOriginalTransactionToken == null) { - return; - } - SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken); - mOriginalTransactionToken = null; - } - - private void setupTransactionQueue() { - if (mOriginalTransactionToken == null) { - return; - } - if (mShellTransitions == null) { - SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken); - return; - } - final IBinder shellApplyToken; - try { - shellApplyToken = mShellTransitions.getShellApplyToken(); - } catch (RemoteException e) { - Log.e(TAG, "Error getting Shell's apply token", e); - return; - } - if (shellApplyToken == null) { - Log.e(TAG, "Didn't receive apply token from Shell"); - return; - } - SurfaceControl.Transaction.setDefaultApplyToken(shellApplyToken); - } - - // - // Starting window - // - - /** - * Sets listener to get callbacks when launching a task. - */ - public void setStartingWindowListener(IStartingWindowListener listener) { - if (mStartingWindow != null) { - try { - mStartingWindow.setStartingWindowListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setStartingWindowListener", e); - } - } - mStartingWindowListener = listener; - } - - // - // SmartSpace transitions - // - - /** - * Sets the instance of {@link ILauncherUnlockAnimationController} that System UI should use to - * control the launcher side of the unlock animation. This will also cause us to dispatch the - * current state of the smartspace to System UI (this will subsequently happen if the state - * changes). - */ - public void setLauncherUnlockAnimationController( - String activityClass, ILauncherUnlockAnimationController controller) { - if (mSysuiUnlockAnimationController != null) { - try { - mSysuiUnlockAnimationController.setLauncherUnlockController( - activityClass, controller); - if (controller != null) { - controller.dispatchSmartspaceStateToSysui(); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed call setLauncherUnlockAnimationController", e); - } - } - mLauncherActivityClass = activityClass; - mLauncherUnlockAnimationController = controller; - } - - /** - * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare - * the unlock animation accordingly. - */ - public void notifySysuiSmartspaceStateUpdated(SmartspaceState state) { - if (mSysuiUnlockAnimationController != null) { - try { - mSysuiUnlockAnimationController.onLauncherSmartspaceStateUpdated(state); - } catch (RemoteException e) { - Log.w(TAG, "Failed call notifySysuiSmartspaceStateUpdated", e); - e.printStackTrace(); - } - } - } - - // - // Recents - // - - public void registerRecentTasksListener(IRecentTasksListener listener) { - if (mRecentTasks != null) { - try { - mRecentTasks.registerRecentTasksListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call registerRecentTasksListener", e); - } - } - mRecentTasksListener = listener; - } - - public void unregisterRecentTasksListener(IRecentTasksListener listener) { - if (mRecentTasks != null) { - try { - mRecentTasks.unregisterRecentTasksListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call unregisterRecentTasksListener"); - } - } - mRecentTasksListener = null; - } - - // - // Back navigation transitions - // - - /** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */ - public void setBackToLauncherCallback(IOnBackInvokedCallback callback, - IRemoteAnimationRunner runner) { - mBackToLauncherCallback = callback; - mBackToLauncherRunner = runner; - if (mBackAnimation == null || mBackToLauncherCallback == null) { - return; - } - try { - mBackAnimation.setBackToLauncherCallback(callback, runner); - } catch (RemoteException | SecurityException e) { - Log.e(TAG, "Failed call setBackToLauncherCallback", e); - } - } - - /** Clears the previously registered {@link IOnBackInvokedCallback}. - * - * @param callback The previously registered callback instance. - */ - public void clearBackToLauncherCallback(IOnBackInvokedCallback callback) { - if (mBackToLauncherCallback != callback) { - return; - } - mBackToLauncherCallback = null; - mBackToLauncherRunner = null; - if (mBackAnimation == null) { - return; - } - try { - mBackAnimation.clearBackToLauncherCallback(); - } catch (RemoteException e) { - Log.e(TAG, "Failed call clearBackToLauncherCallback", e); - } - } - - /** - * Called when the status bar color needs to be customized when back navigation. - */ - public void customizeStatusBarAppearance(AppearanceRegion appearance) { - if (mBackAnimation == null) { - return; - } - try { - mBackAnimation.customizeStatusBarAppearance(appearance); - } catch (RemoteException e) { - Log.e(TAG, "Failed call useLauncherSysBarFlags", e); - } - } - - public ArrayList getRecentTasks(int numTasks, int userId) { - if (mRecentTasks == null) { - Log.w(TAG, "getRecentTasks() failed due to null mRecentTasks"); - return new ArrayList<>(); - } - try { - final GroupedRecentTaskInfo[] rawTasks = mRecentTasks.getRecentTasks(numTasks, - RECENT_IGNORE_UNAVAILABLE, userId); - if (rawTasks == null) { - return new ArrayList<>(); - } - return new ArrayList<>(Arrays.asList(rawTasks)); - } catch (RemoteException e) { - Log.w(TAG, "Failed call getRecentTasks", e); - return new ArrayList<>(); - } - } - - /** - * Gets the set of running tasks. - */ - public ArrayList getRunningTasks(int numTasks) { - if (mRecentTasks != null - && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { - try { - return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks))); - } catch (RemoteException e) { - Log.w(TAG, "Failed call getRunningTasks", e); - } - } - return new ArrayList<>(); - } - - private boolean handleMessageAsync(Message msg) { - switch (msg.what) { - case MSG_SET_SHELF_HEIGHT: - setShelfHeightAsync(msg.arg1, msg.arg2); - return true; - case MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT: - setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2); - return true; - } - - return false; - } - - // - // Desktop Mode - // - - /** Call shell to show all apps active on the desktop */ - public void showDesktopApps(int displayId, @Nullable RemoteTransition transition) { - if (mDesktopMode != null) { - try { - mDesktopMode.showDesktopApps(displayId, transition); - } catch (RemoteException e) { - Log.w(TAG, "Failed call showDesktopApps", e); - } - } - } - - /** Call shell to stash desktop apps */ - public void stashDesktopApps(int displayId) { - if (mDesktopMode != null) { - try { - mDesktopMode.stashDesktopApps(displayId); - } catch (RemoteException e) { - Log.w(TAG, "Failed call stashDesktopApps", e); - } - } - } - - /** Call shell to hide desktop apps that may be stashed */ - public void hideStashedDesktopApps(int displayId) { - if (mDesktopMode != null) { - try { - mDesktopMode.hideStashedDesktopApps(displayId); - } catch (RemoteException e) { - Log.w(TAG, "Failed call hideStashedDesktopApps", e); - } - } - } - - /** - * If task with the given id is on the desktop, bring it to front - */ - public void showDesktopApp(int taskId) { - if (mDesktopMode != null) { - try { - mDesktopMode.showDesktopApp(taskId); - } catch (RemoteException e) { - Log.w(TAG, "Failed call showDesktopApp", e); - } - } - } - - /** Call shell to get number of visible freeform tasks */ - public int getVisibleDesktopTaskCount(int displayId) { - if (mDesktopMode != null) { - try { - return mDesktopMode.getVisibleTaskCount(displayId); - } catch (RemoteException e) { - Log.w(TAG, "Failed call getVisibleDesktopTaskCount", e); - } - } - return 0; - } - - /** Set a listener on shell to get updates about desktop task state */ - public void setDesktopTaskListener(@Nullable IDesktopTaskListener listener) { - mDesktopTaskListener = listener; - if (mDesktopMode != null) { - try { - mDesktopMode.setTaskListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setDesktopTaskListener", e); - } - } - } - - /** Perform cleanup transactions after animation to split select is complete */ - public void onDesktopSplitSelectAnimComplete(ActivityManager.RunningTaskInfo taskInfo) { - if (mDesktopMode != null) { - try { - mDesktopMode.onDesktopSplitSelectAnimComplete(taskInfo); - } catch (RemoteException e) { - Log.w(TAG, "Failed call onDesktopSplitSelectAnimComplete", e); - } - } - } - - // - // Unfold transition - // - - /** Sets the unfold animation lister to sysui. */ - public void setUnfoldAnimationListener(IUnfoldTransitionListener callback) { - mUnfoldAnimationListener = callback; - if (mUnfoldAnimation == null) { - return; - } - try { - Log.d(TAG, "Registering unfold animation receiver"); - mUnfoldAnimation.setListener(callback); - } catch (RemoteException e) { - Log.e(TAG, "Failed call setUnfoldAnimationListener", e); - } - } - - // - // Recents - // - - /** - * Starts the recents activity. The caller should manage the thread on which this is called. - */ - public boolean startRecentsActivity(Intent intent, ActivityOptions options, - RecentsAnimationListener listener) { - if (mRecentTasks == null) { - ActiveGestureLog.INSTANCE.addLog("Null mRecentTasks", RECENT_TASKS_MISSING); - return false; - } - final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() { - @Override - public void onAnimationStart(IRecentsAnimationController controller, - RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, - Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras) { - // Aidl bundles need to explicitly set class loader - // https://developer.android.com/guide/components/aidl#Bundles - if (extras != null) { - extras.setClassLoader(getClass().getClassLoader()); - } - listener.onAnimationStart(new RecentsAnimationControllerCompat(controller), apps, - wallpapers, homeContentInsets, minimizedHomeBounds, extras); - } - - @Override - public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) { - listener.onAnimationCanceled( - ThumbnailData.wrap(taskIds, taskSnapshots)); - } - - @Override - public void onTasksAppeared(RemoteAnimationTarget[] apps) { - listener.onTasksAppeared(apps); - } - }; - final Bundle optsBundle = options.toBundle(); - try { - mRecentTasks.startRecentsTransition(mRecentsPendingIntent, intent, optsBundle, - mContext.getIApplicationThread(), runner); - return true; - } catch (RemoteException e) { - Log.e(TAG, "Error starting recents via shell", e); - return false; - } - } - - // - // Drag and drop - // - - /** - * For testing purposes. Returns `true` only if the shell drop target has shown and - * drawn and is ready to handle drag events and the subsequent drop. - */ - public boolean isDragAndDropReady() { - if (mDragAndDrop == null) { - return false; - } - try { - return mDragAndDrop.isReadyToHandleDrag(); - } catch (RemoteException e) { - Log.e(TAG, "Error querying drag state", e); - return false; - } - } - - public void dump(PrintWriter pw) { - pw.println(TAG + ":"); - - pw.println("\tmSystemUiProxy=" + mSystemUiProxy); - pw.println("\tmPip=" + mPip); - pw.println("\tmPipAnimationListener=" + mPipAnimationListener); - pw.println("\tmBubbles=" + mBubbles); - pw.println("\tmBubblesListener=" + mBubblesListener); - pw.println("\tmSplitScreen=" + mSplitScreen); - pw.println("\tmSplitScreenListener=" + mSplitScreenListener); - pw.println("\tmSplitSelectListener=" + mSplitSelectListener); - pw.println("\tmOneHanded=" + mOneHanded); - pw.println("\tmShellTransitions=" + mShellTransitions); - pw.println("\tmHomeTransitionListener=" + mHomeTransitionListener); - pw.println("\tmStartingWindow=" + mStartingWindow); - pw.println("\tmStartingWindowListener=" + mStartingWindowListener); - pw.println("\tmSysuiUnlockAnimationController=" + mSysuiUnlockAnimationController); - pw.println("\tmLauncherActivityClass=" + mLauncherActivityClass); - pw.println("\tmLauncherUnlockAnimationController=" + mLauncherUnlockAnimationController); - pw.println("\tmRecentTasks=" + mRecentTasks); - pw.println("\tmRecentTasksListener=" + mRecentTasksListener); - pw.println("\tmBackAnimation=" + mBackAnimation); - pw.println("\tmBackToLauncherCallback=" + mBackToLauncherCallback); - pw.println("\tmBackToLauncherRunner=" + mBackToLauncherRunner); - pw.println("\tmDesktopMode=" + mDesktopMode); - pw.println("\tmDesktopTaskListener=" + mDesktopTaskListener); - pw.println("\tmUnfoldAnimation=" + mUnfoldAnimation); - pw.println("\tmUnfoldAnimationListener=" + mUnfoldAnimationListener); - pw.println("\tmDragAndDrop=" + mDragAndDrop); - } -} diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt new file mode 100644 index 0000000000..6184785218 --- /dev/null +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt @@ -0,0 +1,1232 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep + +import android.app.ActivityManager +import android.app.ActivityManager.RunningTaskInfo +import android.app.ActivityOptions +import android.app.PendingIntent +import android.app.PictureInPictureParams +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.PackageManager +import android.content.pm.ShortcutInfo +import android.graphics.Point +import android.graphics.Rect +import android.os.Bundle +import android.os.Handler +import android.os.IBinder +import android.os.Message +import android.os.RemoteException +import android.os.UserHandle +import android.util.Log +import android.view.IRecentsAnimationController +import android.view.IRecentsAnimationRunner +import android.view.IRemoteAnimationRunner +import android.view.MotionEvent +import android.view.RemoteAnimationAdapter +import android.view.RemoteAnimationTarget +import android.view.SurfaceControl +import android.window.IOnBackInvokedCallback +import android.window.RemoteTransition +import android.window.TaskSnapshot +import android.window.TransitionFilter +import androidx.annotation.MainThread +import androidx.annotation.WorkerThread +import com.android.internal.logging.InstanceId +import com.android.internal.util.ScreenshotRequest +import com.android.internal.view.AppearanceRegion +import com.android.launcher3.config.FeatureFlags +import com.android.launcher3.util.Executors +import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR +import com.android.launcher3.util.MainThreadInitializedObject +import com.android.launcher3.util.Preconditions +import com.android.launcher3.util.SplitConfigurationOptions.StagePosition +import com.android.quickstep.util.ActiveGestureErrorDetector +import com.android.quickstep.util.ActiveGestureLog +import com.android.quickstep.util.AssistUtils +import com.android.quickstep.util.LogUtils.splitFailureMessage +import com.android.systemui.shared.recents.ISystemUiProxy +import com.android.systemui.shared.recents.model.ThumbnailData +import com.android.systemui.shared.system.RecentsAnimationControllerCompat +import com.android.systemui.shared.system.RecentsAnimationListener +import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController +import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController +import com.android.systemui.shared.system.smartspace.SmartspaceState +import com.android.systemui.unfold.progress.IUnfoldAnimation +import com.android.systemui.unfold.progress.IUnfoldTransitionListener +import com.android.wm.shell.back.IBackAnimation +import com.android.wm.shell.bubbles.IBubbles +import com.android.wm.shell.bubbles.IBubblesListener +import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition +import com.android.wm.shell.desktopmode.IDesktopMode +import com.android.wm.shell.desktopmode.IDesktopTaskListener +import com.android.wm.shell.draganddrop.IDragAndDrop +import com.android.wm.shell.onehanded.IOneHanded +import com.android.wm.shell.pip.IPip +import com.android.wm.shell.pip.IPipAnimationListener +import com.android.wm.shell.recents.IRecentTasks +import com.android.wm.shell.recents.IRecentTasksListener +import com.android.wm.shell.splitscreen.ISplitScreen +import com.android.wm.shell.splitscreen.ISplitScreenListener +import com.android.wm.shell.splitscreen.ISplitSelectListener +import com.android.wm.shell.startingsurface.IStartingWindow +import com.android.wm.shell.startingsurface.IStartingWindowListener +import com.android.wm.shell.transition.IHomeTransitionListener +import com.android.wm.shell.transition.IShellTransitions +import com.android.wm.shell.util.GroupedRecentTaskInfo +import java.io.PrintWriter + +/** Holds the reference to SystemUI. */ +class SystemUiProxy(private val context: Context) { + + private val asyncHandler = Handler(UI_HELPER_EXECUTOR.looper, this::handleMessageAsync) + + /** + * This is a singleton pending intent that is used to start recents via Shell (which is a + * different process). It is bare-bones, so it's expected that the component and options will be + * provided via fill-in intent. + */ + private val recentsPendingIntent = + PendingIntent.getActivity( + context, + 0, + Intent().setPackage(context.packageName), + PendingIntent.FLAG_MUTABLE or + PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or + Intent.FILL_IN_COMPONENT, + ActivityOptions.makeBasic() + .setPendingIntentCreatorBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED + ) + .toBundle() + ) + + private val systemUiProxyDeathRecipient = + IBinder.DeathRecipient { Executors.MAIN_EXECUTOR.execute { clearProxy() } } + + private var systemUiProxy: ISystemUiProxy? = null + + private var pip: IPip? = null + private var bubbles: IBubbles? = null + private var sysuiUnlockAnimationController: ISysuiUnlockAnimationController? = null + private var splitScreen: ISplitScreen? = null + private var oneHanded: IOneHanded? = null + private var shellTransitions: IShellTransitions? = null + private var startingWindow: IStartingWindow? = null + private var recentTasks: IRecentTasks? = null + private var backAnimation: IBackAnimation? = null + private var desktopMode: IDesktopMode? = null + private var unfoldAnimation: IUnfoldAnimation? = null + + // Save the listeners passed into the proxy since OverviewProxyService may not have been bound + // yet, and we'll need to set/register these listeners with SysUI when they do. Note that it is + // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely + // in case SysUI needs to rebind. + private var pipAnimationListener: IPipAnimationListener? = null + private var bubblesListener: IBubblesListener? = null + private var splitScreenListener: ISplitScreenListener? = null + private var splitSelectListener: ISplitSelectListener? = null + private var startingWindowListener: IStartingWindowListener? = null + private var launcherUnlockAnimationController: ILauncherUnlockAnimationController? = null + private var launcherActivityClass: String? = null + private var recentTasksListener: IRecentTasksListener? = null + private var unfoldAnimationListener: IUnfoldTransitionListener? = null + private var desktopTaskListener: IDesktopTaskListener? = null + private val remoteTransitions = LinkedHashMap() + private var originalTransactionToken: IBinder? = null + private var backToLauncherCallback: IOnBackInvokedCallback? = null + private var backToLauncherRunner: IRemoteAnimationRunner? = null + private var dragAndDrop: IDragAndDrop? = null + private var homeTransitionListener: IHomeTransitionListener? = null + + // Used to dedupe calls to SystemUI + private var lastShelfHeight = 0 + private var lastShelfVisible = false + + // Used to dedupe calls to SystemUI + private var lastLauncherKeepClearAreaHeight = 0 + private var lastLauncherKeepClearAreaHeightVisible = false + + // TODO(141886704): Find a way to remove this + var lastSystemUiStateFlags = 0 + + /** + * Sets proxy state, including death linkage, various listeners, and other configuration objects + */ + @MainThread + fun setProxy( + proxy: ISystemUiProxy?, + pip: IPip?, + bubbles: IBubbles?, + splitScreen: ISplitScreen?, + oneHanded: IOneHanded?, + shellTransitions: IShellTransitions?, + startingWindow: IStartingWindow?, + recentTasks: IRecentTasks?, + sysuiUnlockAnimationController: ISysuiUnlockAnimationController?, + backAnimation: IBackAnimation?, + desktopMode: IDesktopMode?, + unfoldAnimation: IUnfoldAnimation?, + dragAndDrop: IDragAndDrop? + ) { + Preconditions.assertUIThread() + unlinkToDeath() + systemUiProxy = proxy + this.pip = pip + this.bubbles = bubbles + this.splitScreen = splitScreen + this.oneHanded = oneHanded + this.shellTransitions = shellTransitions + this.startingWindow = startingWindow + this.sysuiUnlockAnimationController = sysuiUnlockAnimationController + this.recentTasks = recentTasks + this.backAnimation = backAnimation + this.desktopMode = desktopMode + this.unfoldAnimation = unfoldAnimation + this.dragAndDrop = dragAndDrop + linkToDeath() + // re-attach the listeners once missing due to setProxy has not been initialized yet. + setPipAnimationListener(pipAnimationListener) + setBubblesListener(bubblesListener) + registerSplitScreenListener(splitScreenListener) + registerSplitSelectListener(splitSelectListener) + setHomeTransitionListener(homeTransitionListener) + setStartingWindowListener(startingWindowListener) + setLauncherUnlockAnimationController( + launcherActivityClass, + launcherUnlockAnimationController + ) + LinkedHashMap(remoteTransitions).forEach(this::registerRemoteTransition) + setupTransactionQueue() + registerRecentTasksListener(recentTasksListener) + setBackToLauncherCallback(backToLauncherCallback, backToLauncherRunner) + setUnfoldAnimationListener(unfoldAnimationListener) + setDesktopTaskListener(desktopTaskListener) + setAssistantOverridesRequested( + AssistUtils.newInstance(context).sysUiAssistOverrideInvocationTypes + ) + } + + /** + * Clear the proxy to release held resources and turn the majority of its operations into no-ops + */ + @MainThread + fun clearProxy() { + setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null) + } + + val isActive: Boolean + get() = systemUiProxy != null + + private fun linkToDeath() { + tryOrLog("Failed to link sysui proxy death recipient") { + systemUiProxy?.asBinder()?.linkToDeath(systemUiProxyDeathRecipient, 0 /* flags */) + } + } + + private fun unlinkToDeath() { + systemUiProxy?.asBinder()?.unlinkToDeath(systemUiProxyDeathRecipient, 0 /* flags */) + } + + fun onBackPressed() { + tryOrLog("Failed call onBackPressed") { systemUiProxy?.onBackPressed() } + } + + fun onImeSwitcherPressed() { + tryOrLog("Failed call onImeSwitcherPressed") { systemUiProxy?.onImeSwitcherPressed() } + } + + fun setHomeRotationEnabled(enabled: Boolean) { + tryOrLog("Failed call setHomeRotationEnabled") { + systemUiProxy?.setHomeRotationEnabled(enabled) + } + } + + fun startScreenPinning(taskId: Int) { + tryOrLog("Failed call startScreenPinning") { systemUiProxy?.startScreenPinning(taskId) } + } + + fun onOverviewShown(fromHome: Boolean, tag: String?) { + try { + systemUiProxy?.onOverviewShown(fromHome) + } catch (e: RemoteException) { + Log.w(tag, "Failed call onOverviewShown from: ${if (fromHome) "home" else "app"}", e) + } + } + + @MainThread + fun onStatusBarTouchEvent(event: MotionEvent) { + Preconditions.assertUIThread() + tryOrLog("Failed call onStatusBarTouchEvent with arg: $event") { + systemUiProxy?.onStatusBarTouchEvent(event) + } + } + + fun onStatusBarTrackpadEvent(event: MotionEvent) { + tryOrLog("Failed call onStatusBarTrackpadEvent with arg: $event") { + systemUiProxy?.onStatusBarTrackpadEvent(event) + } + } + + fun onAssistantProgress(progress: Float) { + tryOrLog("Failed call onAssistantProgress with progress: $progress") { + systemUiProxy?.onAssistantProgress(progress) + } + } + + fun onAssistantGestureCompletion(velocity: Float) { + tryOrLog("Failed call onAssistantGestureCompletion") { + systemUiProxy?.onAssistantGestureCompletion(velocity) + } + } + + fun startAssistant(args: Bundle?) { + tryOrLog("Failed call startAssistant") { systemUiProxy?.startAssistant(args) } + } + + fun setAssistantOverridesRequested(invocationTypes: IntArray?) { + tryOrLog("Failed call setAssistantOverridesRequested") { + systemUiProxy?.setAssistantOverridesRequested(invocationTypes) + } + } + + fun animateNavBarLongPress(isTouchDown: Boolean, durationMs: Long) { + tryOrLog("Failed call animateNavBarLongPress") { + systemUiProxy?.animateNavBarLongPress(isTouchDown, durationMs) + } + } + + fun notifyAccessibilityButtonClicked(displayId: Int) { + tryOrLog("Failed call notifyAccessibilityButtonClicked") { + systemUiProxy?.notifyAccessibilityButtonClicked(displayId) + } + } + + fun notifyAccessibilityButtonLongClicked() { + tryOrLog("Failed call notifyAccessibilityButtonLongClicked") { + systemUiProxy?.notifyAccessibilityButtonLongClicked() + } + } + + fun stopScreenPinning() { + tryOrLog("Failed call stopScreenPinning") { systemUiProxy?.stopScreenPinning() } + } + + fun notifyPrioritizedRotation(rotation: Int) { + tryOrLog("Failed call notifyPrioritizedRotation with arg: $rotation") { + systemUiProxy?.notifyPrioritizedRotation(rotation) + } + } + + fun notifyTaskbarStatus(visible: Boolean, stashed: Boolean) { + tryOrLog("Failed call notifyTaskbarStatus with arg: $visible, $stashed") { + systemUiProxy?.notifyTaskbarStatus(visible, stashed) + } + } + + /** + * NOTE: If called to suspend, caller MUST call this method to also un-suspend + * + * @param suspend should be true to stop auto-hide, false to resume normal behavior + */ + fun notifyTaskbarAutohideSuspend(suspend: Boolean) { + tryOrLog("Failed call notifyTaskbarAutohideSuspend with arg: $suspend") { + systemUiProxy?.notifyTaskbarAutohideSuspend(suspend) + } + } + + fun takeScreenshot(request: ScreenshotRequest?) { + tryOrLog("Failed call takeScreenshot") { systemUiProxy?.takeScreenshot(request) } + } + + fun expandNotificationPanel() { + tryOrLog("Failed call expandNotificationPanel") { systemUiProxy?.expandNotificationPanel() } + } + + fun toggleNotificationPanel() { + tryOrLog("Failed call toggleNotificationPanel") { systemUiProxy?.toggleNotificationPanel() } + } + + // + // Pip + // + /** Sets the shelf height. */ + fun setShelfHeight(visible: Boolean, shelfHeight: Int) { + Message.obtain(asyncHandler, MSG_SET_SHELF_HEIGHT, if (visible) 1 else 0, shelfHeight) + .sendToTarget() + } + + @WorkerThread + private fun setShelfHeightAsync(visibleInt: Int, shelfHeight: Int) { + val visible = visibleInt != 0 + if (visible == lastShelfVisible && shelfHeight == lastShelfHeight) return + + pip?.let { + tryOrLog("Failed call setShelfHeight visible: $visible height: $shelfHeight") { + it.setShelfHeight(visible, shelfHeight) + lastShelfVisible = visible + lastShelfHeight = shelfHeight + } + } + } + + /** + * Sets the height of the keep clear area that is going to be reported by the Launcher for the + * Hotseat. + */ + fun setLauncherKeepClearAreaHeight(visible: Boolean, height: Int) { + Message.obtain( + asyncHandler, + MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT, + if (visible) 1 else 0, + height + ) + .sendToTarget() + } + + @WorkerThread + private fun setLauncherKeepClearAreaHeight(visibleInt: Int, height: Int) { + val visible = visibleInt != 0 + if ( + visible == lastLauncherKeepClearAreaHeightVisible && + height == lastLauncherKeepClearAreaHeight + ) + return + + pip?.let { + tryOrLog("Failed call setLauncherKeepClearAreaHeight vis: $visible height: $height") { + it.setLauncherKeepClearAreaHeight(visible, height) + lastLauncherKeepClearAreaHeightVisible = visible + lastLauncherKeepClearAreaHeight = height + } + } + } + + /** Sets listener to get pip animation callbacks. */ + fun setPipAnimationListener(listener: IPipAnimationListener?) { + tryOrLog("Failed call setPinnedStackAnimationListener") { + pip?.setPipAnimationListener(listener) + } + pipAnimationListener = listener + } + + /** @return Destination bounds of auto-pip animation, `null` if the animation is not ready. */ + fun startSwipePipToHome( + componentName: ComponentName?, + activityInfo: ActivityInfo?, + pictureInPictureParams: PictureInPictureParams?, + launcherRotation: Int, + hotseatKeepClearArea: Rect? + ): Rect? { + return tryOrElse("Failed call startSwipePipToHome", null) { + return pip?.startSwipePipToHome( + componentName, + activityInfo, + pictureInPictureParams, + launcherRotation, + hotseatKeepClearArea + ) + } + } + + /** + * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to + * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell + * should be responsible for cleaning up the overlay. + */ + fun stopSwipePipToHome( + taskId: Int, + componentName: ComponentName?, + destinationBounds: Rect?, + overlay: SurfaceControl? + ) { + tryOrLog("Failed call stopSwipePipToHome") { + pip?.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay) + } + } + + /** + * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell can + * use this callback to clean up its internal states. + */ + fun abortSwipePipToHome(taskId: Int, componentName: ComponentName?) { + tryOrLog("Failed call abortSwipePipToHome") { + pip?.abortSwipePipToHome(taskId, componentName) + } + } + + /** Sets the next pip animation type to be the alpha animation. */ + fun setPipAnimationTypeToAlpha() { + tryOrLog("Failed call setPipAnimationTypeToAlpha") { pip?.setPipAnimationTypeToAlpha() } + } + + /** Sets the app icon size in pixel used by Launcher all apps. */ + fun setLauncherAppIconSize(iconSizePx: Int) { + tryOrLog("Failed call setLauncherAppIconSize") { pip?.setLauncherAppIconSize(iconSizePx) } + } + + // + // Bubbles + // + /** Sets the listener to be notified of bubble state changes. */ + fun setBubblesListener(listener: IBubblesListener?) { + bubbles?.let { + tryOrLog("Failed call setLauncherAppIconSize") { + bubblesListener?.let(it::unregisterBubbleListener) + listener?.let(it::registerBubbleListener) + } + } + bubblesListener = listener + } + + /** + * Tells SysUI to show the bubble with the provided key. + * + * @param key the key of the bubble to show. + * @param bubbleBarOffsetX the offset of the bubble bar from the edge of the screen on the X + * axis. + * @param bubbleBarOffsetY the offset of the bubble bar from the edge of the screen on the Y + * axis. + */ + fun showBubble(key: String?, bubbleBarOffsetX: Int, bubbleBarOffsetY: Int) { + tryOrLog("Failed call showBubble") { + bubbles?.showBubble(key, bubbleBarOffsetX, bubbleBarOffsetY) + } + } + + /** + * Tells SysUI to remove the bubble with the provided key. + * + * @param key the key of the bubble to show. + */ + fun removeBubble(key: String?) { + tryOrLog("Failed call removeBubble") { bubbles?.removeBubble(key) } + } + + /** Tells SysUI to remove all bubbles. */ + fun removeAllBubbles() { + tryOrLog("Failed call removeAllBubbles") { bubbles?.removeAllBubbles() } + } + + /** Tells SysUI to collapse the bubbles. */ + fun collapseBubbles() { + tryOrLog("Failed call collapseBubbles") { bubbles?.collapseBubbles() } + } + + /** + * Tells SysUI when the bubble is being dragged. Should be called only when the bubble bar is + * expanded. + * + * @param bubbleKey the key of the bubble to collapse/expand + * @param isBeingDragged whether the bubble is being dragged + */ + fun onBubbleDrag(bubbleKey: String?, isBeingDragged: Boolean) { + tryOrLog("Failed call onBubbleDrag") { bubbles?.onBubbleDrag(bubbleKey, isBeingDragged) } + } + + /** + * Tells SysUI to show user education relative to the reference point provided. + * + * @param position the bubble bar top center position in Screen coordinates. + */ + fun showUserEducation(position: Point) { + tryOrLog("Failed call showUserEducation") { + bubbles?.showUserEducation(position.x, position.y) + } + } + + // + // Splitscreen + // + fun registerSplitScreenListener(listener: ISplitScreenListener?) { + tryOrLog("Failed call registerSplitScreenListener") { + splitScreen?.registerSplitScreenListener(listener) + } + splitScreenListener = listener + } + + fun registerSplitSelectListener(listener: ISplitSelectListener?) { + tryOrLog("Failed call registerSplitScreenListener") { + splitScreen?.registerSplitSelectListener(listener) + } + splitSelectListener = listener + } + + /** Start multiple tasks in split-screen simultaneously. */ + fun startTasks( + taskId1: Int, + options1: Bundle?, + taskId2: Int, + options2: Bundle?, + @StagePosition splitPosition: Int, + @PersistentSnapPosition snapPosition: Int, + remoteTransition: RemoteTransition?, + instanceId: InstanceId? + ) { + tryOrLog(splitFailureMessage("startTasks", "RemoteException")) { + splitScreen?.startTasks( + taskId1, + options1, + taskId2, + options2, + splitPosition, + snapPosition, + remoteTransition, + instanceId + ) + } + } + + fun startIntentAndTask( + pendingIntent: PendingIntent?, + userId1: Int, + options1: Bundle?, + taskId: Int, + options2: Bundle?, + @StagePosition splitPosition: Int, + @PersistentSnapPosition snapPosition: Int, + remoteTransition: RemoteTransition?, + instanceId: InstanceId? + ) { + tryOrLog(splitFailureMessage("startIntentAndTask", "RemoteException")) { + splitScreen?.startIntentAndTask( + pendingIntent, + userId1, + options1, + taskId, + options2, + splitPosition, + snapPosition, + remoteTransition, + instanceId + ) + } + } + + fun startIntents( + pendingIntent1: PendingIntent?, + userId1: Int, + shortcutInfo1: ShortcutInfo?, + options1: Bundle?, + pendingIntent2: PendingIntent?, + userId2: Int, + shortcutInfo2: ShortcutInfo?, + options2: Bundle?, + @StagePosition splitPosition: Int, + @PersistentSnapPosition snapPosition: Int, + remoteTransition: RemoteTransition?, + instanceId: InstanceId? + ) { + tryOrLog(splitFailureMessage("startIntents", "RemoteException")) { + splitScreen?.startIntents( + pendingIntent1, + userId1, + shortcutInfo1, + options1, + pendingIntent2, + userId2, + shortcutInfo2, + options2, + splitPosition, + snapPosition, + remoteTransition, + instanceId + ) + } + } + + fun startShortcutAndTask( + shortcutInfo: ShortcutInfo?, + options1: Bundle?, + taskId: Int, + options2: Bundle?, + @StagePosition splitPosition: Int, + @PersistentSnapPosition snapPosition: Int, + remoteTransition: RemoteTransition?, + instanceId: InstanceId? + ) { + tryOrLog(splitFailureMessage("startShortcutAndTask", "RemoteException")) { + splitScreen?.startShortcutAndTask( + shortcutInfo, + options1, + taskId, + options2, + splitPosition, + snapPosition, + remoteTransition, + instanceId + ) + } + } + + /** Start multiple tasks in split-screen simultaneously. */ + fun startTasksWithLegacyTransition( + taskId1: Int, + options1: Bundle?, + taskId2: Int, + options2: Bundle?, + @StagePosition splitPosition: Int, + @PersistentSnapPosition snapPosition: Int, + adapter: RemoteAnimationAdapter?, + instanceId: InstanceId? + ) { + tryOrLog(splitFailureMessage("startTasksWithLegacyTransition", "RemoteException")) { + splitScreen?.startTasksWithLegacyTransition( + taskId1, + options1, + taskId2, + options2, + splitPosition, + snapPosition, + adapter, + instanceId + ) + } + } + + fun startIntentAndTaskWithLegacyTransition( + pendingIntent: PendingIntent?, + userId1: Int, + options1: Bundle?, + taskId: Int, + options2: Bundle?, + @StagePosition splitPosition: Int, + @PersistentSnapPosition snapPosition: Int, + adapter: RemoteAnimationAdapter?, + instanceId: InstanceId? + ) { + tryOrLog(splitFailureMessage("startIntentAndTaskWithLegacyTransition", "RemoteException")) { + splitScreen?.startIntentAndTaskWithLegacyTransition( + pendingIntent, + userId1, + options1, + taskId, + options2, + splitPosition, + snapPosition, + adapter, + instanceId + ) + } + } + + fun startShortcutAndTaskWithLegacyTransition( + shortcutInfo: ShortcutInfo?, + options1: Bundle?, + taskId: Int, + options2: Bundle?, + @StagePosition splitPosition: Int, + @PersistentSnapPosition snapPosition: Int, + adapter: RemoteAnimationAdapter?, + instanceId: InstanceId? + ) { + tryOrLog( + splitFailureMessage("startShortcutAndTaskWithLegacyTransition", "RemoteException") + ) { + splitScreen?.startShortcutAndTaskWithLegacyTransition( + shortcutInfo, + options1, + taskId, + options2, + splitPosition, + snapPosition, + adapter, + instanceId + ) + } + } + + /** + * Starts a pair of intents or shortcuts in split-screen using legacy transition. Passing a + * non-null shortcut info means to start the app as a shortcut. + */ + fun startIntentsWithLegacyTransition( + pendingIntent1: PendingIntent?, + userId1: Int, + shortcutInfo1: ShortcutInfo?, + options1: Bundle?, + pendingIntent2: PendingIntent?, + userId2: Int, + shortcutInfo2: ShortcutInfo?, + options2: Bundle?, + @StagePosition sidePosition: Int, + @PersistentSnapPosition snapPosition: Int, + adapter: RemoteAnimationAdapter?, + instanceId: InstanceId? + ) { + tryOrLog(splitFailureMessage("startIntentsWithLegacyTransition", "RemoteException")) { + splitScreen?.startIntentsWithLegacyTransition( + pendingIntent1, + userId1, + shortcutInfo1, + options1, + pendingIntent2, + userId2, + shortcutInfo2, + options2, + sidePosition, + snapPosition, + adapter, + instanceId + ) + } + } + + fun startShortcut( + packageName: String?, + shortcutId: String?, + position: Int, + options: Bundle?, + user: UserHandle?, + instanceId: InstanceId? + ) { + tryOrLog(splitFailureMessage("startShortcut", "RemoteException")) { + splitScreen?.startShortcut(packageName, shortcutId, position, options, user, instanceId) + } + } + + fun startIntent( + intent: PendingIntent?, + userId: Int, + fillInIntent: Intent?, + position: Int, + options: Bundle?, + instanceId: InstanceId? + ) { + tryOrLog(splitFailureMessage("startIntent", "RemoteException")) { + splitScreen?.startIntent(intent, userId, fillInIntent, position, options, instanceId) + } + } + + /** + * Call this when going to recents so that shell can set-up and provide appropriate leashes for + * animation (eg. DividerBar). + * + * @return RemoteAnimationTargets of windows that need to animate but only exist in shell. + */ + fun onGoingToRecentsLegacy( + apps: Array? + ): Array? { + if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) return null + return tryOrElse("Failed call onGoingToRecentsLegacy", null) { + return splitScreen?.onGoingToRecentsLegacy(apps) + } + } + + fun onStartingSplitLegacy(apps: Array?): Array? { + return tryOrElse("Failed call onStartingSplitLegacy", null) { + return splitScreen?.onStartingSplitLegacy(apps) + } + } + + // + // One handed + // + fun startOneHandedMode() { + tryOrLog("Failed call startOneHandedMode") { oneHanded?.startOneHanded() } + } + + fun stopOneHandedMode() { + tryOrLog("Failed call startOneHandedstopOneHandedModeMode") { oneHanded?.stopOneHanded() } + } + + // + // Remote transitions + // + fun registerRemoteTransition(remoteTransition: RemoteTransition, filter: TransitionFilter) { + tryOrLog("Failed call registerRemoteTransition") { + shellTransitions?.registerRemote(filter, remoteTransition) + } + if (!remoteTransitions.containsKey(remoteTransition)) { + remoteTransitions[remoteTransition] = filter + } + } + + fun unregisterRemoteTransition(remoteTransition: RemoteTransition) { + tryOrLog("Failed call unregisterRemoteTransition") { + shellTransitions?.unregisterRemote(remoteTransition) + } + remoteTransitions.remove(remoteTransition) + } + + fun setHomeTransitionListener(listener: IHomeTransitionListener?) { + if (!FeatureFlags.enableHomeTransitionListener()) return + + homeTransitionListener = listener + + tryOrLog("Failed call unregisterRemoteTransition") { + shellTransitions?.setHomeTransitionListener(listener) + ?: Log.w( + TAG, + "Unable to call setHomeTransitionListener because ShellTransitions is null" + ) + } + } + + /** + * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary if + * Launcher and SystemUI need to coordinate transactions (eg. for shell transitions). + */ + fun shareTransactionQueue() { + if (originalTransactionToken == null) { + originalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken() + } + setupTransactionQueue() + } + + /** Switch back to using Launcher's independent transaction queue. */ + fun unshareTransactionQueue() { + originalTransactionToken?.let { SurfaceControl.Transaction.setDefaultApplyToken(it) } + originalTransactionToken = null + } + + private fun setupTransactionQueue() { + originalTransactionToken ?: return + + shellTransitions?.apply { + tryOrLog("Error getting Shell's apply token") { + this.shellApplyToken?.apply { + SurfaceControl.Transaction.setDefaultApplyToken(this) + } + ?: Log.e(TAG, "Didn't receive apply token from Shell") + } + } + ?: SurfaceControl.Transaction.setDefaultApplyToken(originalTransactionToken) + } + + // + // Starting window + // + /** Sets listener to get callbacks when launching a task. */ + fun setStartingWindowListener(listener: IStartingWindowListener?) { + tryOrLog("Failed call setStartingWindowListener") { + startingWindow?.setStartingWindowListener(listener) + } + startingWindowListener = listener + } + + // + // SmartSpace transitions + // + /** + * Sets the instance of [ILauncherUnlockAnimationController] that System UI should use to + * control the launcher side of the unlock animation. This will also cause us to dispatch the + * current state of the smartspace to System UI (this will subsequently happen if the state + * changes). + */ + fun setLauncherUnlockAnimationController( + activityClass: String?, + controller: ILauncherUnlockAnimationController? + ) { + tryOrLog("Failed call setLauncherUnlockAnimationController") { + sysuiUnlockAnimationController?.let { + it.setLauncherUnlockController(activityClass, controller) + controller?.dispatchSmartspaceStateToSysui() + } + } + + launcherActivityClass = activityClass + launcherUnlockAnimationController = controller + } + + /** + * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare + * the unlock animation accordingly. + */ + fun notifySysuiSmartspaceStateUpdated(state: SmartspaceState?) { + tryOrLog("Failed call notifySysuiSmartspaceStateUpdated") { + sysuiUnlockAnimationController?.onLauncherSmartspaceStateUpdated(state) + } + } + + // + // Recents + // + fun registerRecentTasksListener(listener: IRecentTasksListener?) { + tryOrLog("Failed call registerRecentTasksListener") { + recentTasks?.registerRecentTasksListener(listener) + } + recentTasksListener = listener + } + + // + // Back navigation transitions + // + /** Sets the launcher [android.window.IOnBackInvokedCallback] to shell */ + fun setBackToLauncherCallback( + callback: IOnBackInvokedCallback?, + runner: IRemoteAnimationRunner? + ) { + backToLauncherCallback = callback + backToLauncherRunner = runner + + callback ?: return + tryOrLog("Failed call setBackToLauncherCallback") { + backAnimation?.setBackToLauncherCallback(callback, runner) + } + } + + /** + * Clears the previously registered [IOnBackInvokedCallback]. + * + * @param callback The previously registered callback instance. + */ + fun clearBackToLauncherCallback(callback: IOnBackInvokedCallback) { + if (backToLauncherCallback !== callback) { + return + } + backToLauncherCallback = null + backToLauncherRunner = null + tryOrLog("Failed call clearBackToLauncherCallback") { + backAnimation?.clearBackToLauncherCallback() + } + } + + /** Called when the status bar color needs to be customized when back navigation. */ + fun customizeStatusBarAppearance(appearance: AppearanceRegion?) { + tryOrLog("Failed call customizeStatusBarAppearance") { + backAnimation?.customizeStatusBarAppearance(appearance) + } + } + + fun getRecentTasks(numTasks: Int, userId: Int): ArrayList { + return tryOrElse("Failed call getRecentTasks", ArrayList()) { + return recentTasks + ?.getRecentTasks(numTasks, ActivityManager.RECENT_IGNORE_UNAVAILABLE, userId) + ?.let { ArrayList(it.asList()) } + ?: ArrayList() + } + } + + /** Gets the set of running tasks. */ + fun getRunningTasks(numTasks: Int): ArrayList { + if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_PC)) { + return ArrayList() + } + + return tryOrElse("Failed call getRunningTasks", ArrayList()) { + recentTasks?.getRunningTasks(numTasks)?.let { ArrayList(it.asList()) } ?: ArrayList() + } + } + + private fun handleMessageAsync(msg: Message): Boolean { + when (msg.what) { + MSG_SET_SHELF_HEIGHT -> { + setShelfHeightAsync(msg.arg1, msg.arg2) + return true + } + MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT -> { + setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2) + return true + } + } + return false + } + + // + // Desktop Mode + // + /** Call shell to show all apps active on the desktop */ + fun showDesktopApps(displayId: Int, transition: RemoteTransition?) { + tryOrLog("Failed call showDesktopApps") { + desktopMode?.showDesktopApps(displayId, transition) + } + } + + /** Call shell to stash desktop apps */ + fun stashDesktopApps(displayId: Int) { + tryOrLog("Failed call stashDesktopApps") { desktopMode?.stashDesktopApps(displayId) } + } + + /** Call shell to hide desktop apps that may be stashed */ + fun hideStashedDesktopApps(displayId: Int) { + tryOrLog("Failed call hideStashedDesktopApps") { + desktopMode?.hideStashedDesktopApps(displayId) + } + } + + /** If task with the given id is on the desktop, bring it to front */ + fun showDesktopApp(taskId: Int) { + tryOrLog("Failed call showDesktopApp") { desktopMode?.showDesktopApp(taskId) } + } + + /** Call shell to get number of visible freeform tasks */ + fun getVisibleDesktopTaskCount(displayId: Int): Int { + return tryOrElse("Failed call getVisibleDesktopTaskCount", 0) { + return desktopMode?.getVisibleTaskCount(displayId) ?: 0 + } + } + + /** Set a listener on shell to get updates about desktop task state */ + fun setDesktopTaskListener(listener: IDesktopTaskListener?) { + desktopTaskListener = listener + tryOrLog("Failed call setDesktopTaskListener") { desktopMode?.setTaskListener(listener) } + } + + /** Perform cleanup transactions after animation to split select is complete */ + fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo?) { + tryOrLog("Failed call onDesktopSplitSelectAnimComplete") { + desktopMode?.onDesktopSplitSelectAnimComplete(taskInfo) + } + } + // + // Unfold transition + // + /** Sets the unfold animation lister to sysui. */ + fun setUnfoldAnimationListener(callback: IUnfoldTransitionListener?) { + unfoldAnimationListener = callback + tryOrLog("Failed call setUnfoldAnimationListener") { + unfoldAnimation?.setListener(callback) + } + } + + // + // Recents + // + /** Starts the recents activity. The caller should manage the thread on which this is called. */ + fun startRecentsActivity( + intent: Intent?, + options: ActivityOptions, + listener: RecentsAnimationListener + ): Boolean { + + val runner: IRecentsAnimationRunner = + object : IRecentsAnimationRunner.Stub() { + override fun onAnimationStart( + controller: IRecentsAnimationController, + apps: Array, + wallpapers: Array, + homeContentInsets: Rect, + minimizedHomeBounds: Rect, + extras: Bundle? + ) { + // Aidl bundles need to explicitly set class loader + // https://developer.android.com/guide/components/aidl#Bundles + extras?.classLoader = javaClass.classLoader + listener.onAnimationStart( + RecentsAnimationControllerCompat(controller), + apps, + wallpapers, + homeContentInsets, + minimizedHomeBounds, + extras + ) + } + + override fun onAnimationCanceled( + taskIds: IntArray, + taskSnapshots: Array + ) { + listener.onAnimationCanceled(ThumbnailData.wrap(taskIds, taskSnapshots)) + } + + override fun onTasksAppeared(apps: Array) { + listener.onTasksAppeared(apps) + } + } + + return tryOrElse("Error starting recents via shell", false) { + recentTasks?.let { + it.startRecentsTransition( + recentsPendingIntent, + intent, + options.toBundle(), + context.iApplicationThread, + runner + ) + return true + } + ?: run { + ActiveGestureLog.INSTANCE.addLog( + "Null recentTasks", + ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING + ) + false + } + } + } + + // + // Drag and drop + // + + /** + * For testing purposes. Returns `true` only if the shell drop target has shown and drawn and is + * ready to handle drag events and the subsequent drop. + */ + val isDragAndDropReady: Boolean + get() = + tryOrElse("Error querying drag state", false) { + dragAndDrop?.isReadyToHandleDrag ?: false + } + + fun dump(pw: PrintWriter) { + pw.println("$TAG:") + pw.println("\tsystemUiProxy=$systemUiProxy") + pw.println("\tpip=$pip") + pw.println("\tpipAnimationListener=$pipAnimationListener") + pw.println("\tbubbles=$bubbles") + pw.println("\tbubblesListener=$bubblesListener") + pw.println("\tsplitScreen=$splitScreen") + pw.println("\tsplitScreenListener=$splitScreenListener") + pw.println("\tsplitSelectListener=$splitSelectListener") + pw.println("\toneHanded=$oneHanded") + pw.println("\tshellTransitions=$shellTransitions") + pw.println("\thomeTransitionListener=$homeTransitionListener") + pw.println("\tstartingWindow=$startingWindow") + pw.println("\tstartingWindowListener=$startingWindowListener") + pw.println("\tsysuiUnlockAnimationController=$sysuiUnlockAnimationController") + pw.println("\tlauncherActivityClass=$launcherActivityClass") + pw.println("\tlauncherUnlockAnimationController=$launcherUnlockAnimationController") + pw.println("\trecentTasks=$recentTasks") + pw.println("\trecentTasksListener=$recentTasksListener") + pw.println("\tbackAnimation=$backAnimation") + pw.println("\tbackToLauncherCallback=$backToLauncherCallback") + pw.println("\tbackToLauncherRunner=$backToLauncherRunner") + pw.println("\tdesktopMode=$desktopMode") + pw.println("\tdesktopTaskListener=$desktopTaskListener") + pw.println("\tunfoldAnimation=$unfoldAnimation") + pw.println("\tunfoldAnimationListener=$unfoldAnimationListener") + pw.println("\tdragAndDrop=$dragAndDrop") + } + + companion object { + private const val TAG = "SystemUiProxy" + private const val MSG_SET_SHELF_HEIGHT = 1 + private const val MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2 + + @JvmField + val INSTANCE = MainThreadInitializedObject { context: Context -> SystemUiProxy(context) } + + private inline fun tryOrLog(msg: String, f: () -> Unit) = + try { + f() + } catch (e: RemoteException) { + Log.w(TAG, msg, e) + } + + private inline fun tryOrElse(msg: String, fallback: T, f: () -> T): T = + try { + f() + } catch (e: RemoteException) { + Log.w(TAG, msg, e) + fallback + } + } +}