From 1fed1764a95d0c4ec8580a4daca836b169f3a90e Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Wed, 30 Oct 2024 19:59:39 +0800 Subject: Add swipe-PIP-to-home animation support for ... non-MATCH_PARENT activity This CL stops to override the animation bounds to main window frame for swipe-PIP-to-home animation. It's because the transition moves and resizes the whole task and shows the launcher. The whole task should include the partially visible activity behind as well. This CL also workarounds the animation to restore activity back from swipe-PIP-to-home. Ideally we should use the whole task leash as the swipe-to-home animation, but the PIP activity is reparanted after the animation in PIP1 module. Thus we still override the animation bounds to main window frame. We should address the issue in PIP2. Test: manual - swipe-pip-to-home for match and non-match parent activity in portrait and landscape mode Test: atest PipAnimationControllerTest -j Bug: 375977163 Bug: 356277166 Flag: com.android.window.flags.better_support_non_match_parent_activity Change-Id: Ibab51c7606c21b8c199f9c54a81759747a93a5fd --- .../wm/shell/pip/PipAnimationController.java | 36 ++++--- .../com/android/wm/shell/pip/PipTaskOrganizer.java | 4 +- .../com/android/wm/shell/pip/PipTransition.java | 15 +-- .../wm/shell/pip/PipAnimationControllerTest.java | 112 +++++++++++++++++++-- 4 files changed, 141 insertions(+), 26 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 4aeecbec7dfb..4d5dc6da6ee5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -176,12 +176,12 @@ public class PipAnimationController { public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash, Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, float startingAngle, - @Surface.Rotation int rotationDelta) { + @Surface.Rotation int rotationDelta, boolean alwaysAnimateTaskBounds) { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds, endBounds, sourceHintRect, direction, 0 /* startingAngle */, - rotationDelta)); + rotationDelta, alwaysAnimateTaskBounds)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA && mCurrentAnimator.isRunning()) { // If we are still animating the fade into pip, then just move the surface and ensure @@ -197,7 +197,8 @@ public class PipAnimationController { mCurrentAnimator.cancel(); mCurrentAnimator = setupPipTransitionAnimator( PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds, - endBounds, sourceHintRect, direction, startingAngle, rotationDelta)); + endBounds, sourceHintRect, direction, startingAngle, rotationDelta, + alwaysAnimateTaskBounds)); } return mCurrentAnimator; } @@ -585,28 +586,29 @@ public class PipAnimationController { static PipTransitionAnimator ofBounds(TaskInfo taskInfo, SurfaceControl leash, Rect baseValue, Rect startValue, Rect endValue, Rect sourceRectHint, @PipAnimationController.TransitionDirection int direction, float startingAngle, - @Surface.Rotation int rotationDelta) { + @Surface.Rotation int rotationDelta, boolean alwaysAnimateTaskBounds) { final boolean isOutPipDirection = isOutPipDirection(direction); final boolean isInPipDirection = isInPipDirection(direction); // Just for simplicity we'll interpolate between the source rect hint insets and empty // insets to calculate the window crop final Rect initialSourceValue; final Rect mainWindowFrame = taskInfo.topActivityMainWindowFrame; - final boolean hasNonMatchFrame = mainWindowFrame != null; + // For the animation to swipe PIP to home or restore a PIP task from home, we don't + // override to the main window frame since we should animate the whole task. + final boolean shouldUseMainWindowFrame = mainWindowFrame != null + && !alwaysAnimateTaskBounds; final boolean changeOrientation = rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270; final Rect baseBounds = new Rect(baseValue); final Rect startBounds = new Rect(startValue); final Rect endBounds = new Rect(endValue); if (isOutPipDirection) { - // TODO(b/356277166): handle rotation change with activity that provides main window - // frame. - if (hasNonMatchFrame && !changeOrientation) { + if (shouldUseMainWindowFrame && !changeOrientation) { endBounds.set(mainWindowFrame); } initialSourceValue = new Rect(endBounds); } else if (isInPipDirection) { - if (hasNonMatchFrame) { + if (shouldUseMainWindowFrame) { baseBounds.set(mainWindowFrame); if (startValue.equals(baseValue)) { // If the start value is at initial state as in PIP animation, also override @@ -635,9 +637,19 @@ public class PipAnimationController { if (changeOrientation) { lastEndRect = new Rect(endBounds); rotatedEndRect = new Rect(endBounds); - // Rotate the end bounds according to the rotation delta because the display will - // be rotated to the same orientation. - rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta); + // TODO(b/375977163): polish the animation to restoring the PIP task back from + // swipe-pip-to-home. Ideally we should send the transitionInfo after reparenting + // the PIP activity back to the original task. + if (shouldUseMainWindowFrame) { + // If we should animate the main window frame, set it to the rotatedRect + // instead. The end bounds reported by transitionInfo is the bounds before + // rotation, while main window frame is calculated after the rotation. + rotatedEndRect.set(mainWindowFrame); + } else { + // Rotate the end bounds according to the rotation delta because the display + // will be rotated to the same orientation. + rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta); + } // Use the rect that has the same orientation as the hint rect. initialContainerRect = isOutPipDirection ? rotatedEndRect : initialSourceValue; } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index c4e63dfdade9..abe50b33625b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -1838,9 +1838,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, ? mPipBoundsState.getBounds() : currentBounds; final boolean existingAnimatorRunning = mPipAnimationController.getCurrentAnimator() != null && mPipAnimationController.getCurrentAnimator().isRunning(); + // For resize animation, we always animate the whole PIP task bounds. final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds, - sourceHintRect, direction, startingAngle, rotationDelta); + sourceHintRect, direction, startingAngle, rotationDelta, + true /* alwaysAnimateTaskBounds */); animator.setTransitionDirection(direction) .setPipTransactionHandler(mPipTransactionHandler) .setDuration(durationMs); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 28b91c6cb812..8c1be5ffa19c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -884,7 +884,8 @@ public class PipTransition extends PipTransitionController { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(taskInfo, pipChange.getLeash(), startBounds, startBounds, endBounds, null, TRANSITION_DIRECTION_LEAVE_PIP, - 0 /* startingAngle */, pipRotateDelta); + 0 /* startingAngle */, pipRotateDelta, + false /* alwaysAnimateTaskBounds */); animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) @@ -899,7 +900,7 @@ public class PipTransition extends PipTransitionController { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(taskInfo, leash, baseBounds, startBounds, endBounds, sourceHintRect, TRANSITION_DIRECTION_LEAVE_PIP, - 0 /* startingAngle */, rotationDelta); + 0 /* startingAngle */, rotationDelta, false /* alwaysAnimateTaskBounds */); animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP) .setDuration(mEnterExitAnimationDuration); if (startTransaction != null) { @@ -1095,8 +1096,6 @@ public class PipTransition extends PipTransitionController { if (taskInfo.pictureInPictureParams != null && taskInfo.pictureInPictureParams.isAutoEnterEnabled() && mPipTransitionState.getInSwipePipToHomeTransition()) { - // TODO(b/356277166): add support to swipe PIP to home with - // non-match parent activity. handleSwipePipToHomeTransition(startTransaction, finishTransaction, leash, sourceHintRect, destinationBounds, taskInfo); return; @@ -1118,7 +1117,7 @@ public class PipTransition extends PipTransitionController { if (enterAnimationType == ANIM_TYPE_BOUNDS) { animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, - 0 /* startingAngle */, rotationDelta); + 0 /* startingAngle */, rotationDelta, false /* alwaysAnimateTaskBounds */); if (sourceHintRect == null) { // We use content overlay when there is no source rect hint to enter PiP use bounds // animation. We also temporarily disallow app icon overlay and use color overlay @@ -1241,10 +1240,14 @@ public class PipTransition extends PipTransitionController { // to avoid flicker. final Rect savedDisplayCutoutInsets = new Rect(pipTaskInfo.displayCutoutInsets); pipTaskInfo.displayCutoutInsets.setEmpty(); + // Always use the task bounds even if the PIP activity doesn't match parent because the app + // and the whole task will move behind. We should animate the whole task bounds in this + // case. final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(pipTaskInfo, leash, sourceBounds, sourceBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, - 0 /* startingAngle */, ROTATION_0 /* rotationDelta */) + 0 /* startingAngle */, ROTATION_0 /* rotationDelta */, + true /* alwaysAnimateTaskBounds */) .setPipTransactionHandler(mTransactionConsumer) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP); // The start state is the end state for swipe-auto-pip. diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java index 72950a8dc139..4dca595b97c5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java @@ -93,7 +93,8 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect endValue1 = new Rect(100, 100, 200, 200); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); assertEquals("Expect ANIM_TYPE_BOUNDS animation", animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS); @@ -107,14 +108,16 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect endValue2 = new Rect(200, 200, 300, 300); final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); oldAnimator.setSurfaceControlTransactionFactory( MockSurfaceControlHelper::createMockSurfaceControlTransaction); oldAnimator.start(); final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); assertEquals("getAnimator with same type returns same animator", oldAnimator, newAnimator); @@ -145,7 +148,8 @@ public class PipAnimationControllerTest extends ShellTestCase { // Fullscreen to PiP. PipAnimationController.PipTransitionAnimator animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null, - TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_90); + TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_90, + false /* alwaysAnimateTaskBounds */); // Apply fraction 1 to compute the end value. animator.applySurfaceControlTransaction(mLeash, tx, 1); final Rect rotatedEndBounds = new Rect(endBounds); @@ -157,7 +161,8 @@ public class PipAnimationControllerTest extends ShellTestCase { startBounds.set(0, 0, 1000, 500); endBounds.set(200, 100, 400, 500); animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, startBounds, startBounds, - endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270); + endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270, + false /* alwaysAnimateTaskBounds */); animator.applySurfaceControlTransaction(mLeash, tx, 1); rotatedEndBounds.set(endBounds); rotateBounds(rotatedEndBounds, startBounds, ROTATION_270); @@ -165,6 +170,37 @@ public class PipAnimationControllerTest extends ShellTestCase { assertEquals("Expect 270 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue); } + @Test + public void pipTransitionAnimator_rotatedEndValue_overrideMainWindowFrame() { + final SurfaceControl.Transaction tx = createMockSurfaceControlTransaction(); + final Rect startBounds = new Rect(200, 700, 400, 800); + final Rect endBounds = new Rect(0, 0, 500, 1000); + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 250, 1000, 500); + + // Fullscreen task to PiP. + PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null, + TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_90, + false /* alwaysAnimateTaskBounds */); + // Apply fraction 1 to compute the end value. + animator.applySurfaceControlTransaction(mLeash, tx, 1); + + assertEquals("Expect use main window frame", mTaskInfo.topActivityMainWindowFrame, + animator.mCurrentValue); + + // PiP to fullscreen. + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 250, 1000, 500); + startBounds.set(0, 0, 1000, 500); + endBounds.set(200, 100, 400, 500); + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, startBounds, startBounds, + endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270, + false /* alwaysAnimateTaskBounds */); + animator.applySurfaceControlTransaction(mLeash, tx, 1); + + assertEquals("Expect use main window frame", mTaskInfo.topActivityMainWindowFrame, + animator.mCurrentValue); + } + @Test @SuppressWarnings("unchecked") public void pipTransitionAnimator_updateEndValue() { @@ -174,7 +210,8 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect endValue2 = new Rect(200, 200, 300, 300); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); animator.updateEndValue(endValue2); @@ -188,7 +225,8 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect endValue = new Rect(100, 100, 200, 200); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); animator.setSurfaceControlTransactionFactory( MockSurfaceControlHelper::createMockSurfaceControlTransaction); @@ -207,4 +245,64 @@ public class PipAnimationControllerTest extends ShellTestCase { verify(mPipAnimationCallback).onPipAnimationEnd(eq(mTaskInfo), any(SurfaceControl.Transaction.class), eq(animator)); } + + @Test + public void pipTransitionAnimator_overrideMainWindowFrame() { + final Rect baseValue = new Rect(0, 0, 100, 100); + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100); + PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is overridden for in-PIP transition", + mTaskInfo.topActivityMainWindowFrame, animator.getBaseValue()); + assertEquals("Expect start value is overridden for in-PIP transition", + mTaskInfo.topActivityMainWindowFrame, animator.getStartValue()); + assertEquals("Expect end value is not overridden for in-PIP transition", + endValue, animator.getEndValue()); + + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue, + endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for leave-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for leave-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is overridden for leave-PIP transition", + mTaskInfo.topActivityMainWindowFrame, animator.getEndValue()); + } + + @Test + public void pipTransitionAnimator_animateTaskBounds() { + final Rect baseValue = new Rect(0, 0, 100, 100); + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100); + PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + true /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for in-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for in-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for in-PIP transition", + endValue, animator.getEndValue()); + + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue, + endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0, + true /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for leave-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for leave-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for leave-PIP transition", + endValue, animator.getEndValue()); + } } -- cgit v1.2.3-59-g8ed1b From a37eec775e7347f079366efc9708cf5eb8ad7bc5 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Wed, 30 Oct 2024 20:30:04 +0800 Subject: Workaround for the case when task is letterboxed There's a regression to make the task leash letterboxed in PIP animation when enable_letterbox_translucent_activity is enabled even if the activity matches parent. This CL wordarounds to avoid overridding animation bounds if the task is in size compat or letterboxed. See b/376200414#comment6 to know how the transitions look like. Bug: 376200414 Test: manual - 1. make activity to PIP mode 2. rotate the device 3. restore the activity from PIP Test: atest PipAnimationControllerTest -j Flag: com.android.window.flags.better_support_non_match_parent_activity Change-Id: I92f1bca121a574c07cdd4c1ebdf4b0e474a90c40 --- .../wm/shell/pip/PipAnimationController.java | 6 +- .../wm/shell/pip/PipAnimationControllerTest.java | 66 ++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 4d5dc6da6ee5..5276d9d6a4df 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -27,6 +27,7 @@ import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; +import android.app.AppCompatTaskInfo; import android.app.TaskInfo; import android.content.Context; import android.content.pm.ActivityInfo; @@ -593,10 +594,13 @@ public class PipAnimationController { // insets to calculate the window crop final Rect initialSourceValue; final Rect mainWindowFrame = taskInfo.topActivityMainWindowFrame; + final AppCompatTaskInfo compatInfo = taskInfo.appCompatTaskInfo; + final boolean isSizeCompatOrLetterboxed = compatInfo.isTopActivityInSizeCompat() + || compatInfo.isTopActivityLetterboxed(); // For the animation to swipe PIP to home or restore a PIP task from home, we don't // override to the main window frame since we should animate the whole task. final boolean shouldUseMainWindowFrame = mainWindowFrame != null - && !alwaysAnimateTaskBounds; + && !alwaysAnimateTaskBounds && !isSizeCompatOrLetterboxed; final boolean changeOrientation = rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270; final Rect baseBounds = new Rect(baseValue); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java index 4dca595b97c5..6d37ed766aef 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java @@ -28,8 +28,11 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import android.app.AppCompatTaskInfo; import android.app.TaskInfo; import android.graphics.Rect; import android.testing.AndroidTestingRunner; @@ -75,6 +78,7 @@ public class PipAnimationControllerTest extends ShellTestCase { .setContainerLayer() .setName("FakeLeash") .build(); + mTaskInfo.appCompatTaskInfo = mock(AppCompatTaskInfo.class); } @Test @@ -305,4 +309,66 @@ public class PipAnimationControllerTest extends ShellTestCase { assertEquals("Expect end value is not overridden for leave-PIP transition", endValue, animator.getEndValue()); } + + @Test + public void pipTransitionAnimator_letterboxed_animateTaskBounds() { + final Rect baseValue = new Rect(0, 0, 100, 100); + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100); + doReturn(true).when(mTaskInfo.appCompatTaskInfo).isTopActivityLetterboxed(); + PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for in-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for in-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for in-PIP transition", + endValue, animator.getEndValue()); + + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue, + endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for leave-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for leave-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for leave-PIP transition", + endValue, animator.getEndValue()); + } + + @Test + public void pipTransitionAnimator_sizeCompat_animateTaskBounds() { + final Rect baseValue = new Rect(0, 0, 100, 100); + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100); + doReturn(true).when(mTaskInfo.appCompatTaskInfo).isTopActivityInSizeCompat(); + PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for in-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for in-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for in-PIP transition", + endValue, animator.getEndValue()); + + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue, + endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for leave-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for leave-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for leave-PIP transition", + endValue, animator.getEndValue()); + } } -- cgit v1.2.3-59-g8ed1b