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

Commit 7627f66e authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Add corner radius for PiP window am: df8bb003 am: 52e7610b am: 813ee787

Change-Id: I3493cb6e24ed06c47c6ce024819cb6534959b14e
parents ae44c39f 813ee787
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -983,6 +983,9 @@
    <!-- The touchable/draggable edge size for PIP resize. -->
    <dimen name="pip_resize_edge_size">30dp</dimen>

    <!-- The corner radius for PiP window. -->
    <dimen name="pip_corner_radius">8dp</dimen>

    <dimen name="default_gear_space">18dp</dimen>
    <dimen name="cell_overlay_padding">18dp</dimen>

+57 −23
Original line number Diff line number Diff line
@@ -37,7 +37,6 @@ public class PipAnimationController {
    private static final float FRACTION_START = 0f;
    private static final float FRACTION_END = 1f;

    public static final int DURATION_NONE = 0;
    public static final int DURATION_DEFAULT_MS = 425;
    public static final int ANIM_TYPE_BOUNDS = 0;
    public static final int ANIM_TYPE_ALPHA = 1;
@@ -49,6 +48,20 @@ public class PipAnimationController {
    @Retention(RetentionPolicy.SOURCE)
    public @interface AnimationType {}

    static final int TRANSITION_DIRECTION_NONE = 0;
    static final int TRANSITION_DIRECTION_SAME = 1;
    static final int TRANSITION_DIRECTION_TO_PIP = 2;
    static final int TRANSITION_DIRECTION_TO_FULLSCREEN = 3;

    @IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = {
            TRANSITION_DIRECTION_NONE,
            TRANSITION_DIRECTION_SAME,
            TRANSITION_DIRECTION_TO_PIP,
            TRANSITION_DIRECTION_TO_FULLSCREEN
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface TransitionDirection {}

    private final Interpolator mFastOutSlowInInterpolator;

    private PipTransitionAnimator mCurrentAnimator;
@@ -58,30 +71,28 @@ public class PipAnimationController {
                com.android.internal.R.interpolator.fast_out_slow_in);
    }

    PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip,
    @SuppressWarnings("unchecked")
    PipTransitionAnimator getAnimator(SurfaceControl leash,
            Rect destinationBounds, float alphaStart, float alphaEnd) {
        if (mCurrentAnimator == null) {
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds,
                            alphaStart, alphaEnd));
                    PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
                && mCurrentAnimator.isRunning()) {
            mCurrentAnimator.updateEndValue(alphaEnd);
        } else {
            mCurrentAnimator.cancel();
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds,
                            alphaStart, alphaEnd));
                    PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
        }
        return mCurrentAnimator;
    }

    PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip,
            Rect startBounds, Rect endBounds) {
    @SuppressWarnings("unchecked")
    PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds) {
        if (mCurrentAnimator == null) {
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofBounds(leash, scheduleFinishPip,
                            startBounds, endBounds));
                    PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
                && mCurrentAnimator.isRunning()) {
            mCurrentAnimator.setDestinationBounds(endBounds);
@@ -90,8 +101,7 @@ public class PipAnimationController {
        } else {
            mCurrentAnimator.cancel();
            mCurrentAnimator = setupPipTransitionAnimator(
                    PipTransitionAnimator.ofBounds(leash, scheduleFinishPip,
                            startBounds, endBounds));
                    PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
        }
        return mCurrentAnimator;
    }
@@ -134,7 +144,6 @@ public class PipAnimationController {
    public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
            ValueAnimator.AnimatorUpdateListener,
            ValueAnimator.AnimatorListener {
        private final boolean mScheduleFinishPip;
        private final SurfaceControl mLeash;
        private final @AnimationType int mAnimationType;
        private final Rect mDestinationBounds = new Rect();
@@ -144,11 +153,11 @@ public class PipAnimationController {
        private T mCurrentValue;
        private PipAnimationCallback mPipAnimationCallback;
        private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory;
        private @TransitionDirection int mTransitionDirection;
        private int mCornerRadius;

        private PipTransitionAnimator(SurfaceControl leash, boolean scheduleFinishPip,
                @AnimationType int animationType, Rect destinationBounds,
                T startValue, T endValue) {
            mScheduleFinishPip = scheduleFinishPip;
        private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
                Rect destinationBounds, T startValue, T endValue) {
            mLeash = leash;
            mAnimationType = animationType;
            mDestinationBounds.set(destinationBounds);
@@ -157,6 +166,7 @@ public class PipAnimationController {
            addListener(this);
            addUpdateListener(this);
            mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
            mTransitionDirection = TRANSITION_DIRECTION_NONE;
        }

        @Override
@@ -202,8 +212,15 @@ public class PipAnimationController {
            return this;
        }

        boolean shouldScheduleFinishPip() {
            return mScheduleFinishPip;
        @TransitionDirection int getTransitionDirection() {
            return mTransitionDirection;
        }

        PipTransitionAnimator<T> setTransitionDirection(@TransitionDirection int direction) {
            if (direction != TRANSITION_DIRECTION_SAME) {
                mTransitionDirection = direction;
            }
            return this;
        }

        T getStartValue() {
@@ -226,6 +243,19 @@ public class PipAnimationController {
            mCurrentValue = value;
        }

        int getCornerRadius() {
            return mCornerRadius;
        }

        PipTransitionAnimator<T> setCornerRadius(int cornerRadius) {
            mCornerRadius = cornerRadius;
            return this;
        }

        boolean shouldApplyCornerRadius() {
            return mTransitionDirection != TRANSITION_DIRECTION_TO_FULLSCREEN;
        }

        /**
         * Updates the {@link #mEndValue}.
         *
@@ -251,9 +281,9 @@ public class PipAnimationController {
        abstract void applySurfaceControlTransaction(SurfaceControl leash,
                SurfaceControl.Transaction tx, float fraction);

        static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash, boolean scheduleFinishPip,
        static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash,
                Rect destinationBounds, float startValue, float endValue) {
            return new PipTransitionAnimator<Float>(leash, scheduleFinishPip, ANIM_TYPE_ALPHA,
            return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA,
                    destinationBounds, startValue, endValue) {
                @Override
                void applySurfaceControlTransaction(SurfaceControl leash,
@@ -266,16 +296,18 @@ public class PipAnimationController {
                        final Rect bounds = getDestinationBounds();
                        tx.setPosition(leash, bounds.left, bounds.top)
                                .setWindowCrop(leash, bounds.width(), bounds.height());
                        tx.setCornerRadius(leash,
                                shouldApplyCornerRadius() ? getCornerRadius() : 0);
                    }
                    tx.apply();
                }
            };
        }

        static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash, boolean scheduleFinishPip,
        static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
                Rect startValue, Rect endValue) {
            // construct new Rect instances in case they are recycled
            return new PipTransitionAnimator<Rect>(leash, scheduleFinishPip, ANIM_TYPE_BOUNDS,
            return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
                    endValue, new Rect(startValue), new Rect(endValue)) {
                private final Rect mTmpRect = new Rect();

@@ -299,6 +331,8 @@ public class PipAnimationController {
                    if (Float.compare(fraction, FRACTION_START) == 0) {
                        // Ensure the start condition
                        tx.setAlpha(leash, 1f);
                        tx.setCornerRadius(leash,
                                shouldApplyCornerRadius() ? getCornerRadius() : 0);
                    }
                    tx.apply();
                }
+81 −73
Original line number Diff line number Diff line
@@ -19,6 +19,10 @@ package com.android.systemui.pip;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
import static com.android.systemui.pip.PipAnimationController.DURATION_DEFAULT_MS;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,7 +34,6 @@ import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.DisplayInfo;
@@ -40,6 +43,7 @@ import android.view.SurfaceControl;
import android.view.WindowContainerTransaction;

import com.android.internal.os.SomeArgs;
import com.android.systemui.R;
import com.android.systemui.pip.phone.PipUpdateThread;

import java.util.ArrayList;
@@ -74,6 +78,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
    private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
    private final Rect mDisplayBounds = new Rect();
    private final Rect mLastReportedBounds = new Rect();
    private final int mCornerRadius;

    // These callbacks are called on the update thread
    private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -97,7 +102,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
                    callback.onPipTransitionFinished();
                }
            });
            finishResize(animator.getDestinationBounds(), tx, animator.shouldScheduleFinishPip());
            finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection());
        }

        @Override
@@ -111,9 +116,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        }
    };

    private Handler.Callback mUpdateCallbacks = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
    @SuppressWarnings("unchecked")
    private Handler.Callback mUpdateCallbacks = (msg) -> {
        SomeArgs args = (SomeArgs) msg.obj;
        Consumer<Rect> updateBoundsCallback = (Consumer<Rect>) args.arg1;
        switch (msg.what) {
@@ -128,9 +132,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
            case MSG_RESIZE_ANIMATE: {
                Rect currentBounds = (Rect) args.arg2;
                Rect toBounds = (Rect) args.arg3;
                    boolean scheduleFinishPip = args.argi1 != 0;
                int duration = args.argi2;
                    animateResizePip(scheduleFinishPip, currentBounds, toBounds, duration);
                animateResizePip(currentBounds, toBounds, args.argi1 /* direction */, duration);
                if (updateBoundsCallback != null) {
                    updateBoundsCallback.accept(toBounds);
                }
@@ -151,8 +154,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
            case MSG_FINISH_RESIZE: {
                SurfaceControl.Transaction tx = (SurfaceControl.Transaction) args.arg2;
                Rect toBounds = (Rect) args.arg3;
                    boolean scheduleFinishPip = args.argi1 != 0;
                    finishResize(toBounds, tx, scheduleFinishPip);
                finishResize(tx, toBounds, args.argi1 /* direction */);
                if (updateBoundsCallback != null) {
                    updateBoundsCallback.accept(toBounds);
                }
@@ -161,7 +163,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        }
        args.recycle();
        return true;
        }
    };

    private ActivityManager.RunningTaskInfo mTaskInfo;
@@ -176,6 +177,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController();
        mPipBoundsHandler = boundsHandler;
        mPipAnimationController = new PipAnimationController(context);
        mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
    }

    public Handler getUpdateHandler() {
@@ -191,7 +193,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {

    /**
     * Sets the preferred animation type for one time.
     * This is typically used to set the animation type to {@link #ANIM_TYPE_ALPHA}.
     * This is typically used to set the animation type to
     * {@link PipAnimationController#ANIM_TYPE_ALPHA}.
     */
    public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
        mOneShotAnimationType = animationType;
@@ -200,13 +203,14 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
    /**
     * Updates the display dimension with given {@link DisplayInfo}
     */
    @SuppressWarnings("unchecked")
    public void onDisplayInfoChanged(DisplayInfo displayInfo) {
        final Rect newDisplayBounds = new Rect(0, 0,
                displayInfo.logicalWidth, displayInfo.logicalHeight);
        if (!mDisplayBounds.equals(newDisplayBounds)) {
            // Updates the exiting PiP animation in case the screen rotation changes in the middle.
            // It's a legit case that PiP window is in portrait mode on home screen and
            // the application requests landscape onces back to fullscreen mode.
            // the application requests landscape once back to fullscreen mode.
            final PipAnimationController.PipTransitionAnimator animator =
                    mPipAnimationController.getCurrentAnimator();
            if (animator != null
@@ -250,12 +254,13 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        }
        if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
            final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
            scheduleAnimateResizePip(true /* scheduleFinishPip */,
                    currentBounds, destinationBounds, DURATION_DEFAULT_MS, null);
            scheduleAnimateResizePip(currentBounds, destinationBounds,
                    TRANSITION_DIRECTION_TO_PIP, DURATION_DEFAULT_MS, null);
        } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
            mUpdateHandler.post(() -> mPipAnimationController
                    .getAnimator(mLeash, true /* scheduleFinishPip */,
                            destinationBounds, 0f, 1f)
                    .getAnimator(mLeash, destinationBounds, 0f, 1f)
                    .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
                    .setCornerRadius(mCornerRadius)
                    .setPipAnimationCallback(mPipAnimationCallback)
                    .setDuration(DURATION_DEFAULT_MS)
                    .start());
@@ -272,7 +277,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
            Log.wtf(TAG, "Unrecognized token: " + token);
            return;
        }
        scheduleAnimateResizePip(mDisplayBounds, DURATION_DEFAULT_MS, null);
        scheduleAnimateResizePip(mLastReportedBounds, mDisplayBounds,
                TRANSITION_DIRECTION_TO_FULLSCREEN, DURATION_DEFAULT_MS, null);
        mInPip = false;
    }

@@ -310,12 +316,12 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
     */
    public void scheduleAnimateResizePip(Rect toBounds, int duration,
            Consumer<Rect> updateBoundsCallback) {
        scheduleAnimateResizePip(false /* scheduleFinishPip */,
                mLastReportedBounds, toBounds, duration, updateBoundsCallback);
        scheduleAnimateResizePip(mLastReportedBounds, toBounds,
                TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
    }

    private void scheduleAnimateResizePip(boolean scheduleFinishPip,
            Rect currentBounds, Rect destinationBounds, int durationMs,
    private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
            @PipAnimationController.TransitionDirection int direction, int durationMs,
            Consumer<Rect> updateBoundsCallback) {
        Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
        if (!mInPip) {
@@ -326,7 +332,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        args.arg1 = updateBoundsCallback;
        args.arg2 = currentBounds;
        args.arg3 = destinationBounds;
        args.argi1 = scheduleFinishPip ? 1 : 0;
        args.argi1 = direction;
        args.argi2 = durationMs;
        mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
    }
@@ -351,25 +357,25 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
        SurfaceControl.Transaction tx = new SurfaceControl.Transaction()
                .setPosition(mLeash, destinationBounds.left, destinationBounds.top)
                .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
        scheduleFinishResizePip(tx, destinationBounds, false /* scheduleFinishPip */,
                null);
                .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height())
                .setCornerRadius(mLeash, mInPip ? mCornerRadius : 0);
        scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE, null);
    }

    private void scheduleFinishResizePip(SurfaceControl.Transaction tx,
            Rect destinationBounds, boolean scheduleFinishPip,
            Rect destinationBounds, @PipAnimationController.TransitionDirection int direction,
            Consumer<Rect> updateBoundsCallback) {
        Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = updateBoundsCallback;
        args.arg2 = tx;
        args.arg3 = destinationBounds;
        args.argi1 = scheduleFinishPip ? 1 : 0;
        args.argi1 = direction;
        mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_FINISH_RESIZE, args));
    }

    /**
     * Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE}
     * Offset the PiP window by a given offset on Y-axis, triggered also from screen rotation.
     */
    public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
            Consumer<Rect> updateBoundsCallback) {
@@ -398,8 +404,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        }
        final Rect destinationBounds = new Rect(originalBounds);
        destinationBounds.offset(xOffset, yOffset);
        animateResizePip(false /* scheduleFinishPip*/, originalBounds, destinationBounds,
                durationMs);
        animateResizePip(originalBounds, destinationBounds, TRANSITION_DIRECTION_SAME, durationMs);
    }

    private void resizePip(Rect destinationBounds) {
@@ -416,11 +421,12 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        new SurfaceControl.Transaction()
                .setPosition(mLeash, destinationBounds.left, destinationBounds.top)
                .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height())
                .setCornerRadius(mLeash, mInPip ? mCornerRadius : 0)
                .apply();
    }

    private void finishResize(Rect destinationBounds, SurfaceControl.Transaction tx,
            boolean shouldScheduleFinishPip) {
    private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
            @PipAnimationController.TransitionDirection int direction) {
        if (Looper.myLooper() != mUpdateHandler.getLooper()) {
            throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
                    + "directly");
@@ -428,7 +434,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        mLastReportedBounds.set(destinationBounds);
        try {
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            if (shouldScheduleFinishPip) {
            if (direction == TRANSITION_DIRECTION_TO_PIP) {
                wct.scheduleFinishEnterPip(mToken, destinationBounds);
            } else {
                wct.setBounds(mToken, destinationBounds);
@@ -440,8 +446,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        }
    }

    private void animateResizePip(boolean scheduleFinishPip, Rect currentBounds,
            Rect destinationBounds, int durationMs) {
    private void animateResizePip(Rect currentBounds, Rect destinationBounds,
            @PipAnimationController.TransitionDirection int direction, int durationMs) {
        if (Looper.myLooper() != mUpdateHandler.getLooper()) {
            throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
                    + "this directly");
@@ -452,7 +458,9 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
            return;
        }
        mUpdateHandler.post(() -> mPipAnimationController
                .getAnimator(mLeash, scheduleFinishPip, currentBounds, destinationBounds)
                .getAnimator(mLeash, currentBounds, destinationBounds)
                .setTransitionDirection(direction)
                .setCornerRadius(mCornerRadius)
                .setPipAnimationCallback(mPipAnimationCallback)
                .setDuration(durationMs)
                .start());
+24 −13
Original line number Diff line number Diff line
@@ -16,9 +16,10 @@

package com.android.systemui.pip;

import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -64,7 +65,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
    @Test
    public void getAnimator_withAlpha_returnFloatAnimator() {
        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                .getAnimator(mLeash, true /* scheduleFinishPip */, new Rect(), 0f, 1f);
                .getAnimator(mLeash, new Rect(), 0f, 1f);

        assertEquals("Expect ANIM_TYPE_ALPHA animation",
                animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA);
@@ -73,7 +74,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
    @Test
    public void getAnimator_withBounds_returnBoundsAnimator() {
        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                .getAnimator(mLeash, true /* scheduleFinishPip */, new Rect(), new Rect());
                .getAnimator(mLeash, new Rect(), new Rect());

        assertEquals("Expect ANIM_TYPE_BOUNDS animation",
                animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -85,12 +86,12 @@ public class PipAnimationControllerTest extends SysuiTestCase {
        final Rect endValue1 = new Rect(100, 100, 200, 200);
        final Rect endValue2 = new Rect(200, 200, 300, 300);
        final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
                .getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue1);
                .getAnimator(mLeash, startValue, endValue1);
        oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
        oldAnimator.start();

        final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
                .getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue2);
                .getAnimator(mLeash, startValue, endValue2);

        assertEquals("getAnimator with same type returns same animator",
                oldAnimator, newAnimator);
@@ -99,23 +100,28 @@ public class PipAnimationControllerTest extends SysuiTestCase {
    }

    @Test
    public void getAnimator_scheduleFinishPip() {
    public void getAnimator_setTransitionDirection() {
        PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                .getAnimator(mLeash, true /* scheduleFinishPip */, new Rect(), 0f, 1f);
        assertTrue("scheduleFinishPip is true", animator.shouldScheduleFinishPip());
                .getAnimator(mLeash, new Rect(), 0f, 1f)
                .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP);
        assertEquals("Transition to PiP mode",
                animator.getTransitionDirection(), TRANSITION_DIRECTION_TO_PIP);

        animator = mPipAnimationController
                .getAnimator(mLeash, false /* scheduleFinishPip */, new Rect(), 0f, 1f);
        assertFalse("scheduleFinishPip is false", animator.shouldScheduleFinishPip());
                .getAnimator(mLeash, new Rect(), 0f, 1f)
                .setTransitionDirection(TRANSITION_DIRECTION_TO_FULLSCREEN);
        assertEquals("Transition to fullscreen mode",
                animator.getTransitionDirection(), TRANSITION_DIRECTION_TO_FULLSCREEN);
    }

    @Test
    @SuppressWarnings("unchecked")
    public void pipTransitionAnimator_updateEndValue() {
        final Rect startValue = new Rect(0, 0, 100, 100);
        final Rect endValue1 = new Rect(100, 100, 200, 200);
        final Rect endValue2 = new Rect(200, 200, 300, 300);
        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                .getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue1);
                .getAnimator(mLeash, startValue, endValue1);

        animator.updateEndValue(endValue2);

@@ -127,7 +133,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
        final Rect startValue = new Rect(0, 0, 100, 100);
        final Rect endValue = new Rect(100, 100, 200, 200);
        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                .getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue);
                .getAnimator(mLeash, startValue, endValue);
        animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);

        animator.setPipAnimationCallback(mPipAnimationCallback);
@@ -166,6 +172,11 @@ public class PipAnimationControllerTest extends SysuiTestCase {
            return this;
        }

        @Override
        public SurfaceControl.Transaction setCornerRadius(SurfaceControl leash, float radius) {
            return this;
        }

        @Override
        public void apply() {}
    }