Merge "Migrate hybrid rotation animation with shell transition"

This commit is contained in:
TreeHugger Robot
2021-12-02 15:49:39 +00:00
committed by Android (Google) Code Review
15 changed files with 223 additions and 84 deletions

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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);
}
};
}
}

View File

@@ -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.
*/

View File

@@ -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);
}

View File

@@ -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());

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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() {