From b314926dcf3167fa0d7f410d07fa8a208394ce45 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Tue, 7 Feb 2023 06:41:02 +0000 Subject: [PATCH] Fix no back animations when the target window is animating Let the back navigation could defer the back animation and could handle it after the running transition finished. The participated targets in back navigation could be calculated and hide when the transition is running. To prevent these targets and their parents being invisible and reset transform when transition finished, this CL will handle the deferred animation when transition finished and call animation cancel if the targets are not participated. Bug: 264716523 Test: launch activity and quickly trigger back navigation Change-Id: I2f3ceb28c0973d1c417c355788bc53eb0702be4f --- data/etc/services.core.protolog.json | 12 ++ .../shell/back/BackAnimationController.java | 17 +- .../wm/shell/back/BackAnimationRunner.java | 13 ++ .../server/wm/BackNavigationController.java | 192 ++++++++++++------ .../com/android/server/wm/Transition.java | 3 + 5 files changed, 173 insertions(+), 64 deletions(-) diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 3d81d37aff20f..05e17720b1752 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -271,6 +271,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", "at": "com\/android\/server\/wm\/AppTransition.java" }, + "-1868518158": { + "message": "Pending back animation due to another animation is running", + "level": "WARN", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "-1868124841": { "message": "screenOnEarly=%b, awake=%b, currentAppOrientation=%d, orientationSensorEnabled=%b, keyguardDrawComplete=%b, windowManagerDrawComplete=%b", "level": "VERBOSE", @@ -1549,6 +1555,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DragState.java" }, + "-692907078": { + "message": "Handling the deferred animation after transition finished", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "-677449371": { "message": "moveTaskToRootTask: moving task=%d to rootTaskId=%d toTop=%b", "level": "DEBUG", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index b3fff1d052632..0b87598903597 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -552,6 +552,9 @@ public class BackAnimationController implements RemoteCallable { + final BackAnimationRunner runner = mAnimationDefinition.get( + mBackNavigationInfo.getType()); + if (runner == null) { + return; + } + runner.cancelAnimation(); + if (!mBackGestureStarted) { + invokeOrCancelBack(); + } + }); + } }; mBackAnimationAdapter = new BackAnimationAdapter(runner); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java index d70b8f53a9115..82c523f337dbb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java @@ -41,6 +41,9 @@ class BackAnimationRunner { // Whether we are waiting to receive onAnimationStart private boolean mWaitingAnimation; + /** True when the back animation is cancelled */ + private boolean mAnimationCancelled; + BackAnimationRunner(@NonNull IOnBackInvokedCallback callback, @NonNull IRemoteAnimationRunner runner) { mCallback = callback; @@ -81,9 +84,19 @@ class BackAnimationRunner { void startGesture() { mWaitingAnimation = true; + mAnimationCancelled = false; } boolean isWaitingAnimation() { return mWaitingAnimation; } + + void cancelAnimation() { + mWaitingAnimation = false; + mAnimationCancelled = true; + } + + boolean isAnimationCancelled() { + return mAnimationCancelled; + } } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index a229fc5b00d37..2d45dc20c9635 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -75,6 +75,11 @@ class BackNavigationController { private final ArrayList mTmpOpenApps = new ArrayList<>(); private final ArrayList mTmpCloseApps = new ArrayList<>(); + // This will be set if the back navigation is in progress and the current transition is still + // running. The pending animation builder will do the animation stuff includes creating leashes, + // re-parenting leashes and set launch behind, etc. Will be handled when transition finished. + private AnimationHandler.ScheduleAnimationBuilder mPendingAnimationBuilder; + /** * true if the back predictability feature is enabled */ @@ -305,25 +310,26 @@ class BackNavigationController { || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) && adapter != null; - // Only prepare animation if no leash has been created (no animation is running). - // TODO(b/241808055): Cancel animation when preparing back animation. - if (prepareAnimation - && (removedWindowContainer.hasCommittedReparentToAnimationLeash() - || removedWindowContainer.mTransitionController.inTransition())) { - Slog.w(TAG, "Can't prepare back animation due to another animation is running."); - prepareAnimation = false; - } - if (prepareAnimation) { - mPendingAnimation = mAnimationHandler.scheduleAnimation(backType, adapter, - currentTask, prevTask, currentActivity, prevActivity); - prepareAnimation = mPendingAnimation != null; - mBackAnimationInProgress = prepareAnimation; - if (prepareAnimation) { - mWindowManagerService.mWindowPlacerLocked.requestTraversal(); - if (mShowWallpaper) { - currentTask.getDisplayContent().mWallpaperController - .adjustWallpaperWindows(); + final AnimationHandler.ScheduleAnimationBuilder builder = + mAnimationHandler.prepareAnimation(backType, adapter, + currentTask, prevTask, currentActivity, prevActivity); + mBackAnimationInProgress = builder != null; + if (mBackAnimationInProgress) { + if (removedWindowContainer.hasCommittedReparentToAnimationLeash() + || removedWindowContainer.mTransitionController.inTransition() + || mWindowManagerService.mSyncEngine.hasPendingSyncSets()) { + ProtoLog.w(WM_DEBUG_BACK_PREVIEW, + "Pending back animation due to another animation is running"); + mPendingAnimationBuilder = builder; + // Current transition is still running, we have to defer the hiding to the + // client process to prevent the unexpected relayout when handling the back + // animation. + if (prevActivity != null) { + prevActivity.setDeferHidingClient(true); + } + } else { + scheduleAnimation(builder); } } } @@ -345,6 +351,15 @@ class BackNavigationController { return isWaitBackTransition() || mNavigationMonitor.isMonitoring(); } + private void scheduleAnimation(@NonNull AnimationHandler.ScheduleAnimationBuilder builder) { + mPendingAnimation = builder.build(); + mWindowManagerService.mWindowPlacerLocked.requestTraversal(); + if (mShowWallpaper) { + mWindowManagerService.getDefaultDisplayContentLocked().mWallpaperController + .adjustWallpaperWindows(); + } + } + private boolean isWaitBackTransition() { return mAnimationHandler.mComposed && mAnimationHandler.mWaitTransition; } @@ -526,6 +541,57 @@ class BackNavigationController { mAnimationHandler.clearBackAnimateTarget(cleanupTransaction); } + /** + * Handle the pending animation when the running transition finished. + * @param targets The final animation targets derived in transition. + */ + boolean handleDeferredBackAnimation(@NonNull ArrayList targets) { + if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) { + return false; + } + + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Handling the deferred animation after transition finished"); + + // Show the target surface and its parents to prevent it or its parents hidden when + // the transition finished. + // The target could be affected by transition when : + // Open transition -> the open target in back navigation + // Close transition -> the close target in back navigation. + boolean hasTarget = false; + final SurfaceControl.Transaction t = + mPendingAnimationBuilder.mCloseTarget.getPendingTransaction(); + for (int i = 0; i < targets.size(); i++) { + final WindowContainer wc = targets.get(i).mContainer; + if (wc.asActivityRecord() == null && wc.asTask() == null) { + continue; + } else if (!mPendingAnimationBuilder.containTarget(wc)) { + continue; + } + + hasTarget = true; + t.show(wc.getSurfaceControl()); + } + + if (!hasTarget) { + // Skip if no target participated in current finished transition. + Slog.w(TAG, "Finished transition didn't include the targets" + + " open: " + mPendingAnimationBuilder.mOpenTarget + + " close: " + mPendingAnimationBuilder.mCloseTarget); + try { + mPendingAnimationBuilder.mBackAnimationAdapter.getRunner().onAnimationCancelled(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + mPendingAnimationBuilder = null; + return false; + } + + scheduleAnimation(mPendingAnimationBuilder); + mPendingAnimationBuilder = null; + return true; + } + /** * Create and handling animations status for an open/close animation targets. */ @@ -638,6 +704,7 @@ class BackNavigationController { if (open) { return wc == mOpenAdaptor.mTarget || mOpenAdaptor.mTarget.hasChild(wc); } + if (mSwitchType == TASK_SWITCH) { return wc == mCloseAdaptor.mTarget || (wc.asTask() != null && wc.hasChild(mCloseAdaptor.mTarget)); @@ -841,23 +908,22 @@ class BackNavigationController { } } - Runnable scheduleAnimation(int backType, BackAnimationAdapter adapter, + ScheduleAnimationBuilder prepareAnimation(int backType, BackAnimationAdapter adapter, Task currentTask, Task previousTask, ActivityRecord currentActivity, ActivityRecord previousActivity) { switch (backType) { case BackNavigationInfo.TYPE_RETURN_TO_HOME: return new ScheduleAnimationBuilder(backType, adapter) .setIsLaunchBehind(true) - .setComposeTarget(currentTask, previousTask) - .build(); + .setComposeTarget(currentTask, previousTask); case BackNavigationInfo.TYPE_CROSS_ACTIVITY: return new ScheduleAnimationBuilder(backType, adapter) .setComposeTarget(currentActivity, previousActivity) - .setOpeningSnapshot(getActivitySnapshot(previousActivity)).build(); + .setOpeningSnapshot(getActivitySnapshot(previousActivity)); case BackNavigationInfo.TYPE_CROSS_TASK: return new ScheduleAnimationBuilder(backType, adapter) .setComposeTarget(currentTask, previousTask) - .setOpeningSnapshot(getTaskSnapshot(previousTask)).build(); + .setOpeningSnapshot(getTaskSnapshot(previousTask)); } return null; } @@ -891,6 +957,11 @@ class BackNavigationController { return this; } + boolean containTarget(@NonNull WindowContainer wc) { + return wc == mOpenTarget || wc == mCloseTarget + || wc.hasChild(mOpenTarget) || wc.hasChild(mCloseTarget); + } + Runnable build() { if (mOpenTarget == null || mCloseTarget == null) { return null; @@ -967,49 +1038,43 @@ class BackNavigationController { } }; } - - private void setLaunchBehind(ActivityRecord activity) { - if (activity == null) { - return; - } - if (!activity.isVisibleRequested()) { - activity.setVisibility(true); - } - activity.mLaunchTaskBehind = true; - - // Handle fixed rotation launching app. - final DisplayContent dc = activity.mDisplayContent; - dc.rotateInDifferentOrientationIfNeeded(activity); - if (activity.hasFixedRotationTransform()) { - // Set the record so we can recognize it to continue to update display - // orientation if the previous activity becomes the top later. - dc.setFixedRotationLaunchingApp(activity, - activity.getWindowConfiguration().getRotation()); - } - - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, - "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity); - activity.mTaskSupervisor.mStoppingActivities.remove(activity); - activity.getDisplayContent().ensureActivitiesVisible(null /* starting */, - 0 /* configChanges */, false /* preserveWindows */, true); - } - private void restoreLaunchBehind(ActivityRecord activity) { - if (activity == null) { - return; - } - - activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); - - // Restore the launch-behind state. - activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); - activity.mLaunchTaskBehind = false; - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, - "Setting Activity.mLauncherTaskBehind to false. Activity=%s", - activity); - } } } + private static void setLaunchBehind(@NonNull ActivityRecord activity) { + if (!activity.isVisibleRequested()) { + activity.setVisibility(true); + } + activity.mLaunchTaskBehind = true; + + // Handle fixed rotation launching app. + final DisplayContent dc = activity.mDisplayContent; + dc.rotateInDifferentOrientationIfNeeded(activity); + if (activity.hasFixedRotationTransform()) { + // Set the record so we can recognize it to continue to update display + // orientation if the previous activity becomes the top later. + dc.setFixedRotationLaunchingApp(activity, + activity.getWindowConfiguration().getRotation()); + } + + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity); + activity.mTaskSupervisor.mStoppingActivities.remove(activity); + activity.getDisplayContent().ensureActivitiesVisible(null /* starting */, + 0 /* configChanges */, false /* preserveWindows */, true); + } + + private static void restoreLaunchBehind(@NonNull ActivityRecord activity) { + activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); + + // Restore the launch-behind state. + activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); + activity.mLaunchTaskBehind = false; + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, + "Setting Activity.mLauncherTaskBehind to false. Activity=%s", + activity); + } + void checkAnimationReady(WallpaperController wallpaperController) { if (!mBackAnimationInProgress) { return; @@ -1039,6 +1104,7 @@ class BackNavigationController { mNavigationMonitor.stopMonitor(); mBackAnimationInProgress = false; mShowWallpaper = false; + mPendingAnimationBuilder = null; } private static TaskSnapshot getActivitySnapshot(@NonNull ActivityRecord r) { diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index a68b3cb2a2ea5..dfba9bf1e9db9 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -983,6 +983,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { cleanUpInternal(); mController.updateAnimatingState(mTmpTransaction); mTmpTransaction.apply(); + + // Handle back animation if it's already started. + mController.mAtm.mBackNavigationController.handleDeferredBackAnimation(mTargets); } void abort() {