Defer rotation update during recent transition
This migrates the changes [1] and [2] to shell transition.
[1] I3f2a46ddfe5e76ad3200ea27ad272ae36b6bcc8d
[2] I5e7487b08e139b251d160bd0627326b83351640e
With shell transition+fixed rotation, even if the home is moved to
top for showing recents overview, a rotation change may still happen
if the display is in non-natural rotation (different than NOSENSOR).
So the legacy condition is still needed to defer the rotation change.
Eliminate the post message for updating rotation by setting
the orientation of recents mLastOrientation. So when finishing
the transition, the change can be seen:
Legacy: handleAppTransitionReady -> FINISH_LAYOUT_REDO_CONFIG
Shell: onAppTransitionFinishedLocked
-> continueUpdateOrientationForDiffOrienLaunchingApp
Bug: 216169338
Test: atest TransitionTests#testDeferRotationForTransientLaunch
RecentsAnimationControllerTest#testCheckRotationAfterCleanup
Change-Id: I2580c6d7b46a641abd4d8f963e915122af215ac7
This commit is contained in:
@@ -6368,13 +6368,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Returns the fixed orientation requested by a transient launch (e.g. recents animation).
|
||||
* If it doesn't return SCREEN_ORIENTATION_UNSET, the rotation change should be deferred.
|
||||
*/
|
||||
boolean isTopFixedOrientationRecentsAnimating() {
|
||||
return mAnimatingRecents != null
|
||||
&& mAnimatingRecents.getRequestedConfigurationOrientation(true /* forDisplay */)
|
||||
!= ORIENTATION_UNDEFINED && !hasTopFixedRotationLaunchingApp();
|
||||
@ActivityInfo.ScreenOrientation int getTransientFixedOrientation() {
|
||||
ActivityRecord source = null;
|
||||
if (mTransitionController.isShellTransitionsEnabled()) {
|
||||
final ActivityRecord r = mFixedRotationLaunchingApp;
|
||||
if (r != null && mTransitionController.isTransientLaunch(r)) {
|
||||
source = r;
|
||||
}
|
||||
} else if (mAnimatingRecents != null && !hasTopFixedRotationLaunchingApp()) {
|
||||
source = mAnimatingRecents;
|
||||
}
|
||||
if (source == null || source.getRequestedConfigurationOrientation(
|
||||
true /* forDisplay */) == ORIENTATION_UNDEFINED) {
|
||||
return SCREEN_ORIENTATION_UNSET;
|
||||
}
|
||||
if (!mWmService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
|
||||
// If screen is off or the device is going to sleep, then still allow to update.
|
||||
return SCREEN_ORIENTATION_UNSET;
|
||||
}
|
||||
return source.mOrientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -419,11 +419,8 @@ public class DisplayRotation {
|
||||
* THE SCREEN.
|
||||
*/
|
||||
boolean updateRotationUnchecked(boolean forceUpdate) {
|
||||
final boolean useShellTransitions =
|
||||
mDisplayContent.mTransitionController.isShellTransitionsEnabled();
|
||||
|
||||
final int displayId = mDisplayContent.getDisplayId();
|
||||
if (!forceUpdate && !useShellTransitions) {
|
||||
if (!forceUpdate) {
|
||||
if (mDeferredRotationPauseCount > 0) {
|
||||
// Rotation updates have been paused temporarily. Defer the update until updates
|
||||
// have been resumed.
|
||||
@@ -449,17 +446,16 @@ public class DisplayRotation {
|
||||
return false;
|
||||
}
|
||||
|
||||
final RecentsAnimationController recentsAnimController =
|
||||
mService.getRecentsAnimationController();
|
||||
if (recentsAnimController != null && mDisplayContent.mFixedRotationTransitionListener
|
||||
.isTopFixedOrientationRecentsAnimating()
|
||||
// If screen is off or the device is going to sleep, then still allow to update.
|
||||
&& mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
|
||||
final int transientFixedOrientation =
|
||||
mDisplayContent.mFixedRotationTransitionListener.getTransientFixedOrientation();
|
||||
if (transientFixedOrientation != SCREEN_ORIENTATION_UNSET) {
|
||||
// Makes sure that after the transition is finished, updateOrientation() can see
|
||||
// the difference from the latest orientation source.
|
||||
mLastOrientation = transientFixedOrientation;
|
||||
// During the recents animation, the closing app might still be considered on top.
|
||||
// In order to ignore its requested orientation to avoid a sensor led rotation (e.g
|
||||
// user rotating the device while the recents animation is running), we ignore
|
||||
// rotation update while the animation is running.
|
||||
recentsAnimController.setCheckRotationAfterCleanup();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -513,7 +509,7 @@ public class DisplayRotation {
|
||||
|
||||
mDisplayContent.setLayoutNeeded();
|
||||
|
||||
if (useShellTransitions) {
|
||||
if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
|
||||
final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
|
||||
final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
|
||||
: new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
|
||||
|
||||
@@ -122,7 +122,6 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
private final int mDisplayId;
|
||||
private boolean mWillFinishToHome = false;
|
||||
private final Runnable mFailsafeRunnable = this::onFailsafe;
|
||||
private Runnable mCheckRotationAfterCleanup;
|
||||
|
||||
// The recents component app token that is shown behind the visibile tasks
|
||||
private ActivityRecord mTargetActivityRecord;
|
||||
@@ -919,24 +918,6 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
mCancelDeferredWithScreenshot = screenshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the display rotation change is ignored while recents animation is running, make sure that
|
||||
* the pending rotation change will be applied after the animation finishes.
|
||||
*/
|
||||
void setCheckRotationAfterCleanup() {
|
||||
if (mCheckRotationAfterCleanup != null) return;
|
||||
mCheckRotationAfterCleanup = () -> {
|
||||
synchronized (mService.mGlobalLock) {
|
||||
if (mDisplayContent.getDisplayRotation()
|
||||
.updateRotationAndSendNewConfigIfChanged()) {
|
||||
if (mTargetActivityRecord != null) {
|
||||
mTargetActivityRecord.finishFixedRotationTransform();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether we should defer the cancel from a root task order change until the next app
|
||||
* transition.
|
||||
@@ -1037,10 +1018,6 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
if (mStatusBar != null) {
|
||||
mStatusBar.onRecentsAnimationStateChanged(false /* running */);
|
||||
}
|
||||
if (mCheckRotationAfterCleanup != null) {
|
||||
mService.mH.post(mCheckRotationAfterCleanup);
|
||||
mCheckRotationAfterCleanup = null;
|
||||
}
|
||||
}
|
||||
|
||||
void scheduleFailsafe() {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
|
||||
@@ -440,16 +441,26 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
|
||||
public void testCheckRotationAfterCleanup() {
|
||||
mWm.setRecentsAnimationController(mController);
|
||||
spyOn(mDisplayContent.mFixedRotationTransitionListener);
|
||||
doReturn(true).when(mDisplayContent.mFixedRotationTransitionListener)
|
||||
.isTopFixedOrientationRecentsAnimating();
|
||||
final ActivityRecord recents = mock(ActivityRecord.class);
|
||||
recents.mOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
|
||||
doReturn(ORIENTATION_PORTRAIT).when(recents)
|
||||
.getRequestedConfigurationOrientation(anyBoolean());
|
||||
mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recents);
|
||||
|
||||
// Rotation update is skipped while the recents animation is running.
|
||||
assertFalse(mDisplayContent.getDisplayRotation().updateOrientation(DisplayContentTests
|
||||
.getRotatedOrientation(mDefaultDisplay), false /* forceUpdate */));
|
||||
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
|
||||
final int topOrientation = DisplayContentTests.getRotatedOrientation(mDefaultDisplay);
|
||||
assertFalse(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */));
|
||||
assertEquals(recents.mOrientation, displayRotation.getLastOrientation());
|
||||
final int prevRotation = mDisplayContent.getRotation();
|
||||
mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
|
||||
waitHandlerIdle(mWm.mH);
|
||||
|
||||
// In real case, it is called from RecentsAnimation#finishAnimation -> continueWindowLayout
|
||||
// -> handleAppTransitionReady -> add FINISH_LAYOUT_REDO_CONFIG, and DisplayContent#
|
||||
// applySurfaceChangesTransaction will call updateOrientation for FINISH_LAYOUT_REDO_CONFIG.
|
||||
assertTrue(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */));
|
||||
// The display should be updated to the changed orientation after the animation is finished.
|
||||
assertNotEquals(mDisplayContent.getRotation(), prevRotation);
|
||||
assertNotEquals(displayRotation.getRotation(), prevRotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.wm;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
|
||||
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
|
||||
@@ -33,13 +34,17 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
|
||||
import static android.window.TransitionInfo.isIndependent;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
|
||||
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;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -659,6 +664,41 @@ public class TransitionTests extends WindowTestsBase {
|
||||
assertNull(mDisplayContent.getAsyncRotationController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeferRotationForTransientLaunch() {
|
||||
final TestTransitionPlayer player = registerTestTransitionPlayer();
|
||||
assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation());
|
||||
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
final ActivityRecord home = new ActivityBuilder(mAtm)
|
||||
.setTask(mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask())
|
||||
.setScreenOrientation(SCREEN_ORIENTATION_NOSENSOR).setVisible(false).build();
|
||||
final Transition transition = home.mTransitionController.createTransition(TRANSIT_OPEN);
|
||||
final int prevRotation = mDisplayContent.getRotation();
|
||||
transition.setTransientLaunch(home, null /* restoreBelow */);
|
||||
home.mTransitionController.requestStartTransition(transition, home.getTask(),
|
||||
null /* remoteTransition */, null /* displayChange */);
|
||||
transition.collectExistenceChange(home);
|
||||
home.mVisibleRequested = true;
|
||||
mDisplayContent.setFixedRotationLaunchingAppUnchecked(home);
|
||||
doReturn(true).when(home).hasFixedRotationTransform(any());
|
||||
player.startTransition();
|
||||
player.onTransactionReady(mDisplayContent.getSyncTransaction());
|
||||
|
||||
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
|
||||
spyOn(displayRotation);
|
||||
doReturn(true).when(displayRotation).isWaitingForRemoteRotation();
|
||||
doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation(
|
||||
anyInt() /* orientation */, anyInt() /* lastRotation */);
|
||||
// Rotation update is skipped while the recents animation is running.
|
||||
assertFalse(mDisplayContent.updateRotationUnchecked());
|
||||
assertEquals(SCREEN_ORIENTATION_NOSENSOR, displayRotation.getLastOrientation());
|
||||
// Return to the app without fixed orientation from recents.
|
||||
app.moveFocusableActivityToTop("test");
|
||||
player.finish();
|
||||
// The display should be updated to the changed orientation after the animation is finish.
|
||||
assertNotEquals(mDisplayContent.getRotation(), prevRotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntermediateVisibility() {
|
||||
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
|
||||
|
||||
Reference in New Issue
Block a user