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
|
* Returns the fixed orientation requested by a transient launch (e.g. recents animation).
|
||||||
* activity as a fixed orientation so shouldn't be rotated.
|
* If it doesn't return SCREEN_ORIENTATION_UNSET, the rotation change should be deferred.
|
||||||
*/
|
*/
|
||||||
boolean isTopFixedOrientationRecentsAnimating() {
|
@ActivityInfo.ScreenOrientation int getTransientFixedOrientation() {
|
||||||
return mAnimatingRecents != null
|
ActivityRecord source = null;
|
||||||
&& mAnimatingRecents.getRequestedConfigurationOrientation(true /* forDisplay */)
|
if (mTransitionController.isShellTransitionsEnabled()) {
|
||||||
!= ORIENTATION_UNDEFINED && !hasTopFixedRotationLaunchingApp();
|
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
|
@Override
|
||||||
|
|||||||
@@ -419,11 +419,8 @@ public class DisplayRotation {
|
|||||||
* THE SCREEN.
|
* THE SCREEN.
|
||||||
*/
|
*/
|
||||||
boolean updateRotationUnchecked(boolean forceUpdate) {
|
boolean updateRotationUnchecked(boolean forceUpdate) {
|
||||||
final boolean useShellTransitions =
|
|
||||||
mDisplayContent.mTransitionController.isShellTransitionsEnabled();
|
|
||||||
|
|
||||||
final int displayId = mDisplayContent.getDisplayId();
|
final int displayId = mDisplayContent.getDisplayId();
|
||||||
if (!forceUpdate && !useShellTransitions) {
|
if (!forceUpdate) {
|
||||||
if (mDeferredRotationPauseCount > 0) {
|
if (mDeferredRotationPauseCount > 0) {
|
||||||
// Rotation updates have been paused temporarily. Defer the update until updates
|
// Rotation updates have been paused temporarily. Defer the update until updates
|
||||||
// have been resumed.
|
// have been resumed.
|
||||||
@@ -449,17 +446,16 @@ public class DisplayRotation {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final RecentsAnimationController recentsAnimController =
|
final int transientFixedOrientation =
|
||||||
mService.getRecentsAnimationController();
|
mDisplayContent.mFixedRotationTransitionListener.getTransientFixedOrientation();
|
||||||
if (recentsAnimController != null && mDisplayContent.mFixedRotationTransitionListener
|
if (transientFixedOrientation != SCREEN_ORIENTATION_UNSET) {
|
||||||
.isTopFixedOrientationRecentsAnimating()
|
// Makes sure that after the transition is finished, updateOrientation() can see
|
||||||
// If screen is off or the device is going to sleep, then still allow to update.
|
// the difference from the latest orientation source.
|
||||||
&& mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
|
mLastOrientation = transientFixedOrientation;
|
||||||
// During the recents animation, the closing app might still be considered on top.
|
// 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
|
// 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
|
// user rotating the device while the recents animation is running), we ignore
|
||||||
// rotation update while the animation is running.
|
// rotation update while the animation is running.
|
||||||
recentsAnimController.setCheckRotationAfterCleanup();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -513,7 +509,7 @@ public class DisplayRotation {
|
|||||||
|
|
||||||
mDisplayContent.setLayoutNeeded();
|
mDisplayContent.setLayoutNeeded();
|
||||||
|
|
||||||
if (useShellTransitions) {
|
if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
|
||||||
final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
|
final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
|
||||||
final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
|
final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
|
||||||
: new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
|
: new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ public class RecentsAnimationController implements DeathRecipient {
|
|||||||
private final int mDisplayId;
|
private final int mDisplayId;
|
||||||
private boolean mWillFinishToHome = false;
|
private boolean mWillFinishToHome = false;
|
||||||
private final Runnable mFailsafeRunnable = this::onFailsafe;
|
private final Runnable mFailsafeRunnable = this::onFailsafe;
|
||||||
private Runnable mCheckRotationAfterCleanup;
|
|
||||||
|
|
||||||
// The recents component app token that is shown behind the visibile tasks
|
// The recents component app token that is shown behind the visibile tasks
|
||||||
private ActivityRecord mTargetActivityRecord;
|
private ActivityRecord mTargetActivityRecord;
|
||||||
@@ -919,24 +918,6 @@ public class RecentsAnimationController implements DeathRecipient {
|
|||||||
mCancelDeferredWithScreenshot = screenshot;
|
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
|
* @return Whether we should defer the cancel from a root task order change until the next app
|
||||||
* transition.
|
* transition.
|
||||||
@@ -1037,10 +1018,6 @@ public class RecentsAnimationController implements DeathRecipient {
|
|||||||
if (mStatusBar != null) {
|
if (mStatusBar != null) {
|
||||||
mStatusBar.onRecentsAnimationStateChanged(false /* running */);
|
mStatusBar.onRecentsAnimationStateChanged(false /* running */);
|
||||||
}
|
}
|
||||||
if (mCheckRotationAfterCleanup != null) {
|
|
||||||
mService.mH.post(mCheckRotationAfterCleanup);
|
|
||||||
mCheckRotationAfterCleanup = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduleFailsafe() {
|
void scheduleFailsafe() {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.server.wm;
|
package com.android.server.wm;
|
||||||
|
|
||||||
|
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||||
import static android.view.Display.DEFAULT_DISPLAY;
|
import static android.view.Display.DEFAULT_DISPLAY;
|
||||||
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
|
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
|
||||||
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
|
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
|
||||||
@@ -440,16 +441,26 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
|
|||||||
public void testCheckRotationAfterCleanup() {
|
public void testCheckRotationAfterCleanup() {
|
||||||
mWm.setRecentsAnimationController(mController);
|
mWm.setRecentsAnimationController(mController);
|
||||||
spyOn(mDisplayContent.mFixedRotationTransitionListener);
|
spyOn(mDisplayContent.mFixedRotationTransitionListener);
|
||||||
doReturn(true).when(mDisplayContent.mFixedRotationTransitionListener)
|
final ActivityRecord recents = mock(ActivityRecord.class);
|
||||||
.isTopFixedOrientationRecentsAnimating();
|
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.
|
// Rotation update is skipped while the recents animation is running.
|
||||||
assertFalse(mDisplayContent.getDisplayRotation().updateOrientation(DisplayContentTests
|
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
|
||||||
.getRotatedOrientation(mDefaultDisplay), false /* forceUpdate */));
|
final int topOrientation = DisplayContentTests.getRotatedOrientation(mDefaultDisplay);
|
||||||
|
assertFalse(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */));
|
||||||
|
assertEquals(recents.mOrientation, displayRotation.getLastOrientation());
|
||||||
final int prevRotation = mDisplayContent.getRotation();
|
final int prevRotation = mDisplayContent.getRotation();
|
||||||
mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
|
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.
|
// The display should be updated to the changed orientation after the animation is finished.
|
||||||
assertNotEquals(mDisplayContent.getRotation(), prevRotation);
|
assertNotEquals(displayRotation.getRotation(), prevRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.server.wm;
|
|||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
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.ROTATION_ANIMATION_SEAMLESS;
|
||||||
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
|
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
|
||||||
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
|
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 android.window.TransitionInfo.isIndependent;
|
||||||
|
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assume.assumeFalse;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
@@ -659,6 +664,41 @@ public class TransitionTests extends WindowTestsBase {
|
|||||||
assertNull(mDisplayContent.getAsyncRotationController());
|
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
|
@Test
|
||||||
public void testIntermediateVisibility() {
|
public void testIntermediateVisibility() {
|
||||||
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
|
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user