diff options
| author | 2021-02-22 13:52:07 +0800 | |
|---|---|---|
| committer | 2021-02-22 09:52:46 +0000 | |
| commit | 8fea9c8e897e49950be4d965d44c7301a7ea945b (patch) | |
| tree | 2c0cb25452c31a667e3ddc43e423095c56e238cb | |
| parent | 7e2d8b8b131758523ccf022b935ac51ae47ca469 (diff) | |
Notify client to clear intermediate rotated adjustments
For example: launch a landscape activity while device is portrait,
and then launch another portrait activity before previous transition
is done. If the landscape activity is destroyed before sending the
fixed rotation adjustment for restoration, the adjustment is always
retained by client side app. That causes the Display objects
associated with application Resources to always return rotation from
the adjustment.
This change notifies client to clear the rotated adjustments if the
the display won't be rotated by the next top activity before the
transition is done, so the app can get the correct rotation in time.
The transform will be cleared at the end of transition animation so
the closing animation won't jump cut with rotation change. E.g.
launch an activity in different orientation and press home or back
key before the launch animation is finished.
Also simply a bit for the path of clearing fixed rotation state to
avoid sending duplicated adjustments.
Bug: 161519202
Bug: 168263090
Bug: 177390830
Test: DisplayContentTests#testClearIntermediateFixedRotationAdjustments
Change-Id: Ica074604df3e74eabbdef931531abe51855103e5
Merged-In: Ica074604df3e74eabbdef931531abe51855103e5
3 files changed, 43 insertions, 7 deletions
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2ef98b59768f..f1b110de37ac 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1513,6 +1513,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } final int rotation = rotationForActivityInDifferentOrientation(r); if (rotation == ROTATION_UNDEFINED) { + // The display rotation won't be changed by current top activity. The client side + // adjustments of previous rotated activity should be cleared earlier. Otherwise if + // the current top is in the same process, it may get the rotated state. The transform + // will be cleared later with transition callback to ensure smooth animation. + if (hasTopFixedRotationLaunchingApp()) { + mFixedRotationLaunchingApp.notifyFixedRotationTransform(false /* enabled */); + } return false; } if (!r.getParent().matchParentBounds()) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index d86f6c998baa..9b1526e9b96f 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -622,11 +622,6 @@ class WindowToken extends WindowContainer<WindowState> { state.mIsTransforming = false; if (applyDisplayRotation != null) { applyDisplayRotation.run(); - } else { - // The display will not rotate to the rotation of this container, let's cancel them. - for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { - state.mAssociatedTokens.get(i).cancelFixedRotationTransform(); - } } // 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. @@ -634,11 +629,15 @@ class WindowToken extends WindowContainer<WindowState> { final WindowToken token = state.mAssociatedTokens.get(i); token.mFixedRotationTransformState = null; token.notifyFixedRotationTransform(false /* enabled */); + if (applyDisplayRotation == null) { + // Notify cancellation because the display does not change rotation. + token.cancelFixedRotationTransform(); + } } } /** Notifies application side to enable or disable the rotation adjustment of display info. */ - private void notifyFixedRotationTransform(boolean enabled) { + void notifyFixedRotationTransform(boolean enabled) { FixedRotationAdjustments adjustments = null; // A token may contain windows of the same processes or different processes. The list is // used to avoid sending the same adjustments to a process multiple times. @@ -682,7 +681,6 @@ class WindowToken extends WindowContainer<WindowState> { // The window may be detached or detaching. return; } - notifyFixedRotationTransform(false /* enabled */); final int originalRotation = getWindowConfiguration().getRotation(); onConfigurationChanged(parent.getConfiguration()); onCancelFixedRotationTransform(originalRotation); 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 d99606b704e4..b28220585c5c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -88,6 +88,7 @@ import static org.mockito.Mockito.doCallRealMethod; import android.annotation.SuppressLint; import android.app.ActivityTaskManager; import android.app.WindowConfiguration; +import android.app.servertransaction.FixedRotationAdjustmentsItem; import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.Region; @@ -1346,6 +1347,36 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testClearIntermediateFixedRotationAdjustments() throws RemoteException { + final ActivityRecord activity = new ActivityTestsBase.StackBuilder(mWm.mRoot) + .setDisplay(mDisplayContent).build().getTopMostActivity(); + mDisplayContent.setFixedRotationLaunchingApp(activity, + (mDisplayContent.getRotation() + 1) % 4); + // Create a window so FixedRotationAdjustmentsItem can be sent. + createWindow(null, TYPE_APPLICATION_STARTING, activity, "AppWin"); + final ActivityRecord activity2 = new ActivityTestsBase.StackBuilder(mWm.mRoot) + .setDisplay(mDisplayContent).build().getTopMostActivity(); + activity2.setVisible(false); + clearInvocations(mWm.mAtmService.getLifecycleManager()); + // The first activity has applied fixed rotation but the second activity becomes the top + // before the transition is done and it has the same rotation as display, so the dispatched + // rotation adjustment of first activity must be cleared. + mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(activity2, + false /* checkOpening */); + + final ArgumentCaptor<FixedRotationAdjustmentsItem> adjustmentsCaptor = + ArgumentCaptor.forClass(FixedRotationAdjustmentsItem.class); + verify(mWm.mAtmService.getLifecycleManager(), atLeastOnce()).scheduleTransaction( + eq(activity.app.getThread()), adjustmentsCaptor.capture()); + // The transformation is kept for animation in real case. + assertTrue(activity.hasFixedRotationTransform()); + final FixedRotationAdjustmentsItem clearAdjustments = FixedRotationAdjustmentsItem.obtain( + activity.token, null /* fixedRotationAdjustments */); + // The captor may match other items. The first one must be the item to clear adjustments. + assertEquals(clearAdjustments, adjustmentsCaptor.getAllValues().get(0)); + } + + @Test public void testRemoteRotation() { DisplayContent dc = createNewDisplay(); |