Loading packages/SystemUI/res/values/dimens.xml +3 −0 Original line number Diff line number Diff line Loading @@ -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> Loading packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +57 −23 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading @@ -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; } Loading Loading @@ -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(); Loading @@ -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); Loading @@ -157,6 +166,7 @@ public class PipAnimationController { addListener(this); addUpdateListener(this); mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mTransitionDirection = TRANSITION_DIRECTION_NONE; } @Override Loading Loading @@ -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() { Loading @@ -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}. * Loading @@ -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, Loading @@ -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(); Loading @@ -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(); } Loading packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +81 −73 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 = Loading @@ -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 Loading @@ -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) { Loading @@ -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); } Loading @@ -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); } Loading @@ -161,7 +163,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } args.recycle(); return true; } }; private ActivityManager.RunningTaskInfo mTaskInfo; Loading @@ -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() { Loading @@ -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; Loading @@ -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 Loading Loading @@ -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()); Loading @@ -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; } Loading Loading @@ -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) { Loading @@ -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)); } Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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"); Loading @@ -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); Loading @@ -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"); Loading @@ -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()); Loading packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java +24 −13 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading Loading @@ -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() {} } Loading Loading
packages/SystemUI/res/values/dimens.xml +3 −0 Original line number Diff line number Diff line Loading @@ -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> Loading
packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +57 −23 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading @@ -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; } Loading Loading @@ -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(); Loading @@ -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); Loading @@ -157,6 +166,7 @@ public class PipAnimationController { addListener(this); addUpdateListener(this); mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mTransitionDirection = TRANSITION_DIRECTION_NONE; } @Override Loading Loading @@ -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() { Loading @@ -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}. * Loading @@ -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, Loading @@ -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(); Loading @@ -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(); } Loading
packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +81 −73 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 = Loading @@ -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 Loading @@ -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) { Loading @@ -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); } Loading @@ -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); } Loading @@ -161,7 +163,6 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub { } args.recycle(); return true; } }; private ActivityManager.RunningTaskInfo mTaskInfo; Loading @@ -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() { Loading @@ -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; Loading @@ -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 Loading Loading @@ -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()); Loading @@ -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; } Loading Loading @@ -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) { Loading @@ -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)); } Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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"); Loading @@ -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); Loading @@ -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"); Loading @@ -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()); Loading
packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java +24 −13 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading @@ -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); Loading Loading @@ -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() {} } Loading