diff options
6 files changed, 87 insertions, 18 deletions
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fcbc7564cabb..0363ea0e7512 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1573,7 +1573,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // the heavy operations. This also benefits that the states of multiple activities // are handled together. r.linkFixedRotationTransform(prevRotatedLaunchingApp); - setFixedRotationLaunchingAppUnchecked(r, rotation); + if (r != mFixedRotationTransitionListener.mAnimatingRecents) { + // Only update the record for normal activity so the display orientation can be + // updated when the transition is done if it becomes the top. And the case of + // recents can be handled when the recents animation is finished. + setFixedRotationLaunchingAppUnchecked(r, rotation); + } return; } @@ -5642,6 +5647,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ private ActivityRecord mAnimatingRecents; + /** Whether {@link #mAnimatingRecents} is going to be the top activity. */ + private boolean mRecentsWillBeTop; + /** * If the recents activity has a fixed orientation which is different from the current top * activity, it will be rotated before being shown so we avoid a screen rotation animation @@ -5667,10 +5675,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * If {@link #mAnimatingRecents} still has fixed rotation, it should be moved to top so we * don't clear {@link #mFixedRotationLaunchingApp} that will be handled by transition. */ - void onFinishRecentsAnimation(boolean moveRecentsToBack) { + void onFinishRecentsAnimation() { final ActivityRecord animatingRecents = mAnimatingRecents; + final boolean recentsWillBeTop = mRecentsWillBeTop; mAnimatingRecents = null; - if (!moveRecentsToBack) { + mRecentsWillBeTop = false; + if (recentsWillBeTop) { // The recents activity will be the top, such as staying at recents list or // returning to home (if home and recents are the same activity). return; @@ -5693,6 +5703,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + void notifyRecentsWillBeTop() { + mRecentsWillBeTop = true; + } + /** * Return {@code true} if there is an ongoing animation to the "Recents" activity and this * activity as a fixed orientation so shouldn't be rotated. @@ -5713,6 +5727,14 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (r == null || r == mAnimatingRecents) { return; } + if (mAnimatingRecents != null && mRecentsWillBeTop) { + // The activity is not the recents and it should be moved to back later, so it is + // better to keep its current appearance for the next transition. Otherwise the + // display orientation may be updated too early and the layout procedures at the + // end of finishing recents animation is skipped. That causes flickering because + // the surface of closing app hasn't updated to invisible. + return; + } if (mFixedRotationLaunchingApp == null) { // In most cases this is a no-op if the activity doesn't have fixed rotation. // Otherwise it could be from finishing recents animation while the display has diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index a2b295a609cd..65db23c714a9 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -702,6 +702,11 @@ public class RecentsAnimationController implements DeathRecipient { "cleanupAnimation(): Notify animation finished mPendingAnimations=%d " + "reorderMode=%d", mPendingAnimations.size(), reorderMode); + if (reorderMode != REORDER_MOVE_TO_ORIGINAL_POSITION) { + // Notify the state at the beginning because the removeAnimation may notify the + // transition is finished. This is a signal that there will be a next transition. + mDisplayContent.mFixedRotationTransitionListener.notifyRecentsWillBeTop(); + } for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) { @@ -742,8 +747,7 @@ public class RecentsAnimationController implements DeathRecipient { mTargetActivityRecord.token); } } - mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation( - reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION /* moveRecentsToBack */); + mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(); // Notify that the animation has ended if (mStatusBar != null) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 86aacf308068..1716dcd5ee16 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -531,7 +531,7 @@ class WindowToken extends WindowContainer<WindowState> { void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames, Configuration config) { if (mFixedRotationTransformState != null) { - return; + cleanUpFixedRotationTransformState(true /* replacing */); } mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames, new Configuration(config), mDisplayContent.getRotation()); @@ -548,13 +548,13 @@ class WindowToken extends WindowContainer<WindowState> { * one. This takes the same effect as {@link #applyFixedRotationTransform}. */ void linkFixedRotationTransform(WindowToken other) { - if (mFixedRotationTransformState != null) { - return; - } final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState; - if (fixedRotationState == null) { + if (fixedRotationState == null || mFixedRotationTransformState == fixedRotationState) { return; } + if (mFixedRotationTransformState != null) { + cleanUpFixedRotationTransformState(true /* replacing */); + } mFixedRotationTransformState = fixedRotationState; fixedRotationState.mAssociatedTokens.add(this); onConfigurationChanged(getParent().getConfiguration()); @@ -609,11 +609,17 @@ class WindowToken extends WindowContainer<WindowState> { // The state is cleared at the end, because it is used to indicate that other windows can // use seamless rotation when applying rotation to display. for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { - state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState(); + state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState( + false /* replacing */); } } - private void cleanUpFixedRotationTransformState() { + private void cleanUpFixedRotationTransformState(boolean replacing) { + if (replacing && mFixedRotationTransformState.mAssociatedTokens.size() > 1) { + // The state is not only used by self. Make sure to leave the influence by others. + mFixedRotationTransformState.mAssociatedTokens.remove(this); + mFixedRotationTransformState.mRotatedContainers.remove(this); + } mFixedRotationTransformState = null; notifyFixedRotationTransform(false /* enabled */); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index cbccfb3725e9..dc838f1bb288 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1282,7 +1282,7 @@ public class DisplayContentTests extends WindowTestsBase { assertFalse(displayRotation.updateRotationUnchecked(false)); // Rotation can be updated if the recents animation is finished. - mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(false); + mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(); assertTrue(displayRotation.updateRotationUnchecked(false)); // Rotation can be updated if the recents animation is animating but it is not on top, e.g. diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 8e85e7b96d1f..f2771175b523 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -363,12 +363,14 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertFalse(homeActivity.hasFixedRotationTransform()); } - @Test - public void testClearFixedRotationLaunchingAppAfterCleanupAnimation() { + private ActivityRecord prepareFixedRotationLaunchingAppWithRecentsAnim() { final ActivityRecord homeActivity = createHomeActivity(); homeActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); final ActivityRecord activity = createActivityRecord(mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + // Add a window so it can be animated by the recents. + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); + activity.addWindow(win); // Assume an activity is launching to different rotation. mDefaultDisplay.setFixedRotationLaunchingApp(activity, (mDefaultDisplay.getRotation() + 1) % 4); @@ -379,6 +381,14 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { // Before the transition is done, the recents animation is triggered. initializeRecentsAnimationController(mController, homeActivity); assertFalse(homeActivity.hasFixedRotationTransform()); + assertTrue(mController.isAnimatingTask(activity.getTask())); + + return activity; + } + + @Test + public void testClearFixedRotationLaunchingAppAfterCleanupAnimation() { + final ActivityRecord activity = prepareFixedRotationLaunchingAppWithRecentsAnim(); // Simulate giving up the swipe up gesture to keep the original activity as top. mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION); @@ -388,6 +398,21 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { } @Test + public void testKeepFixedRotationWhenMovingRecentsToTop() { + final ActivityRecord activity = prepareFixedRotationLaunchingAppWithRecentsAnim(); + // Assume a transition animation has started running before recents animation. Then the + // activity will receive onAnimationFinished that notifies app transition finished when + // removing the recents animation of task. + activity.getTask().getAnimationSources().add(activity); + + // Simulate swiping to home/recents before the transition is done. + mController.cleanupAnimation(REORDER_MOVE_TO_TOP); + // The rotation transform should be preserved. In real case, it will be cleared by the next + // move-to-top transition. + assertTrue(activity.hasFixedRotationTransform()); + } + + @Test public void testWallpaperHasFixedRotationApplied() { mWm.setRecentsAnimationController(mController); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 23a097eb0c7c..0896db4b5532 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -139,6 +140,8 @@ public class WindowTokenTests extends WindowTestsBase { public void testFinishFixedRotationTransform() { final WindowToken appToken = mAppWindow.mToken; final WindowToken wallpaperToken = mWallpaperWindow.mToken; + final WindowToken testToken = + WindowTestUtils.createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent); final Configuration config = new Configuration(mDisplayContent.getConfiguration()); final int originalRotation = config.windowConfiguration.getRotation(); final int targetRotation = (originalRotation + 1) % 4; @@ -151,11 +154,20 @@ public class WindowTokenTests extends WindowTestsBase { assertEquals(targetRotation, appToken.getWindowConfiguration().getRotation()); assertEquals(targetRotation, wallpaperToken.getWindowConfiguration().getRotation()); - // The display doesn't rotate, the transformation will be canceled. - mAppWindow.mToken.finishFixedRotationTransform(); + testToken.applyFixedRotationTransform(mDisplayInfo, mDisplayContent.mDisplayFrames, config); + // The wallpaperToken was linked to appToken, this should make it link to testToken. + wallpaperToken.linkFixedRotationTransform(testToken); - // The window tokens should restore to the original rotation. + // Assume the display doesn't rotate, the transformation will be canceled. + appToken.finishFixedRotationTransform(); + + // The appToken should restore to the original rotation. assertEquals(originalRotation, appToken.getWindowConfiguration().getRotation()); + // The wallpaperToken is linked to testToken, it should keep the target rotation. + assertNotEquals(originalRotation, wallpaperToken.getWindowConfiguration().getRotation()); + + testToken.finishFixedRotationTransform(); + // The rotation of wallpaperToken should be restored because its linked state is finished. assertEquals(originalRotation, wallpaperToken.getWindowConfiguration().getRotation()); } |