Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1fed1764 authored by Charles Chen's avatar Charles Chen
Browse files

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
parent 0df4c032
Loading
Loading
Loading
Loading
+24 −12
Original line number Diff line number Diff line
@@ -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<Rect> 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.
                // 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 {
+3 −1
Original line number Diff line number Diff line
@@ -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);
+9 −6
Original line number Diff line number Diff line
@@ -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.
+105 −7
Original line number Diff line number Diff line
@@ -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());
    }
}