summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java7
-rw-r--r--services/core/java/com/android/server/wm/AsyncRotationController.java49
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java31
-rw-r--r--services/core/java/com/android/server/wm/Transition.java4
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java56
6 files changed, 113 insertions, 39 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 4bc5850cd293..56d51687603a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -348,12 +348,13 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final boolean isTask = change.getTaskInfo() != null;
+ boolean isSeamlessDisplayChange = false;
if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
if (info.getType() == TRANSIT_CHANGE) {
- boolean isSeamless = isRotationSeamless(info, mDisplayController);
+ isSeamlessDisplayChange = isRotationSeamless(info, mDisplayController);
final int anim = getRotationAnimation(info);
- if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
+ if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
mTransactionPool, startTransaction, change, info.getRootLeash(),
anim);
@@ -387,6 +388,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
startTransaction.setPosition(change.getLeash(),
change.getEndAbsBounds().left - info.getRootOffset().x,
change.getEndAbsBounds().top - info.getRootOffset().y);
+ // Seamless display transition doesn't need to animate.
+ if (isSeamlessDisplayChange) continue;
if (isTask) {
// Skip non-tasks since those usually have null bounds.
startTransaction.setWindowCrop(change.getLeash(),
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 5c13e8192701..9e889ad11b8e 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -94,6 +94,9 @@ class AsyncRotationController extends FadeAnimationController implements Consume
/** Whether the start transaction of the transition is committed (by shell). */
private boolean mIsStartTransactionCommitted;
+ /** Whether the target windows have been requested to sync their draw transactions. */
+ private boolean mIsSyncDrawRequested;
+
private SeamlessRotator mRotator;
private final int mOriginalRotation;
@@ -139,22 +142,10 @@ class AsyncRotationController extends FadeAnimationController implements Consume
displayContent.forAllWindows(this, true /* traverseTopToBottom */);
// Legacy animation doesn't need to wait for the start transaction.
- mIsStartTransactionCommitted = mTransitionOp == OP_LEGACY;
- if (mIsStartTransactionCommitted) return;
- // The transition sync group may be finished earlier because it doesn't wait for these
- // target windows. But the windows still need to use sync transaction to keep the appearance
- // in previous rotation, so request a no-op sync to keep the state.
- for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
- if (mHasScreenRotationAnimation
- && mTargetWindowTokens.valueAt(i).mAction == Operation.ACTION_FADE) {
- // The windows are hidden (leash is alpha 0) before finishing drawing so it is
- // unnecessary to request sync.
- continue;
- }
- final WindowToken token = mTargetWindowTokens.keyAt(i);
- for (int j = token.getChildCount() - 1; j >= 0; j--) {
- token.getChildAt(j).applyWithNextDraw(t -> {});
- }
+ if (mTransitionOp == OP_LEGACY) {
+ mIsStartTransactionCommitted = true;
+ } else if (displayContent.mTransitionController.useShellTransitionsRotation()) {
+ keepAppearanceInPreviousRotation();
}
}
@@ -194,6 +185,30 @@ class AsyncRotationController extends FadeAnimationController implements Consume
mTargetWindowTokens.put(w.mToken, new Operation(action));
}
+ /**
+ * Enables {@link #handleFinishDrawing(WindowState, SurfaceControl.Transaction)} to capture the
+ * draw transactions of the target windows if needed.
+ */
+ void keepAppearanceInPreviousRotation() {
+ // The transition sync group may be finished earlier because it doesn't wait for these
+ // target windows. But the windows still need to use sync transaction to keep the appearance
+ // in previous rotation, so request a no-op sync to keep the state.
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ if (mHasScreenRotationAnimation
+ && mTargetWindowTokens.valueAt(i).mAction == Operation.ACTION_FADE) {
+ // The windows are hidden (leash is alpha 0) before finishing drawing so it is
+ // unnecessary to request sync.
+ continue;
+ }
+ final WindowToken token = mTargetWindowTokens.keyAt(i);
+ for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ token.getChildAt(j).applyWithNextDraw(t -> {});
+ }
+ }
+ mIsSyncDrawRequested = true;
+ if (DEBUG) Slog.d(TAG, "Requested to sync draw transaction");
+ }
+
/** Lets the window fit in new rotation naturally. */
private void finishOp(WindowToken windowToken) {
final Operation op = mTargetWindowTokens.remove(windowToken);
@@ -433,7 +448,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
*/
boolean handleFinishDrawing(WindowState w, SurfaceControl.Transaction postDrawTransaction) {
if (mTransitionOp == OP_LEGACY || postDrawTransaction == null
- || !w.mTransitionController.inTransition()) {
+ || !mIsSyncDrawRequested || !w.mTransitionController.inTransition()) {
return false;
}
final Operation op = mTargetWindowTokens.get(w.mToken);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 195d4258c0d9..71a1b3e6c5d2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1735,19 +1735,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
- final boolean useAsyncRotation = !mTransitionController.isShellTransitionsEnabled();
if (mFixedRotationLaunchingApp == null && r != null) {
- mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this,
- rotation);
- if (useAsyncRotation) {
- startAsyncRotation(
- // Delay the hide animation to avoid blinking by clicking navigation bar
- // that may toggle fixed rotation in a short time.
- r == mFixedRotationTransitionListener.mAnimatingRecents);
- }
+ mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
+ // Delay the hide animation to avoid blinking by clicking navigation bar that may
+ // toggle fixed rotation in a short time.
+ final boolean shouldDebounce = r == mFixedRotationTransitionListener.mAnimatingRecents
+ || mTransitionController.isTransientLaunch(r);
+ startAsyncRotation(shouldDebounce);
} else if (mFixedRotationLaunchingApp != null && r == null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
- if (useAsyncRotation) finishAsyncRotationIfPossible();
+ // Keep async rotation controller if the next transition of display is requested.
+ if (!mTransitionController.isCollecting(this)) {
+ finishAsyncRotationIfPossible();
+ }
}
mFixedRotationLaunchingApp = r;
}
@@ -1805,7 +1805,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
// Update directly because the app which will change the orientation of display is ready.
if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
- mTransitionController.setSeamlessRotation(this);
sendNewConfiguration();
return;
}
@@ -3234,7 +3233,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
this, this, null /* remoteTransition */, displayChange);
if (t != null) {
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
- if (isRotationChanging()) {
+ if (mFixedRotationLaunchingApp != null) {
+ // A fixed-rotation transition is done, then continue to start a seamless display
+ // transition. And be fore the start transaction is applied, the non-app windows
+ // need to keep in previous rotation to avoid showing inconsistent content.
+ t.setSeamlessRotation(this);
+ if (mAsyncRotationController != null) {
+ mAsyncRotationController.keepAppearanceInPreviousRotation();
+ }
+ } else if (isRotationChanging()) {
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
controller.mTransitionMetricsReporter.associate(t,
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0f5828c7efb0..785867b391ca 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -548,7 +548,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
for (int i = 0; i < mTargetDisplays.size(); ++i) {
final DisplayContent dc = mTargetDisplays.get(i);
final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
- if (asyncRotationController != null) {
+ if (asyncRotationController != null && mTargets.contains(dc)) {
asyncRotationController.onTransitionFinished();
}
if (mTransientLaunches != null) {
@@ -696,7 +696,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// This is non-null only if display has changes. It handles the visible windows that don't
// need to be participated in the transition.
final AsyncRotationController controller = dc.getAsyncRotationController();
- if (controller != null) {
+ if (controller != null && mTargets.contains(dc)) {
controller.setupStartTransaction(transaction);
}
mStartTransaction = transaction;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index b0532c2a1552..0cfc702018a0 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -519,11 +519,6 @@ class TransitionController {
}
}
- void setSeamlessRotation(@NonNull WindowContainer wc) {
- if (mCollectingTransition == null) return;
- mCollectingTransition.setSeamlessRotation(wc);
- }
-
void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
final Transition transition = Transition.fromBinder(token);
if (transition == null || !mPlayingTransitions.contains(transition)) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 29289a404791..32af672c8693 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -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.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
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_PANEL;
@@ -557,11 +558,20 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testAppTransitionWithRotationChange() {
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+ final boolean useFixedRotation = !player.mController.useShellTransitionsRotation();
+ if (useFixedRotation) {
+ testFixedRotationOpen(player);
+ } else {
+ testShellRotationOpen(player);
+ }
+ }
+
+ private void testShellRotationOpen(TestTransitionPlayer player) {
final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
makeWindowVisible(statusBar);
mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
final ActivityRecord app = createActivityRecord(mDisplayContent);
- final TestTransitionPlayer player = registerTestTransitionPlayer();
final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
app.mTransitionController.requestStartTransition(transition, app.getTask(),
null /* remoteTransition */, null /* displayChange */);
@@ -605,6 +615,50 @@ public class TransitionTests extends WindowTestsBase {
assertNull(mDisplayContent.getAsyncRotationController());
}
+ private void testFixedRotationOpen(TestTransitionPlayer player) {
+ final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ makeWindowVisible(statusBar);
+ mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
+ final ActivityRecord app = createActivityRecord(mDisplayContent);
+ final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
+ app.mTransitionController.requestStartTransition(transition, app.getTask(),
+ null /* remoteTransition */, null /* displayChange */);
+ app.mTransitionController.collectExistenceChange(app.getTask());
+ mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
+ final AsyncRotationController asyncRotationController =
+ mDisplayContent.getAsyncRotationController();
+ assertNotNull(asyncRotationController);
+ assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar));
+ assertTrue(app.getTask().inTransition());
+
+ player.start();
+ player.finish();
+ app.getTask().clearSyncState();
+
+ // The open transition is finished. Continue to play seamless display change transition,
+ // so the previous async rotation controller should still exist.
+ mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
+ mDisplayContent.setLastHasContent();
+ mDisplayContent.requestChangeTransitionIfNeeded(1 /* changes */, null /* displayChange */);
+ assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
+ assertNotNull(mDisplayContent.getAsyncRotationController());
+
+ statusBar.setOrientationChanging(true);
+ player.startTransition();
+ // Non-app windows should not be collected.
+ assertFalse(statusBar.mToken.inTransition());
+
+ onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
+ assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
+ mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation());
+ player.finish();
+
+ // The controller should be cleared if the target windows are drawn.
+ statusBar.finishDrawing(mWm.mTransactionFactory.get());
+ statusBar.setOrientationChanging(false);
+ assertNull(mDisplayContent.getAsyncRotationController());
+ }
+
@Test
public void testIntermediateVisibility() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);