Merge "Migrate hybrid rotation animation with shell transition"
This commit is contained in:
committed by
Android (Google) Code Review
commit
82e737c0e4
@@ -1585,8 +1585,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
|
||||
@Override
|
||||
boolean isSyncFinished() {
|
||||
if (mDisplayRotation.isWaitingForRemoteRotation()) return false;
|
||||
return super.isSyncFinished();
|
||||
// Do not consider children because if they are requested to be synced, they should be
|
||||
// added to sync group explicitly.
|
||||
return !mDisplayRotation.isWaitingForRemoteRotation();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1876,16 +1877,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
}
|
||||
}
|
||||
|
||||
/** Shows the given window which may be hidden for screen frozen. */
|
||||
void finishFadeRotationAnimation(WindowState w) {
|
||||
/** Shows the given window which may be hidden for screen rotation. */
|
||||
void finishFadeRotationAnimation(WindowToken windowToken) {
|
||||
final FadeRotationAnimationController controller = mFadeRotationAnimationController;
|
||||
if (controller != null && controller.show(w.mToken)) {
|
||||
if (controller != null && controller.show(windowToken)) {
|
||||
mFadeRotationAnimationController = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns {@code true} if the display should wait for the given window to stop freezing. */
|
||||
boolean waitForUnfreeze(WindowState w) {
|
||||
/** Returns {@code true} if the screen rotation animation needs to wait for the window. */
|
||||
boolean shouldSyncRotationChange(WindowState w) {
|
||||
if (w.mForceSeamlesslyRotate) {
|
||||
// The window should look no different before and after rotation.
|
||||
return false;
|
||||
@@ -3230,6 +3231,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
|
||||
controller.mTransitionMetricsReporter.associate(t,
|
||||
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
|
||||
startFadeRotationAnimation(false /* shouldDebounce */);
|
||||
}
|
||||
t.setKnownConfigChanges(this, changes);
|
||||
}
|
||||
|
||||
@@ -595,12 +595,8 @@ public class DisplayRotation {
|
||||
// Go through all tasks and collect them before the rotation
|
||||
// TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper
|
||||
// handling is synchronized.
|
||||
mDisplayContent.forAllTasks(task -> {
|
||||
if (task.isVisible()) {
|
||||
mDisplayContent.mTransitionController.collect(task);
|
||||
}
|
||||
});
|
||||
mDisplayContent.getInsetsStateController().addProvidersToTransition();
|
||||
mDisplayContent.mTransitionController.collectForDisplayChange(mDisplayContent,
|
||||
null /* use collecting transition */);
|
||||
}
|
||||
mService.mAtmService.deferWindowLayout();
|
||||
try {
|
||||
|
||||
@@ -36,10 +36,12 @@ import java.io.PrintWriter;
|
||||
* An animation controller to fade-in/out for a window token.
|
||||
*/
|
||||
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;
|
||||
mContext = displayContent.mWmService.mContext;
|
||||
}
|
||||
|
||||
@@ -69,7 +71,9 @@ public class FadeAnimationController {
|
||||
return;
|
||||
}
|
||||
|
||||
final FadeAnimationAdapter animationAdapter = createAdapter(show, windowToken);
|
||||
final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
|
||||
final FadeAnimationAdapter animationAdapter = animation != null
|
||||
? createAdapter(createAnimationSpec(animation), show, windowToken) : null;
|
||||
if (animationAdapter == null) {
|
||||
return;
|
||||
}
|
||||
@@ -86,17 +90,10 @@ public class FadeAnimationController {
|
||||
show /* hidden */, animationType, finishedCallback);
|
||||
}
|
||||
|
||||
protected FadeAnimationAdapter createAdapter(boolean show, WindowToken windowToken) {
|
||||
final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
|
||||
if (animation == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
|
||||
createAnimationSpec(animation);
|
||||
|
||||
return new FadeAnimationAdapter(
|
||||
windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken);
|
||||
protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
|
||||
boolean show, WindowToken windowToken) {
|
||||
return new FadeAnimationAdapter(animationSpec, windowToken.getSurfaceAnimationRunner(),
|
||||
show, windowToken);
|
||||
}
|
||||
|
||||
protected LocalAnimationAdapter.AnimationSpec createAnimationSpec(
|
||||
@@ -140,7 +137,7 @@ public class FadeAnimationController {
|
||||
|
||||
protected class FadeAnimationAdapter extends LocalAnimationAdapter {
|
||||
protected final boolean mShow;
|
||||
private final WindowToken mToken;
|
||||
protected final WindowToken mToken;
|
||||
|
||||
FadeAnimationAdapter(AnimationSpec windowAnimationSpec,
|
||||
SurfaceAnimationRunner surfaceAnimationRunner, boolean show,
|
||||
|
||||
@@ -18,6 +18,10 @@ package com.android.server.wm;
|
||||
|
||||
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
|
||||
|
||||
import android.os.HandlerExecutor;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
@@ -33,10 +37,11 @@ import java.util.ArrayList;
|
||||
*/
|
||||
public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
|
||||
private final ArrayList<WindowToken> mTargetWindowTokens = new ArrayList<>();
|
||||
/** The map of window token to its animation leash. */
|
||||
private final ArrayMap<WindowToken, SurfaceControl> mTargetWindowTokens = new ArrayMap<>();
|
||||
private final WindowManagerService mService;
|
||||
/** If non-null, it usually indicates that there will be a screen rotation animation. */
|
||||
private final Runnable mFrozenTimeoutRunnable;
|
||||
private final Runnable mTimeoutRunnable;
|
||||
private final WindowToken mNavBarToken;
|
||||
|
||||
/** A runnable which gets called when the {@link #show()} is called. */
|
||||
@@ -45,16 +50,30 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
/** Whether to use constant zero alpha animation. */
|
||||
private boolean mHideImmediately;
|
||||
|
||||
/** Whether this controller is triggered from shell transition. */
|
||||
private final boolean mIsChangeTransition;
|
||||
|
||||
/** Whether the start transaction of the transition is committed (by shell). */
|
||||
private boolean mIsStartTransactionCommitted;
|
||||
|
||||
/** The list to store the drawn tokens before the rotation animation starts. */
|
||||
private ArrayList<WindowToken> mPendingShowTokens;
|
||||
|
||||
public FadeRotationAnimationController(DisplayContent displayContent) {
|
||||
super(displayContent);
|
||||
mService = displayContent.mWmService;
|
||||
mFrozenTimeoutRunnable = mService.mDisplayFrozen ? () -> {
|
||||
mIsChangeTransition = displayContent.inTransition()
|
||||
&& displayContent.mTransitionController.getCollectingTransitionType()
|
||||
== WindowManager.TRANSIT_CHANGE;
|
||||
mIsStartTransactionCommitted = !mIsChangeTransition;
|
||||
mTimeoutRunnable = displayContent.getRotationAnimation() != null
|
||||
|| mIsChangeTransition ? () -> {
|
||||
synchronized (mService.mGlobalLock) {
|
||||
displayContent.finishFadeRotationAnimationIfPossible();
|
||||
mService.mWindowPlacerLocked.performSurfacePlacement();
|
||||
}
|
||||
} : null;
|
||||
if (mFrozenTimeoutRunnable != null) {
|
||||
if (mTimeoutRunnable != null) {
|
||||
// Hide the windows immediately because screen should have been covered by screenshot.
|
||||
mHideImmediately = true;
|
||||
}
|
||||
@@ -68,7 +87,7 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
// Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation
|
||||
// bar is currently controlled by recents animation.
|
||||
if (!displayPolicy.navigationBarCanMove() && !navBarControlledByRecents) {
|
||||
mTargetWindowTokens.add(mNavBarToken);
|
||||
mTargetWindowTokens.put(mNavBarToken, null);
|
||||
}
|
||||
} else {
|
||||
mNavBarToken = null;
|
||||
@@ -79,7 +98,7 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
if (w.mActivityRecord == null && w.mHasSurface && !w.mForceSeamlesslyRotate
|
||||
&& !w.mIsWallpaper && !w.mIsImWindow && w != navigationBar
|
||||
&& w != notificationShade) {
|
||||
mTargetWindowTokens.add(w.mToken);
|
||||
mTargetWindowTokens.put(w.mToken, null);
|
||||
}
|
||||
}, true /* traverseTopToBottom */);
|
||||
}
|
||||
@@ -87,12 +106,13 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
/** Applies show animation on the previously hidden window tokens. */
|
||||
void show() {
|
||||
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
|
||||
final WindowToken windowToken = mTargetWindowTokens.get(i);
|
||||
final WindowToken windowToken = mTargetWindowTokens.keyAt(i);
|
||||
fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
|
||||
}
|
||||
mTargetWindowTokens.clear();
|
||||
if (mFrozenTimeoutRunnable != null) {
|
||||
mService.mH.removeCallbacks(mFrozenTimeoutRunnable);
|
||||
mPendingShowTokens = null;
|
||||
if (mTimeoutRunnable != null) {
|
||||
mService.mH.removeCallbacks(mTimeoutRunnable);
|
||||
}
|
||||
if (mOnShowRunnable != null) {
|
||||
mOnShowRunnable.run();
|
||||
@@ -105,10 +125,22 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
* controller is created for normal rotation.
|
||||
*/
|
||||
boolean show(WindowToken token) {
|
||||
if (mFrozenTimeoutRunnable != null && mTargetWindowTokens.remove(token)) {
|
||||
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);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (mTimeoutRunnable != null && mTargetWindowTokens.remove(token) != null) {
|
||||
fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM);
|
||||
if (mTargetWindowTokens.isEmpty()) {
|
||||
mService.mH.removeCallbacks(mFrozenTimeoutRunnable);
|
||||
mService.mH.removeCallbacks(mTimeoutRunnable);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -118,11 +150,11 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
/** Applies hide animation on the window tokens which may be seamlessly rotated later. */
|
||||
void hide() {
|
||||
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
|
||||
final WindowToken windowToken = mTargetWindowTokens.get(i);
|
||||
final WindowToken windowToken = mTargetWindowTokens.keyAt(i);
|
||||
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
|
||||
}
|
||||
if (mFrozenTimeoutRunnable != null) {
|
||||
mService.mH.postDelayed(mFrozenTimeoutRunnable,
|
||||
if (mTimeoutRunnable != null) {
|
||||
mService.mH.postDelayed(mTimeoutRunnable,
|
||||
WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION);
|
||||
}
|
||||
}
|
||||
@@ -131,7 +163,6 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
void hideImmediately(WindowToken windowToken) {
|
||||
final boolean original = mHideImmediately;
|
||||
mHideImmediately = true;
|
||||
mTargetWindowTokens.add(windowToken);
|
||||
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
|
||||
mHideImmediately = original;
|
||||
}
|
||||
@@ -143,16 +174,43 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
|
||||
/** Returns {@code true} if the controller will run fade animations on the window. */
|
||||
boolean isTargetToken(WindowToken token) {
|
||||
return mTargetWindowTokens.contains(token);
|
||||
return mTargetWindowTokens.containsKey(token);
|
||||
}
|
||||
|
||||
void setOnShowRunnable(Runnable onShowRunnable) {
|
||||
mOnShowRunnable = onShowRunnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts initial operation of leash to the transaction which will be executed when the
|
||||
* transition starts. And associate transaction callback to consume pending animations.
|
||||
*/
|
||||
void setupStartTransaction(SurfaceControl.Transaction t) {
|
||||
// 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);
|
||||
if (leash != null) {
|
||||
t.setAlpha(leash, 0f);
|
||||
}
|
||||
}
|
||||
// If there are windows have redrawn in new rotation but the start transaction has not
|
||||
// been applied yet, the fade-in animation will be deferred. So once the transaction is
|
||||
// committed, the fade-in animation can run with screen rotation animation.
|
||||
t.addTransactionCommittedListener(new HandlerExecutor(mService.mH), () -> {
|
||||
synchronized (mService.mGlobalLock) {
|
||||
mIsStartTransactionCommitted = true;
|
||||
if (mPendingShowTokens == null) return;
|
||||
for (int i = mPendingShowTokens.size() - 1; i >= 0; i--) {
|
||||
mDisplayContent.finishFadeRotationAnimation(mPendingShowTokens.get(i));
|
||||
}
|
||||
mPendingShowTokens = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animation getFadeInAnimation() {
|
||||
if (mFrozenTimeoutRunnable != null) {
|
||||
if (mTimeoutRunnable != null) {
|
||||
// Use a shorter animation so it is easier to align with screen rotation animation.
|
||||
return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter);
|
||||
}
|
||||
@@ -162,8 +220,28 @@ public class FadeRotationAnimationController extends FadeAnimationController {
|
||||
@Override
|
||||
public Animation getFadeOutAnimation() {
|
||||
if (mHideImmediately) {
|
||||
return new AlphaAnimation(0 /* fromAlpha */, 0 /* toAlpha */);
|
||||
// For change transition, the hide transaction needs to be applied with sync transaction
|
||||
// (setupStartTransaction). So keep alpha 1 just to get the animation leash.
|
||||
final float alpha = mIsChangeTransition ? 1 : 0;
|
||||
return new AlphaAnimation(alpha /* fromAlpha */, alpha /* toAlpha */);
|
||||
}
|
||||
return super.getFadeOutAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
|
||||
boolean show, WindowToken windowToken) {
|
||||
return new FadeAnimationAdapter(animationSpec, windowToken.getSurfaceAnimationRunner(),
|
||||
show, windowToken) {
|
||||
@Override
|
||||
public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
|
||||
int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
|
||||
// The fade cycle is done when showing, so only need to store the leash when hiding.
|
||||
if (!show) {
|
||||
mTargetWindowTokens.put(mToken, animationLeash);
|
||||
}
|
||||
super.startAnimation(animationLeash, t, type, finishCallback);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,16 +249,6 @@ class InsetsStateController {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void addProvidersToTransition() {
|
||||
for (int i = mProviders.size() - 1; i >= 0; --i) {
|
||||
final InsetsSourceProvider p = mProviders.valueAt(i);
|
||||
if (p == null) continue;
|
||||
final WindowContainer wc = p.mWin;
|
||||
if (wc == null) continue;
|
||||
mDisplayContent.mTransitionController.collect(wc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The provider of a specific type.
|
||||
*/
|
||||
|
||||
@@ -37,7 +37,6 @@ public class NavBarFadeAnimationController extends FadeAnimationController{
|
||||
private static final Interpolator FADE_OUT_INTERPOLATOR =
|
||||
new PathInterpolator(0.2f, 0f, 1f, 1f);
|
||||
|
||||
private DisplayContent mDisplayContent;
|
||||
private final WindowState mNavigationBar;
|
||||
private Animation mFadeInAnimation;
|
||||
private Animation mFadeOutAnimation;
|
||||
@@ -47,7 +46,6 @@ public class NavBarFadeAnimationController extends FadeAnimationController{
|
||||
|
||||
public NavBarFadeAnimationController(DisplayContent displayContent) {
|
||||
super(displayContent);
|
||||
mDisplayContent = displayContent;
|
||||
mNavigationBar = displayContent.getDisplayPolicy().getNavigationBar();
|
||||
mFadeInAnimation = new AlphaAnimation(0f, 1f);
|
||||
mFadeInAnimation.setDuration(FADE_IN_DURATION);
|
||||
@@ -69,16 +67,10 @@ public class NavBarFadeAnimationController extends FadeAnimationController{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FadeAnimationAdapter createAdapter(boolean show, WindowToken windowToken) {
|
||||
final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
|
||||
if (animation == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
|
||||
createAnimationSpec(animation);
|
||||
protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
|
||||
boolean show, WindowToken windowToken) {
|
||||
return new NavFadeAnimationAdapter(
|
||||
windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken,
|
||||
animationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken,
|
||||
show ? mFadeInParent : mFadeOutParent);
|
||||
}
|
||||
|
||||
|
||||
@@ -596,6 +596,12 @@ 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 FadeRotationAnimationController controller = dc.getFadeRotationAnimationController();
|
||||
if (controller != null) {
|
||||
controller.setupStartTransaction(transaction);
|
||||
}
|
||||
mStartTransaction = transaction;
|
||||
mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
|
||||
buildFinishTransaction(mFinishTransaction, info.getRootLeash());
|
||||
|
||||
@@ -337,6 +337,29 @@ class TransitionController {
|
||||
mCollectingTransition.collectExistenceChange(wc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the window containers which need to be synced with the changing display (e.g.
|
||||
* rotating) to the given transition or the current collecting transition.
|
||||
*/
|
||||
void collectForDisplayChange(@NonNull DisplayContent dc, @Nullable Transition incoming) {
|
||||
if (incoming == null) incoming = mCollectingTransition;
|
||||
if (incoming == null) return;
|
||||
final Transition transition = incoming;
|
||||
// Collect all visible tasks.
|
||||
dc.forAllLeafTasks(task -> {
|
||||
if (task.isVisible()) {
|
||||
transition.collect(task);
|
||||
}
|
||||
}, true /* traverseTopToBottom */);
|
||||
// Collect all visible non-app windows which need to be drawn before the animation starts.
|
||||
dc.forAllWindows(w -> {
|
||||
if (w.mActivityRecord == null && w.isVisible() && !inTransition(w.mToken)
|
||||
&& dc.shouldSyncRotationChange(w)) {
|
||||
transition.collect(w.mToken);
|
||||
}
|
||||
}, true /* traverseTopToBottom */);
|
||||
}
|
||||
|
||||
/** @see Transition#setOverrideAnimation */
|
||||
void setOverrideAnimation(TransitionInfo.AnimationOptions options,
|
||||
@Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) {
|
||||
|
||||
@@ -335,10 +335,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
// Go through all tasks and collect them before the rotation
|
||||
// TODO(shell-transitions): move collect() to onConfigurationChange once
|
||||
// wallpaper handling is synchronized.
|
||||
dc.forAllTasks(task -> {
|
||||
if (task.isVisible()) transition.collect(task);
|
||||
});
|
||||
dc.getInsetsStateController().addProvidersToTransition();
|
||||
dc.mTransitionController.collectForDisplayChange(dc, transition);
|
||||
dc.sendNewConfiguration();
|
||||
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
||||
}
|
||||
|
||||
@@ -1473,12 +1473,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
if (changing) {
|
||||
mLastFreezeDuration = 0;
|
||||
if (mWmService.mRoot.mOrientationChangeComplete
|
||||
&& mDisplayContent.waitForUnfreeze(this)) {
|
||||
&& mDisplayContent.shouldSyncRotationChange(this)) {
|
||||
mWmService.mRoot.mOrientationChangeComplete = false;
|
||||
}
|
||||
} else {
|
||||
// The orientation change is completed. If it was hidden by the animation, reshow it.
|
||||
mDisplayContent.finishFadeRotationAnimation(this);
|
||||
mDisplayContent.finishFadeRotationAnimation(mToken);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -566,7 +566,7 @@ class WindowStateAnimator {
|
||||
|
||||
if (w.getOrientationChanging()) {
|
||||
if (!w.isDrawn()) {
|
||||
if (w.mDisplayContent.waitForUnfreeze(w)) {
|
||||
if (w.mDisplayContent.shouldSyncRotationChange(w)) {
|
||||
w.mWmService.mRoot.mOrientationChangeComplete = false;
|
||||
mAnimator.mLastWindowFreezeSource = w;
|
||||
}
|
||||
|
||||
@@ -693,11 +693,8 @@ class WindowToken extends WindowContainer<WindowState> {
|
||||
@Override
|
||||
public String toString() {
|
||||
if (stringName == null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("WindowToken{");
|
||||
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
||||
sb.append(" "); sb.append(token); sb.append('}');
|
||||
stringName = sb.toString();
|
||||
stringName = "WindowToken{" + Integer.toHexString(System.identityHashCode(this))
|
||||
+ " type=" + windowType + " " + token + "}";
|
||||
}
|
||||
return stringName;
|
||||
}
|
||||
|
||||
@@ -1336,7 +1336,6 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
spyOn(rotationAnim);
|
||||
// Assume that the display rotation is changed so it is frozen in preparation for animation.
|
||||
doReturn(true).when(rotationAnim).hasScreenshot();
|
||||
mWm.mDisplayFrozen = true;
|
||||
displayContent.getDisplayRotation().setRotation((displayContent.getRotation() + 1) % 4);
|
||||
displayContent.setRotationAnimation(rotationAnim);
|
||||
// The fade rotation animation also starts to hide some non-app windows.
|
||||
@@ -1347,9 +1346,9 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
w.setOrientationChanging(true);
|
||||
}
|
||||
// The display only waits for the app window to unfreeze.
|
||||
assertFalse(displayContent.waitForUnfreeze(statusBar));
|
||||
assertFalse(displayContent.waitForUnfreeze(navBar));
|
||||
assertTrue(displayContent.waitForUnfreeze(app));
|
||||
assertFalse(displayContent.shouldSyncRotationChange(statusBar));
|
||||
assertFalse(displayContent.shouldSyncRotationChange(navBar));
|
||||
assertTrue(displayContent.shouldSyncRotationChange(app));
|
||||
// If all windows animated by fade rotation animation have done the orientation change,
|
||||
// the animation controller should be cleared.
|
||||
statusBar.setOrientationChanging(false);
|
||||
|
||||
@@ -19,6 +19,9 @@ 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.TYPE_INPUT_METHOD;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
@@ -32,7 +35,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -44,6 +49,7 @@ import android.platform.test.annotations.Presubmit;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.TransactionCommittedListener;
|
||||
import android.window.ITaskOrganizer;
|
||||
import android.window.ITransitionPlayer;
|
||||
import android.window.TransitionInfo;
|
||||
@@ -52,6 +58,7 @@ import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -468,6 +475,54 @@ public class TransitionTests extends WindowTestsBase {
|
||||
assertTrue(awaitInWmLock(() -> latch.await(3, TimeUnit.SECONDS)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisplayRotationChange() {
|
||||
final Task task = createActivityRecord(mDisplayContent).getTask();
|
||||
final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
|
||||
final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
|
||||
final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
|
||||
final WindowState[] windows = { statusBar, navBar, ime };
|
||||
makeWindowVisible(windows);
|
||||
mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
|
||||
mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
|
||||
final TestTransitionPlayer player = registerTestTransitionPlayer();
|
||||
|
||||
mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
|
||||
mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */);
|
||||
final FadeRotationAnimationController fadeController =
|
||||
mDisplayContent.getFadeRotationAnimationController();
|
||||
assertNotNull(fadeController);
|
||||
for (WindowState w : windows) {
|
||||
w.setOrientationChanging(true);
|
||||
}
|
||||
player.startTransition();
|
||||
|
||||
assertFalse(statusBar.mToken.inTransition());
|
||||
assertTrue(ime.mToken.inTransition());
|
||||
assertTrue(task.inTransition());
|
||||
|
||||
// Status bar finishes drawing before the start transaction. Its fade-in animation will be
|
||||
// executed until the transaction is committed, so it is still in target tokens.
|
||||
statusBar.setOrientationChanging(false);
|
||||
assertTrue(fadeController.isTargetToken(statusBar.mToken));
|
||||
|
||||
final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
|
||||
final ArgumentCaptor<TransactionCommittedListener> listenerCaptor =
|
||||
ArgumentCaptor.forClass(TransactionCommittedListener.class);
|
||||
player.onTransactionReady(startTransaction);
|
||||
|
||||
verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
|
||||
// The transaction is committed, so fade-in animation for status bar is consumed.
|
||||
listenerCaptor.getValue().onTransactionCommitted();
|
||||
assertFalse(fadeController.isTargetToken(statusBar.mToken));
|
||||
|
||||
// Status bar finishes drawing after the start transaction, so its fade-in animation can
|
||||
// execute directly.
|
||||
navBar.setOrientationChanging(false);
|
||||
assertFalse(fadeController.isTargetToken(navBar.mToken));
|
||||
assertNull(mDisplayContent.getFadeRotationAnimationController());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntermediateVisibility() {
|
||||
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
|
||||
|
||||
@@ -1660,10 +1660,17 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
||||
mLastRequest = request;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
void startTransition() {
|
||||
mOrganizer.startTransition(mLastRequest.getType(), mLastTransit, null);
|
||||
mLastTransit.onTransactionReady(mLastTransit.getSyncId(),
|
||||
mock(SurfaceControl.Transaction.class));
|
||||
}
|
||||
|
||||
void onTransactionReady(SurfaceControl.Transaction t) {
|
||||
mLastTransit.onTransactionReady(mLastTransit.getSyncId(), t);
|
||||
}
|
||||
|
||||
void start() {
|
||||
startTransition();
|
||||
onTransactionReady(mock(SurfaceControl.Transaction.class));
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
|
||||
Reference in New Issue
Block a user