From 7d82f0a2b83a041d49780f61125c32725552521a Mon Sep 17 00:00:00 2001 From: Pablo Gamito Date: Thu, 30 Mar 2023 10:38:05 +0000 Subject: [PATCH] Prevent flicker on orientation change while starting It's common for apps to set the orentation in the on fragment attach or some otherway that delays that orentation change to a point when the opening activity transition has alrady been started. In this case the orientation change will trigger an activity restart but the position of the activity and letterboxing are updated before the activity re-draws itself with the new configuration, which leads to a flicker. Test: use test app from bug and make sure issue doesn't reproduce Bug: 271276745 Change-Id: I303721bb33bbca32e2e65b7c6f1e0905771d221a --- .../com/android/server/wm/ActivityRecord.java | 4 +-- .../server/wm/LetterboxUiController.java | 32 ++++++++++++++++--- .../com/android/server/wm/WindowState.java | 13 ++++++-- .../server/wm/LetterboxUiControllerTest.java | 4 +-- .../android/server/wm/SizeCompatTests.java | 2 ++ 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index acadf4a03db52..2f7673a309a2e 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4025,7 +4025,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void finishRelaunching() { - mLetterboxUiController.setRelauchingAfterRequestedOrientationChanged(false); + mLetterboxUiController.setRelaunchingAfterRequestedOrientationChanged(false); mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this); if (mPendingRelaunchCount > 0) { @@ -9420,7 +9420,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mRelaunchReason = RELAUNCH_REASON_NONE; } if (isRequestedOrientationChanged) { - mLetterboxUiController.setRelauchingAfterRequestedOrientationChanged(true); + mLetterboxUiController.setRelaunchingAfterRequestedOrientationChanged(true); } if (mState == PAUSING) { // A little annoying: we are waiting for this activity to finish pausing. Let's not diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index da4d3cb79002c..d07c103e072c0 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -241,7 +241,9 @@ final class LetterboxUiController { @Nullable private final Boolean mBooleanPropertyFakeFocus; - private boolean mIsRelauchingAfterRequestedOrientationChanged; + private boolean mIsRelaunchingAfterRequestedOrientationChanged; + + private boolean mLastShouldShowLetterboxUi; private boolean mDoubleTapEvent; @@ -394,7 +396,7 @@ final class LetterboxUiController { ::isPolicyForIgnoringRequestedOrientationEnabled, mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled, mBooleanPropertyIgnoreRequestedOrientation)) { - if (mIsRelauchingAfterRequestedOrientationChanged) { + if (mIsRelaunchingAfterRequestedOrientationChanged) { Slog.w(TAG, "Ignoring orientation update to " + screenOrientationToString(requestedOrientation) + " due to relaunching after setRequestedOrientation for " @@ -483,8 +485,8 @@ final class LetterboxUiController { * Sets whether an activity is relaunching after the app has called {@link * android.app.Activity#setRequestedOrientation}. */ - void setRelauchingAfterRequestedOrientationChanged(boolean isRelaunching) { - mIsRelauchingAfterRequestedOrientationChanged = isRelaunching; + void setRelaunchingAfterRequestedOrientationChanged(boolean isRelaunching) { + mIsRelaunchingAfterRequestedOrientationChanged = isRelaunching; } /** @@ -1161,12 +1163,28 @@ final class LetterboxUiController { @VisibleForTesting boolean shouldShowLetterboxUi(WindowState mainWindow) { - return (mActivityRecord.isInLetterboxAnimation() || isSurfaceVisible(mainWindow)) + if (mIsRelaunchingAfterRequestedOrientationChanged || !isSurfaceReadyToShow(mainWindow)) { + return mLastShouldShowLetterboxUi; + } + + final boolean shouldShowLetterboxUi = + (mActivityRecord.isInLetterboxAnimation() || isSurfaceVisible(mainWindow)) && mainWindow.areAppWindowBoundsLetterboxed() // Check for FLAG_SHOW_WALLPAPER explicitly instead of using // WindowContainer#showWallpaper because the later will return true when this // activity is using blurred wallpaper for letterbox background. && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0; + + mLastShouldShowLetterboxUi = shouldShowLetterboxUi; + + return shouldShowLetterboxUi; + } + + @VisibleForTesting + boolean isSurfaceReadyToShow(WindowState mainWindow) { + return mainWindow.isDrawn() // Regular case + // Waiting for relayoutWindow to call preserveSurface + || mainWindow.isDragResizeChanged(); } @VisibleForTesting @@ -1302,6 +1320,10 @@ final class LetterboxUiController { return null; } + boolean getIsRelaunchingAfterRequestedOrientationChanged() { + return mIsRelaunchingAfterRequestedOrientationChanged; + } + private void adjustBoundsForTaskbar(final WindowState mainWindow, final Rect bounds) { // Rounded corners should be displayed above the taskbar. When taskbar is hidden, // an insets frame is equal to a navigation bar which shouldn't affect position of diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 4f3ac48d2ca33..2d21e710bca8e 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5596,8 +5596,17 @@ class WindowState extends WindowContainer implements WindowManagerP if (surfaceInsetsChanged) { mLastSurfaceInsets.set(mAttrs.surfaceInsets); } - if (surfaceSizeChanged && mWinAnimator.getShown() && !canPlayMoveAnimation() - && okToDisplay() && mSyncState == SYNC_STATE_NONE) { + final boolean surfaceResizedWithoutMoveAnimation = surfaceSizeChanged + && mWinAnimator.getShown() && !canPlayMoveAnimation() && okToDisplay() + && mSyncState == SYNC_STATE_NONE; + final ActivityRecord activityRecord = getActivityRecord(); + // If this window belongs to an activity that is relaunching due to an orientation + // change then delay the position update until it has redrawn to avoid any flickers. + final boolean isLetterboxedAndRelaunching = activityRecord != null + && activityRecord.areBoundsLetterboxed() + && activityRecord.mLetterboxUiController + .getIsRelaunchingAfterRequestedOrientationChanged(); + if (surfaceResizedWithoutMoveAnimation || isLetterboxedAndRelaunching) { applyWithNextDraw(mSetSurfacePositionConsumer); } else { mSetSurfacePositionConsumer.accept(t); diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index 8f31641c66f41..3dd7b73fe6f55 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -145,7 +145,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { mActivity = setUpActivityWithComponent(); mController = new LetterboxUiController(mWm, mActivity); prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch(); - mController.setRelauchingAfterRequestedOrientationChanged(false); + mController.setRelaunchingAfterRequestedOrientationChanged(false); spyOn(mDisplayContent.mDisplayRotationCompatPolicy); doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy) @@ -864,7 +864,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { private void prepareActivityThatShouldIgnoreRequestedOrientationDuringRelaunch() { doReturn(true).when(mLetterboxConfiguration) .isPolicyForIgnoringRequestedOrientationEnabled(); - mController.setRelauchingAfterRequestedOrientationChanged(true); + mController.setRelaunchingAfterRequestedOrientationChanged(true); } private ActivityRecord setUpActivityWithComponent() { diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index e279145ac3d57..83d716dd2d2f1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -738,6 +738,8 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(window, mActivity.findMainWindow()); spyOn(mActivity.mLetterboxUiController); + doReturn(true).when(mActivity.mLetterboxUiController) + .isSurfaceReadyToShow(any()); doReturn(true).when(mActivity.mLetterboxUiController) .isSurfaceVisible(any());