Finish fixed rotation while the task has done transition

Assume an activity A is started in portrait and it starts another
landscape activity B immediately in the same task. If fixed rotation
applies for this case, the starting window will transfer across
different orientations that may look flickering. And because the
transformed activity is the B, the transition finished event of A
cannot match B that causes the display stays in old orientation.

This change makes the case use normal rotation animation to avoid the
flickering, and still handles the case that the transition is done
but the notified record is the first activity to avoid other similar
cases to miss the orientation update.

Fixes: 158112625
Test: DisplayContentTests#testFinishFixedRotationNoAppTransitioningTask

Change-Id: I559b0881463e379c17e35b7cfd96948feb36bf4b
This commit is contained in:
Riddle Hsu
2020-06-16 18:56:17 +08:00
parent cd03d90a72
commit 3ae8aab87f
2 changed files with 53 additions and 11 deletions

View File

@@ -1463,9 +1463,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return true;
}
if (checkOpening) {
if (!mAppTransition.isTransitionSet() && !mOpeningApps.contains(r)) {
if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) {
// Apply normal rotation animation in case of the activity set different requested
// orientation without activity switch.
// orientation without activity switch, or the transition is unset due to starting
// window was transferred ({@link #mSkipAppTransitionAnimation}).
return false;
}
} else if (r != topRunningActivity()) {
@@ -5673,16 +5674,36 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (r == null || r == mAnimatingRecents) {
return;
}
if (mFixedRotationLaunchingApp != null
&& mFixedRotationLaunchingApp.hasFixedRotationTransform(r)) {
// Waiting until all of the associated activities have done animation, or the
// orientation would be updated too early and cause flickers.
if (!mFixedRotationLaunchingApp.hasAnimatingFixedRotationTransition()) {
continueUpdateOrientationForDiffOrienLaunchingApp();
if (mFixedRotationLaunchingApp == null) {
// In most cases this is a no-op if the activity doesn't have fixed rotation.
// Otherwise it could be from finishing recents animation while the display has
// different orientation.
r.finishFixedRotationTransform();
return;
}
if (mFixedRotationLaunchingApp.hasFixedRotationTransform(r)) {
if (mFixedRotationLaunchingApp.hasAnimatingFixedRotationTransition()) {
// Waiting until all of the associated activities have done animation, or the
// orientation would be updated too early and cause flickering.
return;
}
} else {
r.finishFixedRotationTransform();
// Handle a corner case that the task of {@link #mFixedRotationLaunchingApp} is no
// longer animating but the corresponding transition finished event won't notify.
// E.g. activity A transferred starting window to B, only A will receive transition
// finished event. A doesn't have fixed rotation but B is the rotated launching app.
final Task task = r.getTask();
if (task == null || task != mFixedRotationLaunchingApp.getTask()) {
// Different tasks won't be in one activity transition animation.
return;
}
if (task.isAppTransitioning()) {
return;
// Continue to update orientation because the transition of the top rotated
// launching activity is done.
}
}
continueUpdateOrientationForDiffOrienLaunchingApp();
}
@Override

View File

@@ -1134,8 +1134,6 @@ public class DisplayContentTests extends WindowTestsBase {
// Launch another activity before the transition is finished.
final ActivityRecord app2 = new ActivityTestsBase.StackBuilder(mWm.mRoot)
.setDisplay(mDisplayContent).build().getTopMostActivity();
mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */);
mDisplayContent.mOpeningApps.add(app2);
app2.setRequestedOrientation(newOrientation);
@@ -1161,6 +1159,29 @@ public class DisplayContentTests extends WindowTestsBase {
assertNull(mDisplayContent.getFixedRotationAnimationController());
}
@Test
public void testFinishFixedRotationNoAppTransitioningTask() {
final ActivityRecord app = mAppWindow.mActivityRecord;
final Task task = app.getTask();
final ActivityRecord app2 = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
.setTask(task).build();
mDisplayContent.setFixedRotationLaunchingApp(app2, (mDisplayContent.getRotation() + 1) % 4);
doReturn(true).when(task).isAppTransitioning();
// If the task is animating transition, this should be no-op.
mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
assertTrue(app2.hasFixedRotationTransform());
assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
doReturn(false).when(task).isAppTransitioning();
// Although this notifies app instead of app2 that uses the fixed rotation, app2 should
// still finish the transform because there is no more transition event.
mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
assertFalse(app2.hasFixedRotationTransform());
assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp());
}
@Test
public void testRotateSeamlesslyWithFixedRotation() {
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();