From 013a8a75abb9e940163dfafceb0c1fefc218652f Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 12 Apr 2022 22:56:24 -0700 Subject: [PATCH] Removing AM.getTasks call on UI thread during touch down We maintain a cached task queue using TaskStackChangeListener. It keeps the top RunningTaskInfo partially in sync with platfrom: taskInfo is only initialized when the top task changes but not when there are changes within the task itself. Bug: 214581894 Bug: 220742398 Test: Manual and presubmit Change-Id: Ifafde574040c03435b78f044350c45f3b49c4bcb --- .../NavBarToHomeTouchController.java | 5 +- .../android/quickstep/AbsSwipeUpHandler.java | 29 +- .../quickstep/FallbackSwipeHandler.java | 25 +- .../com/android/quickstep/GestureState.java | 30 +-- .../RecentsAnimationDeviceState.java | 39 +-- .../android/quickstep/RemoteTargetGluer.java | 12 +- .../quickstep/SwipeUpAnimationLogic.java | 4 +- .../quickstep/TaskAnimationManager.java | 6 +- .../com/android/quickstep/TopTaskTracker.java | 248 ++++++++++++++++++ .../quickstep/TouchInteractionService.java | 39 +-- .../fallback/FallbackRecentsView.java | 42 ++- .../quickstep/interaction/AllSetActivity.java | 4 +- .../interaction/GestureSandboxActivity.java | 41 ++- .../quickstep/util/AssistantUtilities.java | 46 ---- .../util/LauncherSplitScreenListener.java | 119 --------- .../android/quickstep/views/RecentsView.java | 45 ++-- 16 files changed, 399 insertions(+), 335 deletions(-) create mode 100644 quickstep/src/com/android/quickstep/TopTaskTracker.java delete mode 100644 quickstep/src/com/android/quickstep/util/AssistantUtilities.java delete mode 100644 quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index 4d3b05721c..7bfb76a978 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -48,8 +48,8 @@ import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.util.TouchController; import com.android.quickstep.TaskUtils; +import com.android.quickstep.TopTaskTracker; import com.android.quickstep.util.AnimatorControllerWithResistance; -import com.android.quickstep.util.AssistantUtilities; import com.android.quickstep.util.OverviewToHomeAnim; import com.android.quickstep.views.RecentsView; @@ -112,7 +112,8 @@ public class NavBarToHomeTouchController implements TouchController, return true; } if (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get() - && AssistantUtilities.isExcludedAssistantRunning()) { + && TopTaskTracker.INSTANCE.get(mLauncher).getCachedTopTask(false) + .isExcludedAssistant()) { return true; } return false; diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 6e804021f1..2ae0646ed0 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -102,7 +102,6 @@ import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.InputConsumerProxy; import com.android.quickstep.util.InputProxyHandlerFactory; -import com.android.quickstep.util.LauncherSplitScreenListener; import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.util.ProtoTracer; import com.android.quickstep.util.RecentsOrientedState; @@ -114,6 +113,7 @@ import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.VibratorWrapper; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; @@ -564,24 +564,12 @@ public abstract class AbsSwipeUpHandler, } protected void notifyGestureAnimationStartToRecents() { - ActivityManager.RunningTaskInfo[] runningTasks; + Task[] runningTasks; if (mIsSwipeForStagedSplit) { - int[] splitTaskIds = - LauncherSplitScreenListener.INSTANCE.getNoCreate().getRunningSplitTaskIds(); - runningTasks = new ActivityManager.RunningTaskInfo[splitTaskIds.length]; - for (int i = 0; i < splitTaskIds.length; i++) { - int taskId = splitTaskIds[i]; - // Order matters here, we want first indexed RunningTaskInfo to be leftTop task - for (ActivityManager.RunningTaskInfo rti : mGestureState.getRunningTasks()) { - if (taskId == rti.taskId) { - runningTasks[i] = rti; - break; - } - - } - } + int[] splitTaskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds(); + runningTasks = mGestureState.getRunningTask().getPlaceholderTasks(splitTaskIds); } else { - runningTasks = new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()}; + runningTasks = mGestureState.getRunningTask().getPlaceholderTasks(); } mRecentsView.onGestureAnimationStart(runningTasks, mDeviceState.getRotationTouchHelper()); } @@ -801,7 +789,7 @@ public abstract class AbsSwipeUpHandler, RecentsAnimationTargets targets) { super.onRecentsAnimationStart(controller, targets); ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length); - mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(targets); + mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(mContext, targets); mRecentsAnimationController = controller; mRecentsAnimationTargets = targets; @@ -1380,7 +1368,7 @@ public abstract class AbsSwipeUpHandler, private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory, RemoteAnimationTargetCompat runningTaskTarget, float startProgress) { // Directly animate the app to PiP (picture-in-picture) mode - final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask(); + final ActivityManager.RunningTaskInfo taskInfo = runningTaskTarget.taskInfo; final RecentsOrientedState orientationState = mRemoteTargetHandles[0].getTaskViewSimulator() .getOrientationState(); final int windowRotation = calculateWindowRotation(runningTaskTarget, orientationState); @@ -1782,8 +1770,7 @@ public abstract class AbsSwipeUpHandler, new PictureInPictureSurfaceTransaction.Builder() .setAlpha(0f) .build(); - int[] taskIds = - LauncherSplitScreenListener.INSTANCE.getNoCreate().getRunningSplitTaskIds(); + int[] taskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds(); for (int taskId : taskIds) { mRecentsAnimationController.setFinishTaskTransaction(taskId, tx, null /* overlay */); diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java index 9828467c79..ee5bb44040 100644 --- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java @@ -30,7 +30,7 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACT import android.animation.ObjectAnimator; import android.annotation.TargetApi; -import android.app.ActivityManager; +import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityOptions; import android.content.ActivityNotFoundException; import android.content.Context; @@ -69,7 +69,6 @@ import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.TransformParams; import com.android.quickstep.util.TransformParams.BuilderProxy; import com.android.systemui.shared.recents.model.Task.TaskKey; -import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; @@ -109,7 +108,7 @@ public class FallbackSwipeHandler extends super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs, continuingLastGesture, inputConsumer); - mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask()); + mRunningOverHome = mGestureState.getRunningTask().isHomeTask(); if (mRunningOverHome) { runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams().setHomeBuilderProxy( @@ -150,16 +149,17 @@ public class FallbackSwipeHandler extends return new FallbackPipToHomeAnimationFactory(); } mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration); - startHomeIntent(mActiveAnimationFactory); + startHomeIntent(mActiveAnimationFactory, runningTaskTarget); return mActiveAnimationFactory; } private void startHomeIntent( - @Nullable FallbackHomeAnimationFactory gestureContractAnimationFactory) { + @Nullable FallbackHomeAnimationFactory gestureContractAnimationFactory, + @Nullable RemoteAnimationTargetCompat runningTaskTarget) { ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); Intent intent = new Intent(mGestureState.getHomeIntent()); - if (gestureContractAnimationFactory != null) { - gestureContractAnimationFactory.addGestureContract(intent); + if (gestureContractAnimationFactory != null && runningTaskTarget != null) { + gestureContractAnimationFactory.addGestureContract(intent, runningTaskTarget.taskInfo); } try { mContext.startActivity(intent, options.toBundle()); @@ -187,7 +187,8 @@ public class FallbackSwipeHandler extends // the PiP task appearing. recentsCallback = () -> { callback.run(); - startHomeIntent(null /* gestureContractAnimationFactory */); + startHomeIntent( + null /* gestureContractAnimationFactory */, null /* runningTaskTarget */); }; } else { recentsCallback = callback; @@ -212,7 +213,7 @@ public class FallbackSwipeHandler extends if (mRunningOverHome) { if (DisplayController.getNavigationMode(mContext).hasGestures) { mRecentsView.onGestureAnimationStartOnHome( - new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()}, + mGestureState.getRunningTask().getPlaceholderTasks(), mDeviceState.getRotationTouchHelper()); } } else { @@ -396,12 +397,12 @@ public class FallbackSwipeHandler extends } } - private void addGestureContract(Intent intent) { - if (mRunningOverHome || mGestureState.getRunningTask() == null) { + private void addGestureContract(Intent intent, RunningTaskInfo runningTaskInfo) { + if (mRunningOverHome || runningTaskInfo == null) { return; } - TaskKey key = new TaskKey(mGestureState.getRunningTask()); + TaskKey key = new TaskKey(runningTaskInfo); if (key.getComponent() != null) { if (sMessageReceiver == null) { sMessageReceiver = new StaticMessageReceiver(); diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java index ed0623da24..3b52e9178e 100644 --- a/quickstep/src/com/android/quickstep/GestureState.java +++ b/quickstep/src/com/android/quickstep/GestureState.java @@ -22,7 +22,6 @@ import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import android.annotation.Nullable; import android.annotation.TargetApi; -import android.app.ActivityManager; import android.content.Intent; import android.os.Build; @@ -30,6 +29,7 @@ import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.tracing.GestureStateProto; import com.android.launcher3.tracing.SwipeHandlerProto; +import com.android.quickstep.TopTaskTracker.CachedTaskInfo; import com.android.quickstep.util.ActiveGestureLog; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -135,8 +135,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL private final MultiStateCallback mStateCallback; private final int mGestureId; - private ActivityManager.RunningTaskInfo mRunningTask; - private ActivityManager.RunningTaskInfo[] mRunningTasks; + private CachedTaskInfo mRunningTask; private GestureEndTarget mEndTarget; private RemoteAnimationTargetCompat mLastAppearedTaskTarget; private Set mPreviouslyAppearedTaskIds = new HashSet<>(); @@ -232,41 +231,24 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL /** * @return the running task for this gesture. */ - public ActivityManager.RunningTaskInfo getRunningTask() { + public CachedTaskInfo getRunningTask() { return mRunningTask; } - /** - * This will array will contain the task returned by {@link #getRunningTask()} - * @return the running tasks for this gesture. - */ - public ActivityManager.RunningTaskInfo[] getRunningTasks() { - return mRunningTasks; - } - /** * @return the running task id for this gesture. */ public int getRunningTaskId() { - return mRunningTask != null ? mRunningTask.taskId : -1; + return mRunningTask != null ? mRunningTask.getTaskId() : -1; } /** * Updates the running task for the gesture to be the given {@param runningTask}. */ - public void updateRunningTask(ActivityManager.RunningTaskInfo runningTask) { + public void updateRunningTask(CachedTaskInfo runningTask) { mRunningTask = runningTask; } - /** - * TODO(b/210903248) refactor to consolidate w/ method above - * Updates the running task for the gesture to be the given {@param runningTask}. - */ - public void updateRunningTasks(ActivityManager.RunningTaskInfo[] runningTasks) { - mRunningTasks = runningTasks; - updateRunningTask(runningTasks[0]); - } - /** * Updates the last task that appeared during this gesture. */ @@ -339,7 +321,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL * user controlled gesture. */ public void setHandlingAtomicEvent(boolean handlingAtomicEvent) { - mHandlingAtomicEvent = true; + mHandlingAtomicEvent = handlingAtomicEvent; } /** diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 7ad60bb0bc..920ed71b80 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -45,14 +45,11 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_Q import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; -import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Resources; import android.graphics.Region; import android.inputmethodservice.InputMethodService; import android.net.Uri; @@ -61,18 +58,17 @@ import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserManager; import android.provider.Settings; -import android.text.TextUtils; import android.view.MotionEvent; import androidx.annotation.BinderThread; -import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.DisplayController.NavigationMode; import com.android.launcher3.util.SettingsCache; +import com.android.quickstep.TopTaskTracker.CachedTaskInfo; import com.android.quickstep.util.NavBarPosition; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; @@ -83,7 +79,6 @@ import com.android.systemui.shared.system.TaskStackChangeListeners; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.List; /** * Manages the state of the system during a swipe up gesture. @@ -97,7 +92,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { private final int mDisplayId; private final RotationTouchHelper mRotationTouchHelper; private final TaskStackChangeListener mPipListener; - private final List mGestureBlockedActivities; // Cache for better performance since it doesn't change at runtime. private final boolean mCanImeRenderGesturalNavButtons = InputMethodService.canImeRenderGesturalNavButtons(); @@ -129,6 +123,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { } }; + private int mGestureBlockingTaskId = -1; private Region mExclusionRegion; private SystemGestureExclusionListenerCompat mExclusionListener; @@ -178,22 +173,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { onDisplayInfoChanged(context, mDisplayController.getInfo(), CHANGE_ALL); runOnDestroy(() -> mDisplayController.removeChangeListener(this)); - // Add any blocked activities - String[] blockingActivities; - try { - blockingActivities = - context.getResources().getStringArray(R.array.gesture_blocking_activities); - } catch (Resources.NotFoundException e) { - blockingActivities = new String[0]; - } - mGestureBlockedActivities = new ArrayList<>(blockingActivities.length); - for (String blockingActivity : blockingActivities) { - if (!TextUtils.isEmpty(blockingActivity)) { - mGestureBlockedActivities.add( - ComponentName.unflattenFromString(blockingActivity)); - } - } - SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext); if (mIsOneHandedModeSupported) { Uri oneHandedUri = Settings.Secure.getUriFor(ONE_HANDED_ENABLED); @@ -367,11 +346,17 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { } /** - * @return whether the given running task info matches the gesture-blocked activity. + * Sets the task id where gestures should be blocked */ - public boolean isGestureBlockedActivity(ActivityManager.RunningTaskInfo runningTaskInfo) { - return runningTaskInfo != null - && mGestureBlockedActivities.contains(runningTaskInfo.topActivity); + public void setGestureBlockingTaskId(int taskId) { + mGestureBlockingTaskId = taskId; + } + + /** + * @return whether the given running task info matches the gesture-blocked task. + */ + public boolean isGestureBlockedTask(CachedTaskInfo taskInfo) { + return taskInfo != null && taskInfo.getTaskId() == mGestureBlockingTaskId; } /** diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java index afb4d4dfe4..c3ea25683d 100644 --- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java +++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java @@ -22,7 +22,6 @@ import androidx.annotation.Nullable; import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds; import com.android.quickstep.util.AnimatorControllerWithResistance; -import com.android.quickstep.util.LauncherSplitScreenListener; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -50,8 +49,7 @@ public class RemoteTargetGluer { * running tasks */ public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) { - int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate() - .getRunningSplitTaskIds(); + int[] splitIds = TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds(); mRemoteTargetHandles = createHandles(context, sizingStrategy, splitIds.length == 2 ? 2 : 1); } @@ -73,7 +71,7 @@ public class RemoteTargetGluer { * Length of targets.apps should match that of {@link #mRemoteTargetHandles}. * * If split screen may be active when this is called, you might want to use - * {@link #assignTargetsForSplitScreen(RemoteAnimationTargets)} + * {@link #assignTargetsForSplitScreen(Context, RemoteAnimationTargets)} */ public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) { for (int i = 0; i < mRemoteTargetHandles.length; i++) { @@ -90,9 +88,9 @@ public class RemoteTargetGluer { * apps in targets.apps to that of the _active_ split screened tasks. * See {@link #assignTargetsForSplitScreen(RemoteAnimationTargets, int[])} */ - public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) { - int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate() - .getRunningSplitTaskIds(); + public RemoteTargetHandle[] assignTargetsForSplitScreen( + Context context, RemoteAnimationTargets targets) { + int[] splitIds = TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds(); return assignTargetsForSplitScreen(targets, splitIds); } diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java index 8862073725..088e1cfc00 100644 --- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -37,7 +37,6 @@ import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle; import com.android.quickstep.util.AnimatorControllerWithResistance; -import com.android.quickstep.util.LauncherSplitScreenListener; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; @@ -82,8 +81,7 @@ public abstract class SwipeUpAnimationLogic implements mGestureState = gestureState; mIsSwipeForStagedSplit = ENABLE_SPLIT_SELECT.get() && - LauncherSplitScreenListener.INSTANCE.getNoCreate() - .getRunningSplitTaskIds().length > 1; + TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds().length > 1; mTargetGluer = new RemoteTargetGluer(mContext, mGestureState.getActivityInterface()); mRemoteTargetHandles = mTargetGluer.getRemoteTargetHandles(); diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 4bb2400df1..b8334a9ae8 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -35,6 +35,7 @@ import androidx.annotation.UiThread; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; +import com.android.quickstep.TopTaskTracker.CachedTaskInfo; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -234,9 +235,8 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn // Allowing to pause Home if Home is top activity and Recents is not Home. So when user // start home when recents animation is playing, the home activity can be resumed again // to let the transition controller collect Home activity. - ActivityManager.RunningTaskInfo rti = gestureState.getRunningTask(); - boolean homeIsOnTop = rti != null && rti.topActivity != null - && rti.topActivity.equals(gestureState.getHomeIntent().getComponent()); + CachedTaskInfo cti = gestureState.getRunningTask(); + boolean homeIsOnTop = cti != null && cti.isHomeTask(); if (!homeIsOnTop) { options.setTransientLaunch(); } diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java new file mode 100644 index 0000000000..80bc329620 --- /dev/null +++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2022 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.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.content.Intent.ACTION_CHOOSER; +import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; + +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT; + +import android.app.ActivityManager.RunningTaskInfo; +import android.content.Context; + +import androidx.annotation.UiThread; + +import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.launcher3.util.SplitConfigurationOptions; +import com.android.launcher3.util.SplitConfigurationOptions.StagePosition; +import com.android.launcher3.util.SplitConfigurationOptions.StageType; +import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitTaskPosition; +import com.android.launcher3.util.TraceHelper; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.Task.TaskKey; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; +import com.android.wm.shell.splitscreen.ISplitScreenListener; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * This class tracked the top-most task and some 'approximate' task history to allow faster + * system state estimation during touch interaction + */ +public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskStackChangeListener { + + public static MainThreadInitializedObject INSTANCE = + new MainThreadInitializedObject<>(TopTaskTracker::new); + + private static final int HISTORY_SIZE = 5; + + // Ordered list with first item being the most recent task. + private final LinkedList mOrderedTaskList = new LinkedList<>(); + + private final StagedSplitTaskPosition mMainStagePosition = new StagedSplitTaskPosition(); + private final StagedSplitTaskPosition mSideStagePosition = new StagedSplitTaskPosition(); + + private TopTaskTracker(Context context) { + mMainStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_MAIN; + mSideStagePosition.stageType = SplitConfigurationOptions.STAGE_TYPE_SIDE; + + TaskStackChangeListeners.getInstance().registerTaskStackListener(this); + SystemUiProxy.INSTANCE.get(context).registerSplitScreenListener(this); + } + + @Override + public void onTaskRemoved(int taskId) { + mOrderedTaskList.removeIf(rto -> rto.taskId == taskId); + } + + @Override + public void onTaskMovedToFront(RunningTaskInfo taskInfo) { + mOrderedTaskList.removeIf(rto -> rto.taskId == taskInfo.taskId); + mOrderedTaskList.addFirst(taskInfo); + if (mOrderedTaskList.size() >= HISTORY_SIZE) { + // If we grow in size, remove the last taskInfo which is not part of the split task. + Iterator itr = mOrderedTaskList.descendingIterator(); + while (itr.hasNext()) { + RunningTaskInfo info = itr.next(); + if (info.taskId != taskInfo.taskId + && info.taskId != mMainStagePosition.taskId + && info.taskId != mSideStagePosition.taskId) { + itr.remove(); + return; + } + } + } + } + + @Override + public void onStagePositionChanged(@StageType int stage, @StagePosition int position) { + if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) { + mMainStagePosition.stagePosition = position; + } else { + mSideStagePosition.stagePosition = position; + } + } + + @Override + public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) { + // If task is not visible but we are tracking it, stop tracking it + if (!visible) { + if (mMainStagePosition.taskId == taskId) { + resetTaskId(mMainStagePosition); + } else if (mSideStagePosition.taskId == taskId) { + resetTaskId(mSideStagePosition); + } // else it's an un-tracked child + return; + } + + // If stage has moved to undefined, stop tracking the task + if (stage == SplitConfigurationOptions.STAGE_TYPE_UNDEFINED) { + resetTaskId(taskId == mMainStagePosition.taskId + ? mMainStagePosition : mSideStagePosition); + return; + } + + if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) { + mMainStagePosition.taskId = taskId; + } else { + mSideStagePosition.taskId = taskId; + } + } + + private void resetTaskId(StagedSplitTaskPosition taskPosition) { + taskPosition.taskId = -1; + } + + /** + * @return index 0 will be task in left/top position, index 1 in right/bottom position. + * Will return empty array if device is not in staged split + */ + public int[] getRunningSplitTaskIds() { + if (mMainStagePosition.taskId == -1 || mSideStagePosition.taskId == -1) { + return new int[]{}; + } + int[] out = new int[2]; + if (mMainStagePosition.stagePosition == STAGE_POSITION_TOP_OR_LEFT) { + out[0] = mMainStagePosition.taskId; + out[1] = mSideStagePosition.taskId; + } else { + out[1] = mMainStagePosition.taskId; + out[0] = mSideStagePosition.taskId; + } + return out; + } + + + /** + * Returns the CachedTaskInfo for the top most task + */ + @UiThread + public CachedTaskInfo getCachedTopTask(boolean filterOnlyVisibleRecents) { + if (filterOnlyVisibleRecents) { + // Since we only know about the top most task, any filtering may not be applied on the + // cache. The second to top task may change while the top task is still the same. + RunningTaskInfo[] tasks = TraceHelper.allowIpcs("getCachedTopTask.true", () -> + ActivityManagerWrapper.getInstance().getRunningTasks(true)); + return new CachedTaskInfo(Arrays.asList(tasks)); + } + + if (mOrderedTaskList.isEmpty()) { + RunningTaskInfo[] tasks = TraceHelper.allowIpcs("getCachedTopTask.false", () -> + ActivityManagerWrapper.getInstance().getRunningTasks( + false /* filterOnlyVisibleRecents */)); + Collections.addAll(mOrderedTaskList, tasks); + } + return new CachedTaskInfo(new ArrayList<>(mOrderedTaskList)); + } + + /** + * Class to provide information about a task which can be safely cached and do not change + * during the lifecycle of the task. + */ + public static class CachedTaskInfo { + + private final RunningTaskInfo mTopTask; + private final List mAllCachedTasks; + + CachedTaskInfo(List allCachedTasks) { + mAllCachedTasks = allCachedTasks; + mTopTask = allCachedTasks.get(0); + } + + public int getTaskId() { + return mTopTask.taskId; + } + + /** + * Returns true if the root of the task chooser activity + */ + public boolean isRootChooseActivity() { + return ACTION_CHOOSER.equals(mTopTask.baseIntent.getAction()); + } + + /** + * Returns true if the given task holds an Assistant activity that is excluded from recents + */ + public boolean isExcludedAssistant() { + return mTopTask.configuration.windowConfiguration + .getActivityType() == ACTIVITY_TYPE_ASSISTANT + && (mTopTask.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0; + } + + /** + * Returns true if this represents the HOME task + */ + public boolean isHomeTask() { + return mTopTask.configuration.windowConfiguration + .getActivityType() == ACTIVITY_TYPE_HOME; + } + + /** + * Returns {@link Task} array which can be used as a placeholder until the true object + * is loaded by the model + */ + public Task[] getPlaceholderTasks() { + return new Task[] {Task.from(new TaskKey(mTopTask), mTopTask, false)}; + } + + /** + * Returns {@link Task} array corresponding to the provided task ids which can be used as a + * placeholder until the true object is loaded by the model + */ + public Task[] getPlaceholderTasks(int[] taskIds) { + Task[] result = new Task[taskIds.length]; + for (int i = 0; i < taskIds.length; i++) { + final int index = i; + int taskId = taskIds[i]; + mAllCachedTasks.forEach(rti -> { + if (rti.taskId == taskId) { + result[index] = Task.from(new TaskKey(rti), rti, false); + } + }); + } + return result; + } + } +} diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 320682521c..0078d55398 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -15,7 +15,6 @@ */ package com.android.quickstep; -import static android.content.Intent.ACTION_CHOOSER; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; @@ -41,7 +40,6 @@ import android.annotation.TargetApi; import android.app.PendingIntent; import android.app.RemoteAction; import android.app.Service; -import android.content.ComponentName; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; @@ -95,8 +93,6 @@ import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer; import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer; import com.android.quickstep.util.ActiveGestureLog; -import com.android.quickstep.util.AssistantUtilities; -import com.android.quickstep.util.LauncherSplitScreenListener; import com.android.quickstep.util.ProtoTracer; import com.android.quickstep.util.ProxyScreenStatusProvider; import com.android.quickstep.util.SplitScreenBounds; @@ -314,6 +310,13 @@ public class TouchInteractionService extends Service public void setSwipeUpProxy(Function proxy) { mSwipeUpProxyProvider = proxy != null ? proxy : (i -> null); } + + /** + * Sets the task id where gestures should be blocked + */ + public void setGestureBlockedTaskId(int taskId) { + mDeviceState.setGestureBlockingTaskId(taskId); + } } private static boolean sConnected = false; @@ -369,7 +372,6 @@ public class TouchInteractionService extends Service mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); ProtoTracer.INSTANCE.get(this).add(this); - LauncherSplitScreenListener.INSTANCE.get(this).init(); sConnected = true; } @@ -419,6 +421,9 @@ public class TouchInteractionService extends Service onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags()); onAssistantVisibilityChanged(); + // Initialize the task tracker + TopTaskTracker.INSTANCE.get(this); + // Temporarily disable model preload // new ModelPreload().start(this); mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this) @@ -531,7 +536,6 @@ public class TouchInteractionService extends Service getSystemService(AccessibilityManager.class) .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS); - LauncherSplitScreenListener.INSTANCE.get(this).destroy(); mTaskbarManager.destroy(); sConnected = false; super.onDestroy(); @@ -633,7 +637,7 @@ public class TouchInteractionService extends Service private InputConsumer tryCreateAssistantInputConsumer(InputConsumer base, GestureState gestureState, MotionEvent motionEvent) { - return mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask()) + return mDeviceState.isGestureBlockedTask(gestureState.getRunningTask()) ? base : new AssistantInputConsumer(this, gestureState, base, mInputMonitorCompat, mDeviceState, motionEvent); @@ -648,8 +652,8 @@ public class TouchInteractionService extends Service gestureState.updatePreviouslyAppearedTaskIds( previousGestureState.getPreviouslyAppearedTaskIds()); } else { - gestureState.updateRunningTasks(TraceHelper.allowIpcs("getRunningTask.0", - () -> mAM.getRunningTasks(false /* filterOnlyVisibleRecents */))); + gestureState.updateRunningTask( + TopTaskTracker.INSTANCE.get(this).getCachedTopTask(false)); } return gestureState; } @@ -743,17 +747,14 @@ public class TouchInteractionService extends Service // Use overview input consumer for sharesheets on top of home. boolean forceOverviewInputConsumer = gestureState.getActivityInterface().isStarted() && gestureState.getRunningTask() != null - && ACTION_CHOOSER.equals(gestureState.getRunningTask().baseIntent.getAction()); - if (AssistantUtilities.isExcludedAssistant(gestureState.getRunningTask())) { + && gestureState.getRunningTask().isRootChooseActivity(); + if (gestureState.getRunningTask() != null + && gestureState.getRunningTask().isExcludedAssistant()) { // In the case where we are in the excluded assistant state, ignore it and treat the // running activity as the task behind the assistant - gestureState.updateRunningTask(TraceHelper.allowIpcs("getRunningTask.assistant", - () -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */))); - ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent(); - ComponentName runningComponent = - gestureState.getRunningTask().baseIntent.getComponent(); - forceOverviewInputConsumer = - runningComponent != null && runningComponent.equals(homeComponent); + gestureState.updateRunningTask(TopTaskTracker.INSTANCE.get(this) + .getCachedTopTask(true /* filterOnlyVisibleRecents */)); + forceOverviewInputConsumer = gestureState.getRunningTask().isHomeTask(); } if (ENABLE_QUICKSTEP_LIVE_TILE.get() @@ -770,7 +771,7 @@ public class TouchInteractionService extends Service || forceOverviewInputConsumer) { return createOverviewInputConsumer( previousGestureState, gestureState, event, forceOverviewInputConsumer); - } else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) { + } else if (mDeviceState.isGestureBlockedTask(gestureState.getRunningTask())) { return getDefaultInputConsumer(); } else { return createOtherActivityInputConsumer(gestureState, event); diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index 48af802ff8..c9ee2db1f1 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -24,7 +24,6 @@ import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT; import android.animation.AnimatorSet; import android.annotation.TargetApi; -import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.os.Build; import android.util.AttributeSet; @@ -50,7 +49,6 @@ import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; -import com.android.systemui.shared.recents.model.Task.TaskKey; import java.util.ArrayList; @@ -58,7 +56,7 @@ import java.util.ArrayList; public class FallbackRecentsView extends RecentsView implements StateListener { - private RunningTaskInfo mHomeTaskInfo; + private Task mHomeTask; public FallbackRecentsView(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -87,12 +85,12 @@ public class FallbackRecentsView extends RecentsView 1) { + protected boolean shouldAddStubTaskView(Task[] runningTasks) { + if (runningTasks.length > 1) { // can't be in split screen w/ home task - return super.shouldAddStubTaskView(runningTaskInfos); + return super.shouldAddStubTaskView(runningTasks); } - RunningTaskInfo runningTaskInfo = runningTaskInfos[0]; - if (mHomeTaskInfo != null && runningTaskInfo != null && - mHomeTaskInfo.taskId == runningTaskInfo.taskId + Task runningTask = runningTasks[0]; + if (mHomeTask != null && runningTask != null + && mHomeTask.key.id == runningTask.key.id && getTaskViewCount() == 0 && mLoadPlanEverApplied) { // Do not add a stub task if we are running over home with empty recents, so that we // show the empty recents message instead of showing a stub task and later removing it. // Ignore empty task signal if applyLoadPlan has never run. return false; } - return super.shouldAddStubTaskView(runningTaskInfos); + return super.shouldAddStubTaskView(runningTasks); } @Override @@ -169,7 +167,7 @@ public class FallbackRecentsView extends RecentsView newList = new ArrayList<>(taskGroups.size() + 1); newList.addAll(taskGroups); - newList.add(new GroupTask( - Task.from(new TaskKey(mHomeTaskInfo), mHomeTaskInfo, false), - null, null)); + newList.add(new GroupTask(mHomeTask, null, null)); taskGroups = newList; } } @@ -193,7 +189,7 @@ public class FallbackRecentsView extends RecentsView