From 4b8c659f011e8a3a1adb965f4fe1b665bb6c630f Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Tue, 5 Jan 2021 20:00:51 +0800 Subject: [PATCH] Defer window removal including app transition The mWinAnimator.applyAnimationLocked in removeIfPossible may not apply window level animation, but the activity may be running closing transition. So the window shouldn't be removed immediately, otherwise the animation is disappeared. Bug: 176471456 Test: WindowStateTests#testDeferredRemovalByAnimating Test: Launch a translucent activity and finish it, check the slide-down style exit animation can play with the declared duration. Change-Id: I4227a89108d16fc25f912b96ae83cf125fec4b16 --- .../com/android/server/wm/WindowState.java | 10 +++++++--- .../android/server/wm/WindowStateTests.java | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index d1ea9a2e3cd8c..de4ced5bcd290 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -131,6 +131,7 @@ import static com.android.server.wm.MoveAnimationSpecProto.DURATION_MS; import static com.android.server.wm.MoveAnimationSpecProto.FROM; import static com.android.server.wm.MoveAnimationSpecProto.TO; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; @@ -2281,7 +2282,8 @@ class WindowState extends WindowContainer implements WindowManagerP mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit); } } - final boolean isAnimating = isAnimating(TRANSITION | PARENTS) + final boolean isAnimating = mAnimatingExit || isAnimating(TRANSITION | PARENTS, + ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION) && (mActivityRecord == null || !mActivityRecord.isWaitingForTransitionStart()); final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null && mActivityRecord.isLastWindow(this); @@ -2290,8 +2292,10 @@ class WindowState extends WindowContainer implements WindowManagerP // Also, If isn't the an animating starting window that is the last window in the app. // We allow the removal of the non-animating starting window now as there is no // additional window or animation that will trigger its removal. - if (mWinAnimator.getShown() && mAnimatingExit - && (!lastWindowIsStartingWindow || isAnimating)) { + if (mWinAnimator.getShown() && !lastWindowIsStartingWindow && isAnimating) { + // Make isSelfOrAncestorWindowAnimatingExit return true so onExitAnimationDone + // can proceed to remove this window. + mAnimatingExit = true; // The exit animation is running or should run... wait for it! ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Not removing %s due to exit animation", this); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 7db2fc9a899f9..263aa194a1080 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -447,6 +447,26 @@ public class WindowStateTests extends WindowTestsBase { assertTrue(child2.isSelfOrAncestorWindowAnimatingExit()); } + @Test + public void testDeferredRemovalByAnimating() { + final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); + makeWindowVisible(appWindow); + spyOn(appWindow.mWinAnimator); + doReturn(true).when(appWindow.mWinAnimator).getShown(); + final AnimationAdapter animation = mock(AnimationAdapter.class); + final ActivityRecord activity = appWindow.mActivityRecord; + activity.startAnimation(appWindow.getPendingTransaction(), + animation, false /* hidden */, SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION); + + appWindow.removeIfPossible(); + assertTrue(appWindow.mAnimatingExit); + assertFalse(appWindow.mRemoved); + + activity.cancelAnimation(); + assertFalse(appWindow.mAnimatingExit); + assertTrue(appWindow.mRemoved); + } + @Test public void testLayoutSeqResetOnReparent() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app");