Finish fixed rotation after recents moved to top

While the task transition is running, its activity is the animation
source. If recents animation is triggered and finished before the
transition is done, the animation source will be notified that the
transition is finished. At this timing, the rotated activity is still
the top activity. Since it is moving to back, the display orientation
is unnecessary to be updated for the intermediate state. That avoids
flickering by not committing visibility due to configuration change.

Fixes: 160239226
Test: atest RecentsAnimationControllerTest# \
            testKeepFixedRotationWhenMovingRecentsToTop
Change-Id: I5c5c12338b9bc0cd8a99655a843a33d8729f8892
This commit is contained in:
Riddle Hsu
2020-07-25 00:37:48 +08:00
parent 8a2c829513
commit 6f9a0bf044
4 changed files with 53 additions and 8 deletions

View File

@@ -5403,6 +5403,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private ActivityRecord mAnimatingRecents;
/** Whether {@link #mAnimatingRecents} is going to be the top activity. */
private boolean mRecentsWillBeTop;
/**
* If the recents activity has a fixed orientation which is different from the current top
* activity, it will be rotated before being shown so we avoid a screen rotation animation
@@ -5428,10 +5431,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* If {@link #mAnimatingRecents} still has fixed rotation, it should be moved to top so we
* don't clear {@link #mFixedRotationLaunchingApp} that will be handled by transition.
*/
void onFinishRecentsAnimation(boolean moveRecentsToBack) {
void onFinishRecentsAnimation() {
final ActivityRecord animatingRecents = mAnimatingRecents;
final boolean recentsWillBeTop = mRecentsWillBeTop;
mAnimatingRecents = null;
if (!moveRecentsToBack) {
mRecentsWillBeTop = false;
if (recentsWillBeTop) {
// The recents activity will be the top, such as staying at recents list or
// returning to home (if home and recents are the same activity).
return;
@@ -5454,6 +5459,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
void notifyRecentsWillBeTop() {
mRecentsWillBeTop = true;
}
/**
* 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.
@@ -5474,6 +5483,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (r == null || r == mAnimatingRecents) {
return;
}
if (mAnimatingRecents != null && mRecentsWillBeTop) {
// The activity is not the recents and it should be moved to back later, so it is
// better to keep its current appearance for the next transition. Otherwise the
// display orientation may be updated too early and the layout procedures at the
// end of finishing recents animation is skipped. That causes flickering because
// the surface of closing app hasn't updated to invisible.
return;
}
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

View File

@@ -702,6 +702,11 @@ public class RecentsAnimationController implements DeathRecipient {
"cleanupAnimation(): Notify animation finished mPendingAnimations=%d "
+ "reorderMode=%d",
mPendingAnimations.size(), reorderMode);
if (reorderMode != REORDER_MOVE_TO_ORIGINAL_POSITION) {
// Notify the state at the beginning because the removeAnimation may notify the
// transition is finished. This is a signal that there will be a next transition.
mDisplayContent.mFixedRotationTransitionListener.notifyRecentsWillBeTop();
}
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
@@ -742,8 +747,7 @@ public class RecentsAnimationController implements DeathRecipient {
mTargetActivityRecord.token);
}
}
mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(
reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION /* moveRecentsToBack */);
mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
// Notify that the animation has ended
if (mStatusBar != null) {

View File

@@ -1354,7 +1354,7 @@ public class DisplayContentTests extends WindowTestsBase {
assertFalse(displayRotation.updateRotationUnchecked(false));
// Rotation can be updated if the recents animation is finished.
mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(false);
mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
assertTrue(displayRotation.updateRotationUnchecked(false));
// Rotation can be updated if the recents animation is animating but it is not on top, e.g.

View File

@@ -362,13 +362,14 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
assertFalse(homeActivity.hasFixedRotationTransform());
}
@Test
public void testClearFixedRotationLaunchingAppAfterCleanupAnimation() {
unblockDisplayRotation(mDefaultDisplay);
private ActivityRecord prepareFixedRotationLaunchingAppWithRecentsAnim() {
final ActivityRecord homeActivity = createHomeActivity();
homeActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
final ActivityRecord activity = createActivityRecord(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
// Add a window so it can be animated by the recents.
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
activity.addWindow(win);
// Assume an activity is launching to different rotation.
mDefaultDisplay.setFixedRotationLaunchingApp(activity,
(mDefaultDisplay.getRotation() + 1) % 4);
@@ -379,6 +380,14 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
// Before the transition is done, the recents animation is triggered.
initializeRecentsAnimationController(mController, homeActivity);
assertFalse(homeActivity.hasFixedRotationTransform());
assertTrue(mController.isAnimatingTask(activity.getTask()));
return activity;
}
@Test
public void testClearFixedRotationLaunchingAppAfterCleanupAnimation() {
final ActivityRecord activity = prepareFixedRotationLaunchingAppWithRecentsAnim();
// Simulate giving up the swipe up gesture to keep the original activity as top.
mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
@@ -387,6 +396,21 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
assertFalse(mDefaultDisplay.hasTopFixedRotationLaunchingApp());
}
@Test
public void testKeepFixedRotationWhenMovingRecentsToTop() {
final ActivityRecord activity = prepareFixedRotationLaunchingAppWithRecentsAnim();
// Assume a transition animation has started running before recents animation. Then the
// activity will receive onAnimationFinished that notifies app transition finished when
// removing the recents animation of task.
activity.getTask().getAnimationSources().add(activity);
// Simulate swiping to home/recents before the transition is done.
mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
// The rotation transform should be preserved. In real case, it will be cleared by the next
// move-to-top transition.
assertTrue(activity.hasFixedRotationTransform());
}
@Test
public void testWallpaperHasFixedRotationApplied() {
unblockDisplayRotation(mDefaultDisplay);