diff options
| author | 2021-09-17 02:49:22 +0800 | |
|---|---|---|
| committer | 2021-09-23 12:36:27 +0000 | |
| commit | 570b2eb491155c222d452f6342c6efa0951b021b (patch) | |
| tree | e4a2d37e826a038bef3dab56580658d179ad763d | |
| parent | 9fbadb5a3bbcb5f34cb2c9e1a14098a6b4e3d206 (diff) | |
Use rotation transition to handle display config change
1. Support fade animation with rotation delta 0.
Rotation animation is also used to cover display configuration
change, e.g. density, font size.
2. Use normal rotation animation when app requests orientation,
e.g. from Activity#setRequestedOrientation.
3. Fix flickering of rotation animation
The layers are:
Transition Root
- Animation leash
- RotationLayer
- Windowing layer
So the higher layer should be leash instead of RotationLayer.
Bug: 199836343
Test: ShellTransitionTests
Test: setprop persist.debug.shell_transit 1; reboot
Change font size in Settings, there should be a fade
out animation for transiting to new configuration.
Toggle portrait and landscape from app without specifying
rotation animation type, seamless rotation should not
be used.
Change-Id: I7ec9553cc8df2b98f89b6fbda67c3834d018883b
9 files changed, 82 insertions, 32 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 4ba6acaba025..b673d48f3d15 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 @@ -141,8 +141,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { static boolean isRotationSeamless(@NonNull TransitionInfo info, DisplayController displayController) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, - "Display is rotating, check if it should be seamless."); + "Display is changing, check if it should be seamless."); boolean checkedDisplayLayout = false; + boolean hasTask = false; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); @@ -166,6 +167,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return false; } } else if (change.getTaskInfo() != null) { + hasTask = true; // We only enable seamless rotation if all the visible task windows requested it. if (change.getRotationAnimation() != ROTATION_ANIMATION_SEAMLESS) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, @@ -209,8 +211,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Rotation IS seamless."); - return true; + // ROTATION_ANIMATION_SEAMLESS can only be requested by task. + if (hasTask) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Rotation IS seamless."); + return true; + } + return false; } /** @@ -280,7 +286,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final TransitionInfo.Change change = info.getChanges().get(i); if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE - && (change.getEndRotation() != change.getStartRotation()) && (change.getFlags() & FLAG_IS_DISPLAY) != 0) { boolean isSeamless = isRotationSeamless(info, mDisplayController); final int anim = getRotationAnimation(info); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java index ada2ed27c114..13c670a1ab1e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java @@ -186,11 +186,11 @@ class ScreenRotationAnimation { t.setAlpha(mBackColorSurface, 1); t.show(mBackColorSurface); + t.setLayer(mAnimLeash, SCREEN_FREEZE_LAYER_BASE); t.setPosition(mAnimLeash, 0, 0); t.setAlpha(mAnimLeash, 1); t.show(mAnimLeash); - t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE); t.setBuffer(mScreenshotLayer, buffer); t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace()); t.show(mScreenshotLayer); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index b0f0d71c2638..e39171343bb9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -583,6 +583,13 @@ public class ShellTransitionTests { .setRotate(ROTATION_ANIMATION_SEAMLESS).build()) .build(); assertFalse(DefaultTransitionHandler.isRotationSeamless(seamlessButAlert, displays)); + + // Not seamless if there is no changed task. + final TransitionInfo noTask = new TransitionInfoBuilder(TRANSIT_CHANGE) + .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY) + .setRotate().build()) + .build(); + assertFalse(DefaultTransitionHandler.isRotationSeamless(noTask, displays)); } class TransitionInfoBuilder { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c45b661f06fb..b107ff8a6b7b 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5943,6 +5943,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void startFreezingScreen(int overrideOriginalDisplayRotation) { + if (mAtmService.getTransitionController().isShellTransitionsEnabled()) { + return; + } ProtoLog.i(WM_DEBUG_ORIENTATION, "Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s", appToken, isVisible(), mFreezingScreen, mVisibleRequested, @@ -8443,9 +8446,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) { // Aha, the activity isn't handling the change, so DIE DIE DIE. configChangeFlags |= changes; - if (!mAtmService.getTransitionController().isShellTransitionsEnabled()) { - startFreezingScreenLocked(globalChanges); - } + startFreezingScreenLocked(globalChanges); forceNewConfig = false; // Do not preserve window if it is freezing screen because the original window won't be // able to update drawn state that causes freeze timeout. diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 63f6387c87ae..f800f0e395de 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -76,6 +76,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; @@ -1389,11 +1390,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final Configuration currentDisplayConfig = getConfiguration(); mTmpConfiguration.setTo(currentDisplayConfig); computeScreenConfiguration(mTmpConfiguration); - configChanged |= currentDisplayConfig.diff(mTmpConfiguration) != 0; + final int changes = currentDisplayConfig.diff(mTmpConfiguration); + configChanged |= changes != 0; if (configChanged) { mWaitingForConfig = true; - mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this); + if (mAtmService.getTransitionController().isShellTransitionsEnabled()) { + requestChangeTransitionIfNeeded(changes); + } else { + mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this); + } sendNewConfiguration(); } @@ -3165,6 +3171,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mScreenRotationAnimation; } + /** + * Requests to start a transition for the display configuration change. The given changes must + * be non-zero. This method is no-op if the display has been collected. + */ + void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes) { + final TransitionController controller = mAtmService.getTransitionController(); + if (controller.isCollecting()) { + if (!controller.isCollecting(this)) { + controller.collect(this); + } + return; + } + final Transition t = controller.requestTransitionIfNeeded(TRANSIT_CHANGE, this); + if (t != null) { + t.setKnownConfigChanges(this, changes); + } + } + /** If the display is in transition, there should be a screenshot covering it. */ @Override boolean inTransition() { @@ -5724,6 +5748,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } mWmService.mDisplayNotificationController.dispatchDisplayChanged( this, getConfiguration()); + if (isReady() && mAtmService.getTransitionController().isShellTransitionsEnabled()) { + requestChangeTransitionIfNeeded(changes); + } } return changes; } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index c9db14de507c..971bebd8c486 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -22,7 +22,6 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFA import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; -import static android.view.WindowManager.TRANSIT_CHANGE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; @@ -492,12 +491,6 @@ public class DisplayRotation { recentsAnimationController.cancelAnimationForDisplayChange(); } - final Transition t = (useShellTransitions - && !mService.mAtmService.getTransitionController().isCollecting()) - ? mService.mAtmService.getTransitionController().createTransition(TRANSIT_CHANGE) - : null; - mService.mAtmService.getTransitionController().collect(mDisplayContent); - ProtoLog.v(WM_DEBUG_ORIENTATION, "Display id=%d rotation changed to %d from %d, lastOrientation=%d", displayId, rotation, oldRotation, lastOrientation); @@ -511,11 +504,10 @@ public class DisplayRotation { mDisplayContent.setLayoutNeeded(); if (useShellTransitions) { - if (t != null) { - // This created its own transition, so send a start request. - mService.mAtmService.getTransitionController().requestStartTransition( - t, null /* trigger */, null /* remote */); - } else { + final boolean wasInTransition = mDisplayContent.inTransition(); + mDisplayContent.requestChangeTransitionIfNeeded( + ActivityInfo.CONFIG_WINDOW_CONFIGURATION); + if (wasInTransition) { // Use remote-rotation infra since the transition has already been requested // TODO(shell-transitions): Remove this once lifecycle management can cover all // rotation cases. diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 1a46d0f9a877..1909875565f6 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -58,6 +58,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.content.pm.ActivityInfo; import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; @@ -269,6 +270,18 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mChanges.get(wc).mExistenceChanged = true; } + /** + * Specifies configuration change explicitly for the window container, so it can be chosen as + * transition target. This is usually used with transition mode + * {@link android.view.WindowManager#TRANSIT_CHANGE}. + */ + void setKnownConfigChanges(WindowContainer<?> wc, @ActivityInfo.Config int changes) { + final ChangeInfo changeInfo = mChanges.get(wc); + if (changeInfo != null) { + changeInfo.mKnownConfigChanges = changes; + } + } + private void sendRemoteCallback(@Nullable IRemoteCallback callback) { if (callback == null) return; mController.mAtm.mH.sendMessage(PooledLambda.obtainMessage(cb -> { @@ -1218,6 +1231,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final Rect mAbsoluteBounds = new Rect(); boolean mShowWallpaper; int mRotation = ROTATION_UNDEFINED; + @ActivityInfo.Config int mKnownConfigChanges; ChangeInfo(@NonNull WindowContainer origState) { mVisible = origState.isVisibleRequested(); @@ -1240,6 +1254,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final boolean currVisible = newState.isVisibleRequested(); if (currVisible == mVisible && !mVisible) return false; return currVisible != mVisible + || mKnownConfigChanges != 0 // if mWindowingMode is 0, this container wasn't attached at collect time, so // assume no change in windowing-mode. || (mWindowingMode != 0 && newState.getWindowingMode() != mWindowingMode) diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index c1d0f80adbb7..fc5423942dc3 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -71,15 +71,7 @@ class TransitionController { final Lock mRunningLock = new Lock(); - private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> { - // clean-up/finish any playing transitions. - for (int i = 0; i < mPlayingTransitions.size(); ++i) { - mPlayingTransitions.get(i).cleanUpOnFailure(); - } - mPlayingTransitions.clear(); - mTransitionPlayer = null; - mRunningLock.doNotifyLocked(); - }; + private final IBinder.DeathRecipient mTransitionPlayerDeath; /** The transition currently being constructed (collecting participants). */ private Transition mCollectingTransition = null; @@ -90,6 +82,17 @@ class TransitionController { TransitionController(ActivityTaskManagerService atm) { mAtm = atm; mStatusBar = LocalServices.getService(StatusBarManagerInternal.class); + mTransitionPlayerDeath = () -> { + synchronized (mAtm.mGlobalLock) { + // Clean-up/finish any playing transitions. + for (int i = 0; i < mPlayingTransitions.size(); ++i) { + mPlayingTransitions.get(i).cleanUpOnFailure(); + } + mPlayingTransitions.clear(); + mTransitionPlayer = null; + mRunningLock.doNotifyLocked(); + } + }; } /** @see #createTransition(int, int) */ diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 834b6e62305d..0f2d058907fb 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -326,7 +326,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (transition != null) { // First check if we have a display rotation transition and if so, update it. final DisplayContent dc = DisplayRotation.getDisplayFromTransition(transition); - if (dc != null && transition.mChanges.get(dc).mRotation != dc.getRotation()) { + if (dc != null && transition.mChanges.get(dc).hasChanged(dc)) { // Go through all tasks and collect them before the rotation // TODO(shell-transitions): move collect() to onConfigurationChange once // wallpaper handling is synchronized. |