Merge "Handle non-app windows in app rotation transition asynchronously"
This commit is contained in:
@@ -1843,6 +1843,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns {@code true} if the decided new rotation has not applied to configuration yet. */
|
||||
private boolean isRotationChanging() {
|
||||
return mDisplayRotation.getRotation() != getWindowConfiguration().getRotation();
|
||||
}
|
||||
|
||||
private void startFadeRotationAnimationIfNeeded() {
|
||||
if (isRotationChanging()) {
|
||||
startFadeRotationAnimation(false /* shouldDebounce */);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the hide animation for the windows which will be rotated seamlessly.
|
||||
*
|
||||
@@ -3189,11 +3200,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
|
||||
// Hide the windows which are not significant in rotation animation. So that the windows
|
||||
// don't need to block the unfreeze time.
|
||||
if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()
|
||||
// Do not fade for freezing without rotation change.
|
||||
&& mDisplayRotation.getRotation() != getWindowConfiguration().getRotation()
|
||||
&& mFadeRotationAnimationController == null) {
|
||||
startFadeRotationAnimation(false /* shouldDebounce */);
|
||||
if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {
|
||||
startFadeRotationAnimationIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3214,6 +3222,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
}
|
||||
if (!controller.isCollecting(this)) {
|
||||
controller.collect(this);
|
||||
startFadeRotationAnimationIfNeeded();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3221,7 +3230,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
this, this, null /* remoteTransition */, displayChange);
|
||||
if (t != null) {
|
||||
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
|
||||
if (getRotation() != getWindowConfiguration().getRotation()) {
|
||||
if (isRotationChanging()) {
|
||||
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
|
||||
controller.mTransitionMetricsReporter.associate(t,
|
||||
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
|
||||
|
||||
@@ -21,7 +21,6 @@ import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.animation.Animation;
|
||||
@@ -38,7 +37,6 @@ import java.io.PrintWriter;
|
||||
public class FadeAnimationController {
|
||||
protected final DisplayContent mDisplayContent;
|
||||
protected final Context mContext;
|
||||
protected final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();
|
||||
|
||||
public FadeAnimationController(DisplayContent displayContent) {
|
||||
mDisplayContent = displayContent;
|
||||
@@ -78,16 +76,8 @@ public class FadeAnimationController {
|
||||
return;
|
||||
}
|
||||
|
||||
// We deferred the end of the animation when hiding the token, so we need to end it now that
|
||||
// it's shown again.
|
||||
final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
|
||||
final Runnable runnable = mDeferredFinishCallbacks.remove(windowToken);
|
||||
if (runnable != null) {
|
||||
runnable.run();
|
||||
}
|
||||
} : null;
|
||||
windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter,
|
||||
show /* hidden */, animationType, finishedCallback);
|
||||
show /* hidden */, animationType, null /* finishedCallback */);
|
||||
}
|
||||
|
||||
protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
|
||||
@@ -135,7 +125,7 @@ public class FadeAnimationController {
|
||||
};
|
||||
}
|
||||
|
||||
protected class FadeAnimationAdapter extends LocalAnimationAdapter {
|
||||
protected static class FadeAnimationAdapter extends LocalAnimationAdapter {
|
||||
protected final boolean mShow;
|
||||
protected final WindowToken mToken;
|
||||
|
||||
@@ -149,13 +139,10 @@ public class FadeAnimationController {
|
||||
|
||||
@Override
|
||||
public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
|
||||
// We defer the end of the hide animation to ensure the tokens stay hidden until
|
||||
// we show them again.
|
||||
if (!mShow) {
|
||||
mDeferredFinishCallbacks.put(mToken, endDeferFinishCallback);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// Defer the finish callback (restore leash) of the hide animation to ensure the token
|
||||
// stay hidden until it needs to show again. Besides, when starting the show animation,
|
||||
// the previous hide animation will be cancelled, so the callback can be ignored.
|
||||
return !mShow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
/** Whether to use constant zero alpha animation. */
|
||||
private boolean mHideImmediately;
|
||||
|
||||
/** Whether this controller is triggered from shell transition. */
|
||||
/** Whether this controller is triggered from shell transition with type CHANGE. */
|
||||
private final boolean mIsChangeTransition;
|
||||
|
||||
/** Whether the start transaction of the transition is committed (by shell). */
|
||||
@@ -59,21 +59,30 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
/** The list to store the drawn tokens before the rotation animation starts. */
|
||||
private ArrayList<WindowToken> mPendingShowTokens;
|
||||
|
||||
/** It is used when the display has rotated, but some windows fade out in old rotation. */
|
||||
private SeamlessRotator mRotator;
|
||||
|
||||
private final int mOriginalRotation;
|
||||
private final boolean mHasScreenRotationAnimation;
|
||||
|
||||
public FadeRotationAnimationController(DisplayContent displayContent) {
|
||||
super(displayContent);
|
||||
mService = displayContent.mWmService;
|
||||
mIsChangeTransition = displayContent.inTransition()
|
||||
&& displayContent.mTransitionController.getCollectingTransitionType()
|
||||
== WindowManager.TRANSIT_CHANGE;
|
||||
mOriginalRotation = displayContent.getWindowConfiguration().getRotation();
|
||||
final int transitionType =
|
||||
displayContent.mTransitionController.getCollectingTransitionType();
|
||||
mIsChangeTransition = transitionType == WindowManager.TRANSIT_CHANGE;
|
||||
// Only CHANGE type (rotation animation) needs to wait for the start transaction.
|
||||
mIsStartTransactionCommitted = !mIsChangeTransition;
|
||||
mTimeoutRunnable = displayContent.getRotationAnimation() != null
|
||||
|| mIsChangeTransition ? () -> {
|
||||
mTimeoutRunnable = displayContent.inTransition() ? () -> {
|
||||
synchronized (mService.mGlobalLock) {
|
||||
displayContent.finishFadeRotationAnimationIfPossible();
|
||||
mService.mWindowPlacerLocked.performSurfacePlacement();
|
||||
}
|
||||
} : null;
|
||||
if (mTimeoutRunnable != null) {
|
||||
mHasScreenRotationAnimation =
|
||||
displayContent.getRotationAnimation() != null || mIsChangeTransition;
|
||||
if (mHasScreenRotationAnimation) {
|
||||
// Hide the windows immediately because screen should have been covered by screenshot.
|
||||
mHideImmediately = true;
|
||||
}
|
||||
@@ -103,6 +112,19 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
}, true /* traverseTopToBottom */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
|
||||
if (show) {
|
||||
final SurfaceControl leash = mTargetWindowTokens.remove(windowToken);
|
||||
if (leash != null && mRotator != null) {
|
||||
// The leash was unrotated by start transaction of transition. Clear the transform
|
||||
// to reshow the window in current rotation.
|
||||
mRotator.setIdentityMatrix(mDisplayContent.getPendingTransaction(), leash);
|
||||
}
|
||||
}
|
||||
super.fadeWindowToken(show, windowToken, animationType);
|
||||
}
|
||||
|
||||
/** Applies show animation on the previously hidden window tokens. */
|
||||
void show() {
|
||||
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
|
||||
@@ -125,19 +147,23 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
* controller is created for normal rotation.
|
||||
*/
|
||||
boolean show(WindowToken token) {
|
||||
if (!isTargetToken(token)) return false;
|
||||
if (!mIsStartTransactionCommitted) {
|
||||
// The fade-in animation should only start after the screenshot layer is shown by shell.
|
||||
// Otherwise the window will be blinking before the rotation animation starts. So store
|
||||
// to a pending list and animate them until the transaction is committed.
|
||||
if (mTargetWindowTokens.containsKey(token)) {
|
||||
if (mPendingShowTokens == null) {
|
||||
mPendingShowTokens = new ArrayList<>();
|
||||
}
|
||||
mPendingShowTokens.add(token);
|
||||
if (mPendingShowTokens == null) {
|
||||
mPendingShowTokens = new ArrayList<>();
|
||||
}
|
||||
mPendingShowTokens.add(token);
|
||||
return false;
|
||||
}
|
||||
if (mTimeoutRunnable != null && mTargetWindowTokens.remove(token) != null) {
|
||||
if (!mHasScreenRotationAnimation && token.mTransitionController.inTransition()) {
|
||||
// Defer showing to onTransitionFinished().
|
||||
return false;
|
||||
}
|
||||
// If the timeout runnable is null (fixed rotation), the case will be handled by show().
|
||||
if (mTimeoutRunnable != null) {
|
||||
fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM);
|
||||
if (mTargetWindowTokens.isEmpty()) {
|
||||
mService.mH.removeCallbacks(mTimeoutRunnable);
|
||||
@@ -177,6 +203,15 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
return mTargetWindowTokens.containsKey(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the insets animation leash should use previous position when running fade out
|
||||
* animation in rotated display.
|
||||
*/
|
||||
boolean shouldFreezeInsetsPosition(WindowState w) {
|
||||
return !mHasScreenRotationAnimation && w.mTransitionController.inTransition()
|
||||
&& isTargetToken(w.mToken);
|
||||
}
|
||||
|
||||
void setOnShowRunnable(Runnable onShowRunnable) {
|
||||
mOnShowRunnable = onShowRunnable;
|
||||
}
|
||||
@@ -186,6 +221,22 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
* transition starts. And associate transaction callback to consume pending animations.
|
||||
*/
|
||||
void setupStartTransaction(SurfaceControl.Transaction t) {
|
||||
if (!mIsChangeTransition) {
|
||||
// Take OPEN/CLOSE transition type as the example, the non-activity windows need to
|
||||
// fade out in previous rotation while display has rotated to the new rotation, so
|
||||
// their leashes are unrotated with the start transaction.
|
||||
mRotator = new SeamlessRotator(mOriginalRotation,
|
||||
mDisplayContent.getWindowConfiguration().getRotation(),
|
||||
mDisplayContent.getDisplayInfo(),
|
||||
false /* applyFixedTransformationHint */);
|
||||
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
|
||||
final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
|
||||
if (leash != null) {
|
||||
mRotator.applyTransform(t, leash);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Hide the windows immediately because a screenshot layer should cover the screen.
|
||||
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
|
||||
final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
|
||||
@@ -208,9 +259,30 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
});
|
||||
}
|
||||
|
||||
void onTransitionFinished() {
|
||||
if (mIsChangeTransition) {
|
||||
// With screen rotation animation, the windows are always faded in when they are drawn.
|
||||
// Because if they are drawn fast enough, the fade animation should not be observable.
|
||||
return;
|
||||
}
|
||||
// For other transition types, the fade-in animation runs after the transition to make the
|
||||
// transition animation (e.g. launch activity) look cleaner.
|
||||
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
|
||||
final WindowToken token = mTargetWindowTokens.keyAt(i);
|
||||
for (int j = token.getChildCount() - 1; j >= 0; j--) {
|
||||
// Only fade in the drawn windows. If the remaining windows are drawn later,
|
||||
// show(WindowToken) will be called to fade in them.
|
||||
if (token.getChildAt(j).isDrawFinishedLw()) {
|
||||
mDisplayContent.finishFadeRotationAnimation(token);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animation getFadeInAnimation() {
|
||||
if (mTimeoutRunnable != null) {
|
||||
if (mHasScreenRotationAnimation) {
|
||||
// Use a shorter animation so it is easier to align with screen rotation animation.
|
||||
return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter);
|
||||
}
|
||||
|
||||
@@ -297,6 +297,14 @@ class InsetsSourceProvider {
|
||||
}
|
||||
|
||||
private Point getWindowFrameSurfacePosition() {
|
||||
if (mControl != null) {
|
||||
final FadeRotationAnimationController fadeController =
|
||||
mWin.mDisplayContent.getFadeRotationAnimationController();
|
||||
if (fadeController != null && fadeController.shouldFreezeInsetsPosition(mWin)) {
|
||||
// Use previous position because the fade-out animation runs in old rotation.
|
||||
return mControl.getSurfacePosition();
|
||||
}
|
||||
}
|
||||
final Rect frame = mWin.getFrame();
|
||||
final Point position = new Point();
|
||||
mWin.transformFrameToSurfacePosition(frame.left, frame.top, position);
|
||||
|
||||
@@ -95,14 +95,6 @@ public class NavBarFadeAnimationController extends FadeAnimationController{
|
||||
} else {
|
||||
fadeAnim.run();
|
||||
}
|
||||
} else {
|
||||
// If fade rotation animation is running and controlling the nav bar, make sure we empty
|
||||
// the mDeferredFinishCallbacks and defer the runnable until fade rotation animation
|
||||
// finishes.
|
||||
final Runnable runnable = mDeferredFinishCallbacks.remove(mNavigationBar.mToken);
|
||||
if (runnable != null) {
|
||||
controller.setOnShowRunnable(runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import static android.view.Surface.ROTATION_270;
|
||||
import static android.view.Surface.ROTATION_90;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.os.IBinder;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.Surface.Rotation;
|
||||
import android.view.SurfaceControl;
|
||||
@@ -73,7 +72,7 @@ public class SeamlessRotator {
|
||||
* global display rotation.
|
||||
*/
|
||||
public void unrotate(Transaction transaction, WindowContainer win) {
|
||||
transaction.setMatrix(win.getSurfaceControl(), mTransform, mFloat9);
|
||||
applyTransform(transaction, win.getSurfaceControl());
|
||||
// WindowState sets the position of the window so transform the position and update it.
|
||||
final float[] winSurfacePos = {win.mLastSurfacePosition.x, win.mLastSurfacePosition.y};
|
||||
mTransform.mapPoints(winSurfacePos);
|
||||
@@ -83,6 +82,10 @@ public class SeamlessRotator {
|
||||
}
|
||||
}
|
||||
|
||||
void applyTransform(Transaction t, SurfaceControl sc) {
|
||||
t.setMatrix(sc, mTransform, mFloat9);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rotation of the display before it started rotating.
|
||||
*
|
||||
@@ -106,14 +109,17 @@ public class SeamlessRotator {
|
||||
return;
|
||||
}
|
||||
|
||||
mTransform.reset();
|
||||
t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
|
||||
setIdentityMatrix(t, win.mSurfaceControl);
|
||||
t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
|
||||
if (mApplyFixedTransformHint) {
|
||||
t.unsetFixedTransformHint(win.mSurfaceControl);
|
||||
}
|
||||
}
|
||||
|
||||
void setIdentityMatrix(Transaction t, SurfaceControl sc) {
|
||||
t.setMatrix(sc, Matrix.IDENTITY_MATRIX, mFloat9);
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw) {
|
||||
pw.print("{old="); pw.print(mOldRotation); pw.print(", new="); pw.print(mNewRotation);
|
||||
pw.print("}");
|
||||
|
||||
@@ -143,6 +143,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
/** The final animation targets derived from participants after promotion. */
|
||||
private ArraySet<WindowContainer> mTargets = null;
|
||||
|
||||
/** The main display running this transition. */
|
||||
private DisplayContent mTargetDisplay;
|
||||
|
||||
/**
|
||||
* Set of participating windowtokens (activity/wallpaper) which are visible at the end of
|
||||
* the transition animation.
|
||||
@@ -473,6 +476,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
|
||||
dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
|
||||
}
|
||||
|
||||
final FadeRotationAnimationController fadeRotationController =
|
||||
mTargetDisplay.getFadeRotationAnimationController();
|
||||
if (fadeRotationController != null) {
|
||||
fadeRotationController.onTransitionFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void abort() {
|
||||
@@ -514,6 +523,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
}
|
||||
}
|
||||
if (dc == null) dc = mController.mAtm.mRootWindowContainer.getDefaultDisplay();
|
||||
mTargetDisplay = dc;
|
||||
|
||||
if (mState == STATE_ABORT) {
|
||||
mController.abort(this);
|
||||
|
||||
@@ -524,6 +524,43 @@ public class TransitionTests extends WindowTestsBase {
|
||||
assertNull(mDisplayContent.getFadeRotationAnimationController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppTransitionWithRotationChange() {
|
||||
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 */);
|
||||
mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
|
||||
final int anyChanges = 1;
|
||||
mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */);
|
||||
transition.setKnownConfigChanges(mDisplayContent, anyChanges);
|
||||
final FadeRotationAnimationController fadeController =
|
||||
mDisplayContent.getFadeRotationAnimationController();
|
||||
assertNotNull(fadeController);
|
||||
assertTrue(fadeController.shouldFreezeInsetsPosition(statusBar));
|
||||
|
||||
statusBar.setOrientationChanging(true);
|
||||
player.startTransition();
|
||||
// Non-app windows should not be collected.
|
||||
assertFalse(statusBar.mToken.inTransition());
|
||||
assertTrue(app.getTask().inTransition());
|
||||
|
||||
final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
|
||||
player.onTransactionReady(startTransaction);
|
||||
// The leash should be unrotated.
|
||||
verify(startTransaction).setMatrix(eq(statusBar.mToken.getAnimationLeash()), any(), any());
|
||||
|
||||
// The redrawn window will be faded in when the transition finishes. And because this test
|
||||
// only use one non-activity window, the fade rotation controller should also be cleared.
|
||||
statusBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
|
||||
player.finish();
|
||||
assertNull(mDisplayContent.getFadeRotationAnimationController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntermediateVisibility() {
|
||||
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
|
||||
|
||||
Reference in New Issue
Block a user