Merge "Defer rotation update during recent transition" into tm-dev

This commit is contained in:
Riddle Hsu
2022-03-01 09:49:45 +00:00
committed by Android (Google) Code Review
5 changed files with 86 additions and 47 deletions

View File

@@ -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

View File

@@ -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(),

View File

@@ -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() {

View File

@@ -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

View File

@@ -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);