diff options
10 files changed, 255 insertions, 78 deletions
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 597b34bf8554..956161acd762 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -41,9 +41,11 @@ interface IRecentsAnimationController { * with remote animation targets should be relinquished. If {@param moveHomeToTop} is true, then * the home activity should be moved to the top. Otherwise, the home activity is hidden and the * user is returned to the app. + * @param sendUserLeaveHint If set to true, {@link Activity#onUserLeaving} will be sent to the + * top resumed app, false otherwise. */ @UnsupportedAppUsage - void finish(boolean moveHomeToTop); + void finish(boolean moveHomeToTop, boolean sendUserLeaveHint); /** * Called by the handler to indicate that the recents animation input consumer should be diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 1d9105c35ac5..d2fe5cd9ef64 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -77,9 +77,15 @@ public class RecentsAnimationControllerCompat { } } - public void finish(boolean toHome) { + /** + * Finish the current recents animation. + * @param toHome Going to home or back to the previous app. + * @param sendUserLeaveHint determines whether userLeaveHint will be set true to the previous + * app. + */ + public void finish(boolean toHome, boolean sendUserLeaveHint) { try { - mAnimationController.finish(toHome); + mAnimationController.finish(toHome, sendUserLeaveHint); } catch (RemoteException e) { Log.e(TAG, "Failed to finish recents animation", e); } diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index f9980bebca9e..c1d872f23f0f 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -73,6 +73,13 @@ public class BoundsAnimationController { /** Schedule a PiP mode changed callback when this animation ends. */ public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2; + public static final int BOUNDS = 0; + public static final int FADE_IN = 1; + + @IntDef({BOUNDS, FADE_IN}) public @interface AnimationType {} + + private static final int FADE_IN_DURATION = 500; + // Only accessed on UI thread. private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>(); @@ -115,6 +122,7 @@ public class BoundsAnimationController { private boolean mFinishAnimationAfterTransition = false; private final AnimationHandler mAnimationHandler; private Choreographer mChoreographer; + private @AnimationType int mAnimationType; private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000; @@ -140,6 +148,7 @@ public class BoundsAnimationController { implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { private final BoundsAnimationTarget mTarget; + private final @AnimationType int mAnimationType; private final Rect mFrom = new Rect(); private final Rect mTo = new Rect(); private final Rect mTmpRect = new Rect(); @@ -166,8 +175,8 @@ public class BoundsAnimationController { // Depending on whether we are animating from // a smaller to a larger size - private final int mFrozenTaskWidth; - private final int mFrozenTaskHeight; + private int mFrozenTaskWidth; + private int mFrozenTaskHeight; // Timeout callback to ensure we continue the animation if waiting for resuming or app // windows drawn fails @@ -176,12 +185,13 @@ public class BoundsAnimationController { resume(); }; - BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to, - @SchedulePipModeChangedState int schedulePipModeChangedState, + BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from, + Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState, @SchedulePipModeChangedState int prevShedulePipModeChangedState, boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) { super(); mTarget = target; + mAnimationType = animationType; mFrom.set(from); mTo.set(to); mSchedulePipModeChangedState = schedulePipModeChangedState; @@ -195,12 +205,14 @@ public class BoundsAnimationController { // to their final size immediately so we can use scaling to make the window // larger. Likewise if we are going from bigger to smaller, we want to wait until // the end so we don't have to upscale from the smaller finished size. - if (animatingToLargerSize()) { - mFrozenTaskWidth = mTo.width(); - mFrozenTaskHeight = mTo.height(); - } else { - mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width(); - mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height(); + if (mAnimationType == BOUNDS) { + if (animatingToLargerSize()) { + mFrozenTaskWidth = mTo.width(); + mFrozenTaskHeight = mTo.height(); + } else { + mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width(); + mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height(); + } } } @@ -222,8 +234,9 @@ public class BoundsAnimationController { // otherwise. boolean continueAnimation; if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) { - continueAnimation = mTarget.onAnimationStart(mSchedulePipModeChangedState == - SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */); + continueAnimation = mTarget.onAnimationStart( + mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START, + false /* forceUpdate */, mAnimationType); // When starting an animation from fullscreen, pause here and wait for the // windows-drawn signal before we start the rest of the transition down into PiP. @@ -238,7 +251,8 @@ public class BoundsAnimationController { // However, we still need to report to them that they are leaving PiP, so this will // force an update via a mode changed callback. continueAnimation = mTarget.onAnimationStart( - true /* schedulePipModeChangedCallback */, true /* forceUpdate */); + true /* schedulePipModeChangedCallback */, true /* forceUpdate */, + mAnimationType); } else { // The animation is already running, but we should check that the TaskStack is still // valid before continuing with the animation @@ -285,6 +299,13 @@ public class BoundsAnimationController { @Override public void onAnimationUpdate(ValueAnimator animation) { final float value = (Float) animation.getAnimatedValue(); + if (mAnimationType == FADE_IN) { + if (!mTarget.setPinnedStackAlpha(value)) { + cancelAndCallAnimationEnd(); + } + return; + } + final float remains = 1 - value; mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f); mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f); @@ -408,16 +429,29 @@ public class BoundsAnimationController { public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to, int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen) { + boolean moveFromFullscreen, boolean moveToFullscreen, + @AnimationType int animationType) { animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState, - moveFromFullscreen, moveToFullscreen); + moveFromFullscreen, moveToFullscreen, animationType); } @VisibleForTesting BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to, int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen) { + boolean moveFromFullscreen, boolean moveToFullscreen, + @AnimationType int animationType) { final BoundsAnimator existing = mRunningAnimations.get(target); + // animateBoundsImpl gets called twice for each animation. The second time we get the final + // to rect that respects the shelf, which is when we want to resize. Our signal for fade in + // comes in from how to enter into pip, but we also need to use the to and from rect to + // decide which animation we want to run finally. + boolean shouldResize = false; + if (isRunningFadeInAnimation(target)) { + shouldResize = true; + if (from.contains(to)) { + animationType = FADE_IN; + } + } final boolean replacing = existing != null; @SchedulePipModeChangedState int prevSchedulePipModeChangedState = NO_PIP_MODE_CHANGED_CALLBACKS; @@ -477,18 +511,34 @@ public class BoundsAnimationController { // Since we are replacing, we skip both animation start and end callbacks existing.cancel(); } - final BoundsAnimator animator = new BoundsAnimator(target, from, to, + if (shouldResize) { + target.setPinnedStackSize(to, null); + } + final BoundsAnimator animator = new BoundsAnimator(target, animationType, from, to, schedulePipModeChangedState, prevSchedulePipModeChangedState, moveFromFullscreen, moveToFullscreen, frozenTask); mRunningAnimations.put(target, animator); animator.setFloatValues(0f, 1f); - animator.setDuration((animationDuration != -1 ? animationDuration - : DEFAULT_TRANSITION_DURATION) * DEBUG_ANIMATION_SLOW_DOWN_FACTOR); + animator.setDuration(animationType == FADE_IN ? FADE_IN_DURATION + : (animationDuration != -1 ? animationDuration : DEFAULT_TRANSITION_DURATION) + * DEBUG_ANIMATION_SLOW_DOWN_FACTOR); animator.setInterpolator(mFastOutSlowInInterpolator); animator.start(); return animator; } + public void setAnimationType(@AnimationType int animationType) { + mAnimationType = animationType; + } + + /** return the current animation type. */ + public @AnimationType int getAnimationType() { + @AnimationType int animationType = mAnimationType; + // Default to BOUNDS. + mAnimationType = BOUNDS; + return animationType; + } + public Handler getHandler() { return mHandler; } @@ -498,6 +548,11 @@ public class BoundsAnimationController { mHandler.post(this::resume); } + private boolean isRunningFadeInAnimation(final BoundsAnimationTarget target) { + final BoundsAnimator existing = mRunningAnimations.get(target); + return existing != null && existing.mAnimationType == FADE_IN && existing.isStarted(); + } + private void resume() { for (int i = 0; i < mRunningAnimations.size(); i++) { final BoundsAnimator b = mRunningAnimations.valueAt(i); diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java index 5cb80de1a36d..9f54e49e0022 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java @@ -32,7 +32,8 @@ interface BoundsAnimationTarget { * callbacks * @return whether to continue the animation */ - boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate); + boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, + @BoundsAnimationController.AnimationType int animationType); /** * @return Whether the animation should be paused waiting for the windows to draw before @@ -53,6 +54,9 @@ interface BoundsAnimationTarget { */ boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds); + /** Sets the alpha of the animation target */ + boolean setPinnedStackAlpha(float alpha); + /** * Callback for the target to inform it that the animation has ended, so it can do some * necessary cleanup. diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 144efb49e84a..07d3fb990470 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -26,6 +26,8 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.WindowManager.TRANSIT_NONE; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.BoundsAnimationController.BOUNDS; +import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP; @@ -201,7 +203,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } } - private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) { + private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode, + boolean sendUserLeaveHint) { synchronized (mService.mGlobalLock) { if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller=" + mWindowManager.getRecentsAnimationController() @@ -246,7 +249,18 @@ class RecentsAnimation implements RecentsAnimationCallbacks, if (reorderMode == REORDER_MOVE_TO_TOP) { // Bring the target stack to the front mStackSupervisor.mNoAnimActivities.add(targetActivity); - targetStack.moveToFront("RecentsAnimation.onAnimationFinished()"); + + if (sendUserLeaveHint) { + // Setting this allows the previous app to PiP. + mStackSupervisor.mUserLeaving = true; + targetStack.moveTaskToFrontLocked(targetActivity.getTaskRecord(), + true /* noAnimation */, null /* activityOptions */, + targetActivity.appTimeTracker, + "RecentsAnimation.onAnimationFinished()"); + } else { + targetStack.moveToFront("RecentsAnimation.onAnimationFinished()"); + } + if (DEBUG) { final ActivityStack topStack = getTopNonAlwaysOnTopStack(); if (topStack != targetStack) { @@ -300,11 +314,11 @@ class RecentsAnimation implements RecentsAnimationCallbacks, @Override public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode, - boolean runSychronously) { + boolean runSychronously, boolean sendUserLeaveHint) { if (runSychronously) { - finishAnimation(reorderMode); + finishAnimation(reorderMode, sendUserLeaveHint); } else { - mService.mH.post(() -> finishAnimation(reorderMode)); + mService.mH.post(() -> finishAnimation(reorderMode, sendUserLeaveHint)); } } @@ -317,6 +331,10 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } final RecentsAnimationController controller = mWindowManager.getRecentsAnimationController(); + final DisplayContent dc = + mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent; + dc.mBoundsAnimationController.setAnimationType( + controller.shouldCancelWithDeferredScreenshot() ? FADE_IN : BOUNDS); // Cancel running recents animation and screenshot previous task when the next // transition starts in below cases: diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 381366995dd5..d2c510fa8902 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -27,6 +27,7 @@ import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; import static com.android.server.wm.AnimationAdapterProto.REMOTE; +import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS; import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; @@ -142,7 +143,9 @@ public class RecentsAnimationController implements DeathRecipient { }; public interface RecentsAnimationCallbacks { - void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously); + /** Callback when recents animation is finished. */ + void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously, + boolean sendUserLeaveHint); } private final IRecentsAnimationController mController = @@ -179,7 +182,7 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - public void finish(boolean moveHomeToTop) { + public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) { if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "finish(" + moveHomeToTop + "):" + " mCanceled=" + mCanceled); final long token = Binder.clearCallingIdentity(); @@ -195,7 +198,9 @@ public class RecentsAnimationController implements DeathRecipient { mCallbacks.onAnimationFinished(moveHomeToTop ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION, - true /* runSynchronously */); + true /* runSynchronously */, sendUserLeaveHint); + final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); + dc.mBoundsAnimationController.setAnimationType(FADE_IN); } finally { Binder.restoreCallingIdentity(token); } @@ -497,7 +502,8 @@ public class RecentsAnimationController implements DeathRecipient { Slog.e(TAG, "Failed to cancel recents animation", e); } // Clean up and return to the previous app - mCallbacks.onAnimationFinished(reorderMode, runSynchronously); + mCallbacks.onAnimationFinished(reorderMode, runSynchronously, + false /* sendUserLeaveHint */); } } @@ -542,7 +548,8 @@ public class RecentsAnimationController implements DeathRecipient { if (DEBUG_RECENTS_ANIMATIONS) { Slog.d(TAG, "mRecentScreenshotAnimator finish"); } - mCallbacks.onAnimationFinished(reorderMode, runSynchronously); + mCallbacks.onAnimationFinished(reorderMode, runSynchronously, + false /* sendUserLeaveHint */); }, mService); mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator); } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 09baf8cf1111..bdb4d0474865 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; import static android.view.WindowManager.DOCKED_TOP; +import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; @@ -148,6 +149,7 @@ public class TaskStack extends WindowContainer<Task> implements private boolean mCancelCurrentBoundsAnimation = false; private Rect mBoundsAnimationTarget = new Rect(); private Rect mBoundsAnimationSourceHintBounds = new Rect(); + private @BoundsAnimationController.AnimationType int mAnimationType; Rect mPreAnimationBounds = new Rect(); @@ -1572,7 +1574,8 @@ public class TaskStack extends WindowContainer<Task> implements } @Override // AnimatesBounds - public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) { + public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, + @BoundsAnimationController.AnimationType int animationType) { // Hold the lock since this is called from the BoundsAnimator running on the UiThread synchronized (mWmService.mGlobalLock) { if (!isAttached()) { @@ -1583,6 +1586,7 @@ public class TaskStack extends WindowContainer<Task> implements mBoundsAnimatingRequested = false; mBoundsAnimating = true; mCancelCurrentBoundsAnimation = false; + mAnimationType = animationType; // If we are changing UI mode, as in the PiP to fullscreen // transition, then we need to wait for the window to draw. @@ -1599,7 +1603,8 @@ public class TaskStack extends WindowContainer<Task> implements // I don't believe you... } - if (schedulePipModeChangedCallback && mActivityStack != null) { + if ((schedulePipModeChangedCallback || animationType == FADE_IN) + && mActivityStack != null) { // We need to schedule the PiP mode change before the animation up. It is possible // in this case for the animation down to not have been completed, so always // force-schedule and update to the client to ensure that it is notified that it @@ -1614,6 +1619,21 @@ public class TaskStack extends WindowContainer<Task> implements @Override // AnimatesBounds public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, boolean moveToFullscreen) { + if (mAnimationType == BoundsAnimationController.FADE_IN) { + setPinnedStackAlpha(1f); + try { + mWmService.mActivityTaskManager.notifyPinnedStackAnimationEnded(); + } catch (RemoteException e) { + // I don't believe you... + } + return; + } + + onBoundAnimationEnd(schedulePipModeChangedCallback, finalStackSize, moveToFullscreen); + } + + private void onBoundAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, + boolean moveToFullscreen) { if (inPinnedWindowingMode()) { // Update to the final bounds if requested. This is done here instead of in the bounds // animator to allow us to coordinate this after we notify the PiP mode changed @@ -1725,10 +1745,23 @@ public class TaskStack extends WindowContainer<Task> implements final @SchedulePipModeChangedState int finalSchedulePipModeChangedState = schedulePipModeChangedState; final DisplayContent displayContent = getDisplayContent(); + @BoundsAnimationController.AnimationType int intendedAnimationType = + displayContent.mBoundsAnimationController.getAnimationType(); + if (intendedAnimationType == FADE_IN) { + if (fromFullscreen) { + setPinnedStackAlpha(0f); + } + if (toBounds.width() == fromBounds.width() + && toBounds.height() == fromBounds.height()) { + intendedAnimationType = BoundsAnimationController.BOUNDS; + } + } + + final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType; displayContent.mBoundsAnimationController.getHandler().post(() -> { displayContent.mBoundsAnimationController.animateBounds(this, fromBounds, finalToBounds, animationDuration, finalSchedulePipModeChangedState, - fromFullscreen, toFullscreen); + fromFullscreen, toFullscreen, animationType); }); } @@ -1905,6 +1938,20 @@ public class TaskStack extends WindowContainer<Task> implements } } + @Override + public boolean setPinnedStackAlpha(float alpha) { + // Hold the lock since this is called from the BoundsAnimator running on the UiThread + synchronized (mWmService.mGlobalLock) { + if (mCancelCurrentBoundsAnimation) { + return false; + } + getPendingTransaction().setAlpha(getSurfaceControl(), alpha); + scheduleAnimation(); + } + + return true; + } + public DisplayInfo getDisplayInfo() { return mDisplayContent.getDisplayInfo(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java index 9ce579512eda..beec1a8b8942 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java @@ -18,6 +18,8 @@ package com.android.server.wm; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.server.wm.BoundsAnimationController.BOUNDS; +import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; @@ -131,6 +133,8 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { boolean mCancelRequested; Rect mStackBounds; Rect mTaskBounds; + float mAlpha; + @BoundsAnimationController.AnimationType int mAnimationType; void initialize(Rect from) { mAwaitingAnimationStart = true; @@ -148,11 +152,12 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @Override public boolean onAnimationStart(boolean schedulePipModeChangedCallback, - boolean forceUpdate) { + boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType) { mAwaitingAnimationStart = false; mAnimationStarted = true; mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback; mForcePipModeChangedCallback = forceUpdate; + mAnimationType = animationType; return true; } @@ -185,6 +190,12 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { mMovedToFullscreen = moveToFullscreen; mTaskBounds = null; } + + @Override + public boolean setPinnedStackAlpha(float alpha) { + mAlpha = alpha; + return true; + } } /** @@ -201,6 +212,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { private Rect mTo; private Rect mLargerBounds; private Rect mExpectedFinalBounds; + private @BoundsAnimationController.AnimationType int mAnimationType; BoundsAnimationDriver(BoundsAnimationController controller, TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) { @@ -209,7 +221,8 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { mMockAnimator = mockValueAnimator; } - BoundsAnimationDriver start(Rect from, Rect to) { + BoundsAnimationDriver start(Rect from, Rect to, + @BoundsAnimationController.AnimationType int animationType) { if (mAnimator != null) { throw new IllegalArgumentException("Call restart() to restart an animation"); } @@ -223,7 +236,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { assertTrue(mTarget.mAwaitingAnimationStart); assertFalse(mTarget.mAnimationStarted); - startImpl(from, to); + startImpl(from, to, animationType); // Ensure that the animator is paused for the all windows drawn signal when animating // to/from fullscreen @@ -253,7 +266,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { mTarget.mAnimationStarted = false; // Start animation - startImpl(mTarget.mStackBounds, to); + startImpl(mTarget.mStackBounds, to, BOUNDS); if (toSameBounds) { // Same animator if same final bounds @@ -273,13 +286,15 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { return this; } - private BoundsAnimationDriver startImpl(Rect from, Rect to) { + private BoundsAnimationDriver startImpl(Rect from, Rect to, + @BoundsAnimationController.AnimationType int animationType) { boolean fromFullscreen = from.equals(BOUNDS_FULL); boolean toFullscreen = to.equals(BOUNDS_FULL); mFrom = new Rect(from); mTo = new Rect(to); mExpectedFinalBounds = new Rect(to); mLargerBounds = getLargerBounds(mFrom, mTo); + mAnimationType = animationType; // Start animation final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen @@ -288,17 +303,19 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { ? SCHEDULE_PIP_MODE_CHANGED_ON_END : NO_PIP_MODE_CHANGED_CALLBACKS; mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION, - schedulePipModeChangedState, fromFullscreen, toFullscreen); - - // Original stack bounds, frozen task bounds - assertEquals(mFrom, mTarget.mStackBounds); - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); + schedulePipModeChangedState, fromFullscreen, toFullscreen, animationType); - // Animating to larger size - if (mFrom.equals(mLargerBounds)) { - assertFalse(mAnimator.animatingToLargerSize()); - } else if (mTo.equals(mLargerBounds)) { - assertTrue(mAnimator.animatingToLargerSize()); + if (animationType == BOUNDS) { + // Original stack bounds, frozen task bounds + assertEquals(mFrom, mTarget.mStackBounds); + assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); + + // Animating to larger size + if (mFrom.equals(mLargerBounds)) { + assertFalse(mAnimator.animatingToLargerSize()); + } else if (mTo.equals(mLargerBounds)) { + assertTrue(mAnimator.animatingToLargerSize()); + } } return this; @@ -315,16 +332,20 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { BoundsAnimationDriver update(float t) { mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t)); - // Temporary stack bounds, frozen task bounds - if (t == 0f) { - assertEquals(mFrom, mTarget.mStackBounds); - } else if (t == 1f) { - assertEquals(mTo, mTarget.mStackBounds); + if (mAnimationType == BOUNDS) { + // Temporary stack bounds, frozen task bounds + if (t == 0f) { + assertEquals(mFrom, mTarget.mStackBounds); + } else if (t == 1f) { + assertEquals(mTo, mTarget.mStackBounds); + } else { + assertNotEquals(mFrom, mTarget.mStackBounds); + assertNotEquals(mTo, mTarget.mStackBounds); + } + assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); } else { - assertNotEquals(mFrom, mTarget.mStackBounds); - assertNotEquals(mTo, mTarget.mStackBounds); + assertEquals((float) mMockAnimator.getAnimatedValue(), mTarget.mAlpha, 0.01f); } - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); return this; } @@ -353,10 +374,14 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { BoundsAnimationDriver end() { mAnimator.end(); - // Final stack bounds - assertEquals(mTo, mTarget.mStackBounds); - assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds); - assertNull(mTarget.mTaskBounds); + if (mAnimationType == BOUNDS) { + // Final stack bounds + assertEquals(mTo, mTarget.mStackBounds); + assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds); + assertNull(mTarget.mTaskBounds); + } else { + assertEquals(mTarget.mAlpha, 1f, 0.01f); + } return this; } @@ -413,7 +438,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFullscreenToFloatingTransition() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0f) .update(0.5f) @@ -425,7 +450,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToFullscreenTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) .expectStarted(SCHEDULE_PIP_MODE_CHANGED) .update(0f) .update(0.5f) @@ -437,7 +462,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToSmallerFloatingTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING) + mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0f) .update(0.5f) @@ -449,7 +474,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToLargerFloatingTransition() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING) + mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0f) .update(0.5f) @@ -463,7 +488,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFullscreenToFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .cancel() @@ -473,7 +498,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFullscreenToFloatingCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */) @@ -484,7 +509,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .restart(BOUNDS_SMALLER_FLOATING, @@ -498,7 +523,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() { // When animating from fullscreen and the animation is interruped, we expect the animation // start callback to be made, with a forced pip mode change callback - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */) @@ -511,7 +536,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToFullscreenCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) .expectStarted(SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .cancel() @@ -521,7 +546,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToFullscreenCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) .expectStarted(SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */) @@ -532,7 +557,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) .expectStarted(SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .restart(BOUNDS_SMALLER_FLOATING, @@ -546,7 +571,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToSmallerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING) + mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .cancel() @@ -556,13 +581,25 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToLargerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING) + mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .cancel() .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); } + @UiThreadTest + @Test + public void testFadeIn() { + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, FADE_IN) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0f) + .update(0.5f) + .update(1f) + .end() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + /** MISC **/ @UiThreadTest @@ -570,7 +607,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { public void testBoundsAreCopied() { Rect from = new Rect(0, 0, 100, 100); Rect to = new Rect(25, 25, 75, 75); - mDriver.start(from, to) + mDriver.start(from, to, BOUNDS) .update(0.25f) .end(); assertEquals(new Rect(0, 0, 100, 100), from); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index e392353a8875..0c2ce614b772 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -163,7 +163,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { // Assume IRecentsAnimationController#cleanupScreenshot called to finish screenshot // animation. mController.mRecentScreenshotAnimator.cancelAnimation(); - verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true); + verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false); } private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 5625ea42726f..f615823a645c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -71,6 +71,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testCancelAnimationOnVisibleStackOrderChange() { ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay(); + display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class); ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mService) |