Merge "Tweak animation for non-app windows with shell fixed rotation" into tm-dev
This commit is contained in:
@@ -348,12 +348,13 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||||
final boolean isTask = change.getTaskInfo() != null;
|
final boolean isTask = change.getTaskInfo() != null;
|
||||||
|
boolean isSeamlessDisplayChange = false;
|
||||||
|
|
||||||
if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
|
if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
|
||||||
if (info.getType() == TRANSIT_CHANGE) {
|
if (info.getType() == TRANSIT_CHANGE) {
|
||||||
boolean isSeamless = isRotationSeamless(info, mDisplayController);
|
isSeamlessDisplayChange = isRotationSeamless(info, mDisplayController);
|
||||||
final int anim = getRotationAnimation(info);
|
final int anim = getRotationAnimation(info);
|
||||||
if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
|
if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
|
||||||
mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
|
mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
|
||||||
mTransactionPool, startTransaction, change, info.getRootLeash(),
|
mTransactionPool, startTransaction, change, info.getRootLeash(),
|
||||||
anim);
|
anim);
|
||||||
@@ -387,6 +388,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
startTransaction.setPosition(change.getLeash(),
|
startTransaction.setPosition(change.getLeash(),
|
||||||
change.getEndAbsBounds().left - info.getRootOffset().x,
|
change.getEndAbsBounds().left - info.getRootOffset().x,
|
||||||
change.getEndAbsBounds().top - info.getRootOffset().y);
|
change.getEndAbsBounds().top - info.getRootOffset().y);
|
||||||
|
// Seamless display transition doesn't need to animate.
|
||||||
|
if (isSeamlessDisplayChange) continue;
|
||||||
if (isTask) {
|
if (isTask) {
|
||||||
// Skip non-tasks since those usually have null bounds.
|
// Skip non-tasks since those usually have null bounds.
|
||||||
startTransaction.setWindowCrop(change.getLeash(),
|
startTransaction.setWindowCrop(change.getLeash(),
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ class AsyncRotationController extends FadeAnimationController implements Consume
|
|||||||
/** Whether the start transaction of the transition is committed (by shell). */
|
/** Whether the start transaction of the transition is committed (by shell). */
|
||||||
private boolean mIsStartTransactionCommitted;
|
private boolean mIsStartTransactionCommitted;
|
||||||
|
|
||||||
|
/** Whether the target windows have been requested to sync their draw transactions. */
|
||||||
|
private boolean mIsSyncDrawRequested;
|
||||||
|
|
||||||
private SeamlessRotator mRotator;
|
private SeamlessRotator mRotator;
|
||||||
|
|
||||||
private final int mOriginalRotation;
|
private final int mOriginalRotation;
|
||||||
@@ -139,22 +142,10 @@ class AsyncRotationController extends FadeAnimationController implements Consume
|
|||||||
displayContent.forAllWindows(this, true /* traverseTopToBottom */);
|
displayContent.forAllWindows(this, true /* traverseTopToBottom */);
|
||||||
|
|
||||||
// Legacy animation doesn't need to wait for the start transaction.
|
// Legacy animation doesn't need to wait for the start transaction.
|
||||||
mIsStartTransactionCommitted = mTransitionOp == OP_LEGACY;
|
if (mTransitionOp == OP_LEGACY) {
|
||||||
if (mIsStartTransactionCommitted) return;
|
mIsStartTransactionCommitted = true;
|
||||||
// The transition sync group may be finished earlier because it doesn't wait for these
|
} else if (displayContent.mTransitionController.useShellTransitionsRotation()) {
|
||||||
// target windows. But the windows still need to use sync transaction to keep the appearance
|
keepAppearanceInPreviousRotation();
|
||||||
// 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 -> {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +185,30 @@ class AsyncRotationController extends FadeAnimationController implements Consume
|
|||||||
mTargetWindowTokens.put(w.mToken, new Operation(action));
|
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. */
|
/** Lets the window fit in new rotation naturally. */
|
||||||
private void finishOp(WindowToken windowToken) {
|
private void finishOp(WindowToken windowToken) {
|
||||||
final Operation op = mTargetWindowTokens.remove(windowToken);
|
final Operation op = mTargetWindowTokens.remove(windowToken);
|
||||||
@@ -433,7 +448,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
|
|||||||
*/
|
*/
|
||||||
boolean handleFinishDrawing(WindowState w, SurfaceControl.Transaction postDrawTransaction) {
|
boolean handleFinishDrawing(WindowState w, SurfaceControl.Transaction postDrawTransaction) {
|
||||||
if (mTransitionOp == OP_LEGACY || postDrawTransaction == null
|
if (mTransitionOp == OP_LEGACY || postDrawTransaction == null
|
||||||
|| !w.mTransitionController.inTransition()) {
|
|| !mIsSyncDrawRequested || !w.mTransitionController.inTransition()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final Operation op = mTargetWindowTokens.get(w.mToken);
|
final Operation op = mTargetWindowTokens.get(w.mToken);
|
||||||
|
|||||||
@@ -1735,19 +1735,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
|
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
|
||||||
final boolean useAsyncRotation = !mTransitionController.isShellTransitionsEnabled();
|
|
||||||
if (mFixedRotationLaunchingApp == null && r != null) {
|
if (mFixedRotationLaunchingApp == null && r != null) {
|
||||||
mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this,
|
mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
|
||||||
rotation);
|
// Delay the hide animation to avoid blinking by clicking navigation bar that may
|
||||||
if (useAsyncRotation) {
|
// toggle fixed rotation in a short time.
|
||||||
startAsyncRotation(
|
final boolean shouldDebounce = r == mFixedRotationTransitionListener.mAnimatingRecents
|
||||||
// Delay the hide animation to avoid blinking by clicking navigation bar
|
|| mTransitionController.isTransientLaunch(r);
|
||||||
// that may toggle fixed rotation in a short time.
|
startAsyncRotation(shouldDebounce);
|
||||||
r == mFixedRotationTransitionListener.mAnimatingRecents);
|
|
||||||
}
|
|
||||||
} else if (mFixedRotationLaunchingApp != null && r == null) {
|
} else if (mFixedRotationLaunchingApp != null && r == null) {
|
||||||
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
|
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;
|
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.
|
// Update directly because the app which will change the orientation of display is ready.
|
||||||
if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
|
if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
|
||||||
mTransitionController.setSeamlessRotation(this);
|
|
||||||
sendNewConfiguration();
|
sendNewConfiguration();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3234,7 +3233,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
this, this, null /* remoteTransition */, displayChange);
|
this, this, null /* remoteTransition */, displayChange);
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
|
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);
|
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
|
||||||
controller.mTransitionMetricsReporter.associate(t,
|
controller.mTransitionMetricsReporter.associate(t,
|
||||||
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
|
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
|
||||||
|
|||||||
@@ -548,7 +548,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
|||||||
for (int i = 0; i < mTargetDisplays.size(); ++i) {
|
for (int i = 0; i < mTargetDisplays.size(); ++i) {
|
||||||
final DisplayContent dc = mTargetDisplays.get(i);
|
final DisplayContent dc = mTargetDisplays.get(i);
|
||||||
final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
|
final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
|
||||||
if (asyncRotationController != null) {
|
if (asyncRotationController != null && mTargets.contains(dc)) {
|
||||||
asyncRotationController.onTransitionFinished();
|
asyncRotationController.onTransitionFinished();
|
||||||
}
|
}
|
||||||
if (mTransientLaunches != null) {
|
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
|
// This is non-null only if display has changes. It handles the visible windows that don't
|
||||||
// need to be participated in the transition.
|
// need to be participated in the transition.
|
||||||
final AsyncRotationController controller = dc.getAsyncRotationController();
|
final AsyncRotationController controller = dc.getAsyncRotationController();
|
||||||
if (controller != null) {
|
if (controller != null && mTargets.contains(dc)) {
|
||||||
controller.setupStartTransaction(transaction);
|
controller.setupStartTransaction(transaction);
|
||||||
}
|
}
|
||||||
mStartTransaction = transaction;
|
mStartTransaction = transaction;
|
||||||
|
|||||||
@@ -519,11 +519,6 @@ class TransitionController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSeamlessRotation(@NonNull WindowContainer wc) {
|
|
||||||
if (mCollectingTransition == null) return;
|
|
||||||
mCollectingTransition.setSeamlessRotation(wc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
|
void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
|
||||||
final Transition transition = Transition.fromBinder(token);
|
final Transition transition = Transition.fromBinder(token);
|
||||||
if (transition == null || !mPlayingTransitions.contains(transition)) {
|
if (transition == null || !mPlayingTransitions.contains(transition)) {
|
||||||
|
|||||||
@@ -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.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;
|
||||||
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
|
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
|
||||||
@@ -557,11 +558,20 @@ public class TransitionTests extends WindowTestsBase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAppTransitionWithRotationChange() {
|
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");
|
final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
|
||||||
makeWindowVisible(statusBar);
|
makeWindowVisible(statusBar);
|
||||||
mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
|
mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
|
||||||
final ActivityRecord app = createActivityRecord(mDisplayContent);
|
final ActivityRecord app = createActivityRecord(mDisplayContent);
|
||||||
final TestTransitionPlayer player = registerTestTransitionPlayer();
|
|
||||||
final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
|
final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
|
||||||
app.mTransitionController.requestStartTransition(transition, app.getTask(),
|
app.mTransitionController.requestStartTransition(transition, app.getTask(),
|
||||||
null /* remoteTransition */, null /* displayChange */);
|
null /* remoteTransition */, null /* displayChange */);
|
||||||
@@ -605,6 +615,50 @@ public class TransitionTests extends WindowTestsBase {
|
|||||||
assertNull(mDisplayContent.getAsyncRotationController());
|
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
|
@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