diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index 62bd72f393cc1..138910cb9820c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -315,6 +315,17 @@ public class ActivityManagerWrapper { } } + /** + * Cancels the remote recents animation started from {@link #startRecentsActivity}. + */ + public void cancelRecentsAnimation() { + try { + ActivityManager.getService().cancelRecentsAnimation(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel recents animation", e); + } + } + /** * Starts a task from Recents. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index 9d1c1e8b4b77f..98bebec8511e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -52,6 +52,7 @@ import com.android.systemui.Dependency; import com.android.systemui.OverviewProxyService; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface; +import com.android.systemui.shared.system.ActivityManagerWrapper; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; @@ -269,11 +270,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { if (doIt) { // If there was a pending remote recents animation, then we need to // cancel the animation now before we handle the button itself - try { - ActivityManager.getService().cancelRecentsAnimation(); - } catch (RemoteException e) { - Log.e(TAG, "Could not cancel recents animation", e); - } + ActivityManagerWrapper.getInstance().cancelRecentsAnimation(); sendEvent(KeyEvent.ACTION_UP, 0); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); } else { diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java index db4e09fb79b8a..6dcf04193c8e6 100644 --- a/services/core/java/com/android/server/am/RecentsAnimation.java +++ b/services/core/java/com/android/server/am/RecentsAnimation.java @@ -28,7 +28,9 @@ import android.app.ActivityOptions; import android.content.ComponentName; import android.content.Intent; import android.os.Handler; +import android.os.RemoteException; import android.os.Trace; +import android.util.Slog; import android.view.IRecentsAnimationRunner; import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; import com.android.server.wm.WindowManagerService; @@ -63,6 +65,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks { mHandler = new Handler(mStackSupervisor.mLooper); mWindowManager = wm; mUserController = userController; + mCancelAnimationRunnable = () -> { // The caller has not finished the animation in a predefined amount of time, so // force-cancel the animation @@ -73,13 +76,33 @@ class RecentsAnimation implements RecentsAnimationCallbacks { void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner, ComponentName recentsComponent, int recentsUid) { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity"); + + if (!mWindowManager.canStartRecentsAnimation()) { + notifyAnimationCancelBeforeStart(recentsAnimationRunner); + return; + } + + // If the existing home activity is already on top, then cancel + ActivityRecord homeActivity = mStackSupervisor.getHomeActivity(); + final boolean hasExistingHomeActivity = homeActivity != null; + if (hasExistingHomeActivity) { + final ActivityDisplay display = homeActivity.getDisplay(); + mRestoreHomeBehindStack = display.getStackAboveHome(); + if (mRestoreHomeBehindStack == null) { + notifyAnimationCancelBeforeStart(recentsAnimationRunner); + return; + } + } + mWindowManager.deferSurfaceLayout(); try { - // Cancel the previous recents animation if necessary - mWindowManager.cancelRecentsAnimation(); - final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null; - if (!hasExistingHomeActivity) { + final ActivityDisplay display; + if (hasExistingHomeActivity) { + // Move the home activity into place for the animation if it is not already top most + display = homeActivity.getDisplay(); + display.moveHomeStackBehindBottomMostVisibleStack(); + } else { // No home activity final ActivityOptions opts = ActivityOptions.makeBasic(); opts.setLaunchActivityType(ACTIVITY_TYPE_HOME); @@ -95,25 +118,20 @@ class RecentsAnimation implements RecentsAnimationCallbacks { .execute(); mWindowManager.prepareAppTransition(TRANSIT_NONE, false); + homeActivity = mStackSupervisor.getHomeActivity(); + display = homeActivity.getDisplay(); + // TODO: Maybe wait for app to draw in this particular case? } - final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity(); - final ActivityDisplay display = homeActivity.getDisplay(); - - // Save the initial position of the home activity stack to be restored to after the - // animation completes - mRestoreHomeBehindStack = hasExistingHomeActivity - ? display.getStackAboveHome() - : null; - - // Move the home activity into place for the animation - display.moveHomeStackBehindBottomMostVisibleStack(); - // Mark the home activity as launch-behind to bump its visibility for the // duration of the gesture that is driven by the recents component homeActivity.mLaunchTaskBehind = true; + // Post a timeout for the animation. This needs to happen before initializing the + // recents animation on the WM side since we may decide to cancel the animation there + mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT); + // Fetch all the surface controls and pass them to the client to get the animation // started mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, @@ -122,9 +140,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks { // If we updated the launch-behind state, update the visibility of the activities after // we fetch the visible tasks to be controlled by the animation mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); - - // Post a timeout for the animation - mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT); } finally { mWindowManager.continueSurfaceLayout(); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); @@ -178,4 +193,15 @@ class RecentsAnimation implements RecentsAnimationCallbacks { }); } } + + /** + * Called only when the animation should be canceled prior to starting. + */ + private void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) { + try { + recentsAnimationRunner.onAnimationCanceled(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to cancel recents animation before start", e); + } + } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index e869f582ed1f7..6c3beaf24f1d6 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -59,6 +59,7 @@ public class RecentsAnimationController { private final IRecentsAnimationRunner mRunner; private final RecentsAnimationCallbacks mCallbacks; private final ArrayList mPendingAnimations = new ArrayList<>(); + private final int mDisplayId; // The recents component app token that is shown behind the visibile tasks private AppWindowToken mHomeAppToken; @@ -159,8 +160,6 @@ public class RecentsAnimationController { }; /** - * Initializes a new RecentsAnimationController. - * * @param remoteAnimationRunner The remote runner which should be notified when the animation is * ready to start or has been canceled * @param callbacks Callbacks to be made when the animation finishes @@ -171,16 +170,19 @@ public class RecentsAnimationController { mService = service; mRunner = remoteAnimationRunner; mCallbacks = callbacks; + mDisplayId = displayId; + } - final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); - final ArrayList visibleTasks = dc.getVisibleTasks(); - if (visibleTasks.isEmpty()) { - cancelAnimation(); - return; - } - + /** + * Initializes the recents animation controller. This is a separate call from the constructor + * because it may call cancelAnimation() which needs to properly clean up the controller + * in the window manager. + */ + public void initialize() { // Make leashes for each of the visible tasks and add it to the recents animation to be // started + final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); + final ArrayList visibleTasks = dc.getVisibleTasks(); final int taskCount = visibleTasks.size(); for (int i = 0; i < taskCount; i++) { final Task task = visibleTasks.get(i); @@ -193,6 +195,12 @@ public class RecentsAnimationController { addAnimation(task); } + // Skip the animation if there is nothing to animate + if (mPendingAnimations.isEmpty()) { + cancelAnimation(); + return; + } + // Adjust the wallpaper visibility for the showing home activity final AppWindowToken recentsComponentAppToken = dc.getHomeStack().getTopChild().getTopFullscreenAppToken(); @@ -222,8 +230,10 @@ public class RecentsAnimationController { } void startAnimation() { - if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart); - if (!mPendingStart) { + if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart + + " mCanceled=" + mCanceled); + if (!mPendingStart || mCanceled) { + // Skip starting if we've already started or canceled the animation return; } try { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c2ed2ae302277..e68fbdbac1a9a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2680,6 +2680,7 @@ public class WindowManagerService extends IWindowManager.Stub cancelRecentsAnimation(); mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner, callbacks, displayId); + mRecentsAnimationController.initialize(); } } @@ -2687,6 +2688,19 @@ public class WindowManagerService extends IWindowManager.Stub return mRecentsAnimationController; } + /** + * @return Whether the next recents animation can continue to start. Called from + * {@link RecentsAnimation#startRecentsActivity}. + */ + public boolean canStartRecentsAnimation() { + synchronized (mWindowMap) { + if (mAppTransition.isTransitionSet()) { + return false; + } + return true; + } + } + public void cancelRecentsAnimation() { synchronized (mWindowMap) { if (mRecentsAnimationController != null) {