Add corner radius for PiP window

Per UI/UX, the radius is fixed to 8dp, configurable in SystemUI
resources. Also included in this CL:
- deprecated scheduleFinishEnterPip with a more meaningful transition
direction @IntDef
- consolidate the parameter orders in PipTaskOrganizer,
    Sf.Transtion > from / to bounds > direction > duration > others

Screenshot
- https://screenshot.googleplex.com/y67xOhM7Hxt.png
- https://screenshot.googleplex.com/EUvw63Y8ckY.png

Bug: 137390622
Test: Enter/expand/collapse PiP
Test: atest PipAnimationControllerTest
Test: atest PinnedStackTests
Change-Id: I87231746b34c36eba6aaec77b61c9249e835fd47
This commit is contained in:
Hongwei Wang
2020-03-03 17:41:02 -08:00
parent 381b9c1781
commit df8bb00371
4 changed files with 168 additions and 112 deletions

View File

@@ -983,6 +983,9 @@
<!-- The touchable/draggable edge size for PIP resize. --> <!-- The touchable/draggable edge size for PIP resize. -->
<dimen name="pip_resize_edge_size">30dp</dimen> <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="default_gear_space">18dp</dimen>
<dimen name="cell_overlay_padding">18dp</dimen> <dimen name="cell_overlay_padding">18dp</dimen>

View File

@@ -37,7 +37,6 @@ public class PipAnimationController {
private static final float FRACTION_START = 0f; private static final float FRACTION_START = 0f;
private static final float FRACTION_END = 1f; 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 DURATION_DEFAULT_MS = 425;
public static final int ANIM_TYPE_BOUNDS = 0; public static final int ANIM_TYPE_BOUNDS = 0;
public static final int ANIM_TYPE_ALPHA = 1; public static final int ANIM_TYPE_ALPHA = 1;
@@ -49,6 +48,20 @@ public class PipAnimationController {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface AnimationType {} 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 final Interpolator mFastOutSlowInInterpolator;
private PipTransitionAnimator mCurrentAnimator; private PipTransitionAnimator mCurrentAnimator;
@@ -58,30 +71,28 @@ public class PipAnimationController {
com.android.internal.R.interpolator.fast_out_slow_in); 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) { Rect destinationBounds, float alphaStart, float alphaEnd) {
if (mCurrentAnimator == null) { if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator( mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds, PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
alphaStart, alphaEnd));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) { && mCurrentAnimator.isRunning()) {
mCurrentAnimator.updateEndValue(alphaEnd); mCurrentAnimator.updateEndValue(alphaEnd);
} else { } else {
mCurrentAnimator.cancel(); mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator( mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds, PipTransitionAnimator.ofAlpha(leash, destinationBounds, alphaStart, alphaEnd));
alphaStart, alphaEnd));
} }
return mCurrentAnimator; return mCurrentAnimator;
} }
PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip, @SuppressWarnings("unchecked")
Rect startBounds, Rect endBounds) { PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds) {
if (mCurrentAnimator == null) { if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator( mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(leash, scheduleFinishPip, PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
startBounds, endBounds));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
&& mCurrentAnimator.isRunning()) { && mCurrentAnimator.isRunning()) {
mCurrentAnimator.setDestinationBounds(endBounds); mCurrentAnimator.setDestinationBounds(endBounds);
@@ -90,8 +101,7 @@ public class PipAnimationController {
} else { } else {
mCurrentAnimator.cancel(); mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator( mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(leash, scheduleFinishPip, PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
startBounds, endBounds));
} }
return mCurrentAnimator; return mCurrentAnimator;
} }
@@ -134,7 +144,6 @@ public class PipAnimationController {
public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorUpdateListener,
ValueAnimator.AnimatorListener { ValueAnimator.AnimatorListener {
private final boolean mScheduleFinishPip;
private final SurfaceControl mLeash; private final SurfaceControl mLeash;
private final @AnimationType int mAnimationType; private final @AnimationType int mAnimationType;
private final Rect mDestinationBounds = new Rect(); private final Rect mDestinationBounds = new Rect();
@@ -144,11 +153,11 @@ public class PipAnimationController {
private T mCurrentValue; private T mCurrentValue;
private PipAnimationCallback mPipAnimationCallback; private PipAnimationCallback mPipAnimationCallback;
private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory;
private @TransitionDirection int mTransitionDirection;
private int mCornerRadius;
private PipTransitionAnimator(SurfaceControl leash, boolean scheduleFinishPip, private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
@AnimationType int animationType, Rect destinationBounds, Rect destinationBounds, T startValue, T endValue) {
T startValue, T endValue) {
mScheduleFinishPip = scheduleFinishPip;
mLeash = leash; mLeash = leash;
mAnimationType = animationType; mAnimationType = animationType;
mDestinationBounds.set(destinationBounds); mDestinationBounds.set(destinationBounds);
@@ -157,6 +166,7 @@ public class PipAnimationController {
addListener(this); addListener(this);
addUpdateListener(this); addUpdateListener(this);
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mTransitionDirection = TRANSITION_DIRECTION_NONE;
} }
@Override @Override
@@ -202,8 +212,15 @@ public class PipAnimationController {
return this; return this;
} }
boolean shouldScheduleFinishPip() { @TransitionDirection int getTransitionDirection() {
return mScheduleFinishPip; return mTransitionDirection;
}
PipTransitionAnimator<T> setTransitionDirection(@TransitionDirection int direction) {
if (direction != TRANSITION_DIRECTION_SAME) {
mTransitionDirection = direction;
}
return this;
} }
T getStartValue() { T getStartValue() {
@@ -226,6 +243,19 @@ public class PipAnimationController {
mCurrentValue = value; 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}. * Updates the {@link #mEndValue}.
* *
@@ -251,9 +281,9 @@ public class PipAnimationController {
abstract void applySurfaceControlTransaction(SurfaceControl leash, abstract void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction); 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) { 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) { destinationBounds, startValue, endValue) {
@Override @Override
void applySurfaceControlTransaction(SurfaceControl leash, void applySurfaceControlTransaction(SurfaceControl leash,
@@ -266,16 +296,18 @@ public class PipAnimationController {
final Rect bounds = getDestinationBounds(); final Rect bounds = getDestinationBounds();
tx.setPosition(leash, bounds.left, bounds.top) tx.setPosition(leash, bounds.left, bounds.top)
.setWindowCrop(leash, bounds.width(), bounds.height()); .setWindowCrop(leash, bounds.width(), bounds.height());
tx.setCornerRadius(leash,
shouldApplyCornerRadius() ? getCornerRadius() : 0);
} }
tx.apply(); tx.apply();
} }
}; };
} }
static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash, boolean scheduleFinishPip, static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
Rect startValue, Rect endValue) { Rect startValue, Rect endValue) {
// construct new Rect instances in case they are recycled // 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)) { endValue, new Rect(startValue), new Rect(endValue)) {
private final Rect mTmpRect = new Rect(); private final Rect mTmpRect = new Rect();
@@ -299,6 +331,8 @@ public class PipAnimationController {
if (Float.compare(fraction, FRACTION_START) == 0) { if (Float.compare(fraction, FRACTION_START) == 0) {
// Ensure the start condition // Ensure the start condition
tx.setAlpha(leash, 1f); tx.setAlpha(leash, 1f);
tx.setCornerRadius(leash,
shouldApplyCornerRadius() ? getCornerRadius() : 0);
} }
tx.apply(); tx.apply();
} }

View File

@@ -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_ALPHA;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS; 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.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.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
@@ -30,7 +34,6 @@ import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import android.view.DisplayInfo; import android.view.DisplayInfo;
@@ -40,6 +43,7 @@ import android.view.SurfaceControl;
import android.view.WindowContainerTransaction; import android.view.WindowContainerTransaction;
import com.android.internal.os.SomeArgs; import com.android.internal.os.SomeArgs;
import com.android.systemui.R;
import com.android.systemui.pip.phone.PipUpdateThread; import com.android.systemui.pip.phone.PipUpdateThread;
import java.util.ArrayList; import java.util.ArrayList;
@@ -74,6 +78,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
private final Rect mDisplayBounds = new Rect(); private final Rect mDisplayBounds = new Rect();
private final Rect mLastReportedBounds = new Rect(); private final Rect mLastReportedBounds = new Rect();
private final int mCornerRadius;
// These callbacks are called on the update thread // These callbacks are called on the update thread
private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -97,7 +102,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
callback.onPipTransitionFinished(); callback.onPipTransitionFinished();
} }
}); });
finishResize(animator.getDestinationBounds(), tx, animator.shouldScheduleFinishPip()); finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection());
} }
@Override @Override
@@ -111,57 +116,53 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
} }
}; };
private Handler.Callback mUpdateCallbacks = new Handler.Callback() { @SuppressWarnings("unchecked")
@Override private Handler.Callback mUpdateCallbacks = (msg) -> {
public boolean handleMessage(Message msg) { SomeArgs args = (SomeArgs) msg.obj;
SomeArgs args = (SomeArgs) msg.obj; Consumer<Rect> updateBoundsCallback = (Consumer<Rect>) args.arg1;
Consumer<Rect> updateBoundsCallback = (Consumer<Rect>) args.arg1; switch (msg.what) {
switch (msg.what) { case MSG_RESIZE_IMMEDIATE: {
case MSG_RESIZE_IMMEDIATE: { Rect toBounds = (Rect) args.arg2;
Rect toBounds = (Rect) args.arg2; resizePip(toBounds);
resizePip(toBounds); if (updateBoundsCallback != null) {
if (updateBoundsCallback != null) { updateBoundsCallback.accept(toBounds);
updateBoundsCallback.accept(toBounds);
}
break;
}
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);
if (updateBoundsCallback != null) {
updateBoundsCallback.accept(toBounds);
}
break;
}
case MSG_OFFSET_ANIMATE: {
Rect originalBounds = (Rect) args.arg2;
final int offset = args.argi1;
final int duration = args.argi2;
offsetPip(originalBounds, 0 /* xOffset */, offset, duration);
Rect toBounds = new Rect(originalBounds);
toBounds.offset(0, offset);
if (updateBoundsCallback != null) {
updateBoundsCallback.accept(toBounds);
}
break;
}
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);
if (updateBoundsCallback != null) {
updateBoundsCallback.accept(toBounds);
}
break;
} }
break;
}
case MSG_RESIZE_ANIMATE: {
Rect currentBounds = (Rect) args.arg2;
Rect toBounds = (Rect) args.arg3;
int duration = args.argi2;
animateResizePip(currentBounds, toBounds, args.argi1 /* direction */, duration);
if (updateBoundsCallback != null) {
updateBoundsCallback.accept(toBounds);
}
break;
}
case MSG_OFFSET_ANIMATE: {
Rect originalBounds = (Rect) args.arg2;
final int offset = args.argi1;
final int duration = args.argi2;
offsetPip(originalBounds, 0 /* xOffset */, offset, duration);
Rect toBounds = new Rect(originalBounds);
toBounds.offset(0, offset);
if (updateBoundsCallback != null) {
updateBoundsCallback.accept(toBounds);
}
break;
}
case MSG_FINISH_RESIZE: {
SurfaceControl.Transaction tx = (SurfaceControl.Transaction) args.arg2;
Rect toBounds = (Rect) args.arg3;
finishResize(tx, toBounds, args.argi1 /* direction */);
if (updateBoundsCallback != null) {
updateBoundsCallback.accept(toBounds);
}
break;
} }
args.recycle();
return true;
} }
args.recycle();
return true;
}; };
private ActivityManager.RunningTaskInfo mTaskInfo; private ActivityManager.RunningTaskInfo mTaskInfo;
@@ -176,6 +177,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController(); mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController();
mPipBoundsHandler = boundsHandler; mPipBoundsHandler = boundsHandler;
mPipAnimationController = new PipAnimationController(context); mPipAnimationController = new PipAnimationController(context);
mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius);
} }
public Handler getUpdateHandler() { public Handler getUpdateHandler() {
@@ -191,7 +193,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
/** /**
* Sets the preferred animation type for one time. * 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) { public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
mOneShotAnimationType = animationType; mOneShotAnimationType = animationType;
@@ -200,13 +203,14 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
/** /**
* Updates the display dimension with given {@link DisplayInfo} * Updates the display dimension with given {@link DisplayInfo}
*/ */
@SuppressWarnings("unchecked")
public void onDisplayInfoChanged(DisplayInfo displayInfo) { public void onDisplayInfoChanged(DisplayInfo displayInfo) {
final Rect newDisplayBounds = new Rect(0, 0, final Rect newDisplayBounds = new Rect(0, 0,
displayInfo.logicalWidth, displayInfo.logicalHeight); displayInfo.logicalWidth, displayInfo.logicalHeight);
if (!mDisplayBounds.equals(newDisplayBounds)) { if (!mDisplayBounds.equals(newDisplayBounds)) {
// Updates the exiting PiP animation in case the screen rotation changes in the middle. // 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 // 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 = final PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getCurrentAnimator(); mPipAnimationController.getCurrentAnimator();
if (animator != null if (animator != null
@@ -250,12 +254,13 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
} }
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
scheduleAnimateResizePip(true /* scheduleFinishPip */, scheduleAnimateResizePip(currentBounds, destinationBounds,
currentBounds, destinationBounds, DURATION_DEFAULT_MS, null); TRANSITION_DIRECTION_TO_PIP, DURATION_DEFAULT_MS, null);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
mUpdateHandler.post(() -> mPipAnimationController mUpdateHandler.post(() -> mPipAnimationController
.getAnimator(mLeash, true /* scheduleFinishPip */, .getAnimator(mLeash, destinationBounds, 0f, 1f)
destinationBounds, 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setCornerRadius(mCornerRadius)
.setPipAnimationCallback(mPipAnimationCallback) .setPipAnimationCallback(mPipAnimationCallback)
.setDuration(DURATION_DEFAULT_MS) .setDuration(DURATION_DEFAULT_MS)
.start()); .start());
@@ -272,7 +277,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
Log.wtf(TAG, "Unrecognized token: " + token); Log.wtf(TAG, "Unrecognized token: " + token);
return; return;
} }
scheduleAnimateResizePip(mDisplayBounds, DURATION_DEFAULT_MS, null); scheduleAnimateResizePip(mLastReportedBounds, mDisplayBounds,
TRANSITION_DIRECTION_TO_FULLSCREEN, DURATION_DEFAULT_MS, null);
mInPip = false; mInPip = false;
} }
@@ -310,12 +316,12 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
*/ */
public void scheduleAnimateResizePip(Rect toBounds, int duration, public void scheduleAnimateResizePip(Rect toBounds, int duration,
Consumer<Rect> updateBoundsCallback) { Consumer<Rect> updateBoundsCallback) {
scheduleAnimateResizePip(false /* scheduleFinishPip */, scheduleAnimateResizePip(mLastReportedBounds, toBounds,
mLastReportedBounds, toBounds, duration, updateBoundsCallback); TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
} }
private void scheduleAnimateResizePip(boolean scheduleFinishPip, private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
Rect currentBounds, Rect destinationBounds, int durationMs, @PipAnimationController.TransitionDirection int direction, int durationMs,
Consumer<Rect> updateBoundsCallback) { Consumer<Rect> updateBoundsCallback) {
Objects.requireNonNull(mToken, "Requires valid IWindowContainer"); Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
if (!mInPip) { if (!mInPip) {
@@ -326,7 +332,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
args.arg1 = updateBoundsCallback; args.arg1 = updateBoundsCallback;
args.arg2 = currentBounds; args.arg2 = currentBounds;
args.arg3 = destinationBounds; args.arg3 = destinationBounds;
args.argi1 = scheduleFinishPip ? 1 : 0; args.argi1 = direction;
args.argi2 = durationMs; args.argi2 = durationMs;
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args)); mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
} }
@@ -351,25 +357,25 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
Objects.requireNonNull(mToken, "Requires valid IWindowContainer"); Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
SurfaceControl.Transaction tx = new SurfaceControl.Transaction() SurfaceControl.Transaction tx = new SurfaceControl.Transaction()
.setPosition(mLeash, destinationBounds.left, destinationBounds.top) .setPosition(mLeash, destinationBounds.left, destinationBounds.top)
.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height())
scheduleFinishResizePip(tx, destinationBounds, false /* scheduleFinishPip */, .setCornerRadius(mLeash, mInPip ? mCornerRadius : 0);
null); scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE, null);
} }
private void scheduleFinishResizePip(SurfaceControl.Transaction tx, private void scheduleFinishResizePip(SurfaceControl.Transaction tx,
Rect destinationBounds, boolean scheduleFinishPip, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction,
Consumer<Rect> updateBoundsCallback) { Consumer<Rect> updateBoundsCallback) {
Objects.requireNonNull(mToken, "Requires valid IWindowContainer"); Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
SomeArgs args = SomeArgs.obtain(); SomeArgs args = SomeArgs.obtain();
args.arg1 = updateBoundsCallback; args.arg1 = updateBoundsCallback;
args.arg2 = tx; args.arg2 = tx;
args.arg3 = destinationBounds; args.arg3 = destinationBounds;
args.argi1 = scheduleFinishPip ? 1 : 0; args.argi1 = direction;
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_FINISH_RESIZE, args)); 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, public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
Consumer<Rect> updateBoundsCallback) { Consumer<Rect> updateBoundsCallback) {
@@ -398,8 +404,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
} }
final Rect destinationBounds = new Rect(originalBounds); final Rect destinationBounds = new Rect(originalBounds);
destinationBounds.offset(xOffset, yOffset); destinationBounds.offset(xOffset, yOffset);
animateResizePip(false /* scheduleFinishPip*/, originalBounds, destinationBounds, animateResizePip(originalBounds, destinationBounds, TRANSITION_DIRECTION_SAME, durationMs);
durationMs);
} }
private void resizePip(Rect destinationBounds) { private void resizePip(Rect destinationBounds) {
@@ -416,11 +421,12 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
new SurfaceControl.Transaction() new SurfaceControl.Transaction()
.setPosition(mLeash, destinationBounds.left, destinationBounds.top) .setPosition(mLeash, destinationBounds.left, destinationBounds.top)
.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()) .setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height())
.setCornerRadius(mLeash, mInPip ? mCornerRadius : 0)
.apply(); .apply();
} }
private void finishResize(Rect destinationBounds, SurfaceControl.Transaction tx, private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
boolean shouldScheduleFinishPip) { @PipAnimationController.TransitionDirection int direction) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) { if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleResizePip() instead of this " throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
+ "directly"); + "directly");
@@ -428,7 +434,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
mLastReportedBounds.set(destinationBounds); mLastReportedBounds.set(destinationBounds);
try { try {
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
if (shouldScheduleFinishPip) { if (direction == TRANSITION_DIRECTION_TO_PIP) {
wct.scheduleFinishEnterPip(mToken, destinationBounds); wct.scheduleFinishEnterPip(mToken, destinationBounds);
} else { } else {
wct.setBounds(mToken, destinationBounds); wct.setBounds(mToken, destinationBounds);
@@ -440,8 +446,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
} }
} }
private void animateResizePip(boolean scheduleFinishPip, Rect currentBounds, private void animateResizePip(Rect currentBounds, Rect destinationBounds,
Rect destinationBounds, int durationMs) { @PipAnimationController.TransitionDirection int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) { if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of " throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
+ "this directly"); + "this directly");
@@ -452,7 +458,9 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
return; return;
} }
mUpdateHandler.post(() -> mPipAnimationController mUpdateHandler.post(() -> mPipAnimationController
.getAnimator(mLeash, scheduleFinishPip, currentBounds, destinationBounds) .getAnimator(mLeash, currentBounds, destinationBounds)
.setTransitionDirection(direction)
.setCornerRadius(mCornerRadius)
.setPipAnimationCallback(mPipAnimationCallback) .setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs) .setDuration(durationMs)
.start()); .start());

View File

@@ -16,9 +16,10 @@
package com.android.systemui.pip; 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.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -64,7 +65,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
@Test @Test
public void getAnimator_withAlpha_returnFloatAnimator() { public void getAnimator_withAlpha_returnFloatAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController 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", assertEquals("Expect ANIM_TYPE_ALPHA animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA); animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA);
@@ -73,7 +74,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
@Test @Test
public void getAnimator_withBounds_returnBoundsAnimator() { public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController 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", assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS); 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 endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300); final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
.getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue1); .getAnimator(mLeash, startValue, endValue1);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start(); oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
.getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue2); .getAnimator(mLeash, startValue, endValue2);
assertEquals("getAnimator with same type returns same animator", assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator); oldAnimator, newAnimator);
@@ -99,23 +100,28 @@ public class PipAnimationControllerTest extends SysuiTestCase {
} }
@Test @Test
public void getAnimator_scheduleFinishPip() { public void getAnimator_setTransitionDirection() {
PipAnimationController.PipTransitionAnimator animator = mPipAnimationController PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mLeash, true /* scheduleFinishPip */, new Rect(), 0f, 1f); .getAnimator(mLeash, new Rect(), 0f, 1f)
assertTrue("scheduleFinishPip is true", animator.shouldScheduleFinishPip()); .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP);
assertEquals("Transition to PiP mode",
animator.getTransitionDirection(), TRANSITION_DIRECTION_TO_PIP);
animator = mPipAnimationController animator = mPipAnimationController
.getAnimator(mLeash, false /* scheduleFinishPip */, new Rect(), 0f, 1f); .getAnimator(mLeash, new Rect(), 0f, 1f)
assertFalse("scheduleFinishPip is false", animator.shouldScheduleFinishPip()); .setTransitionDirection(TRANSITION_DIRECTION_TO_FULLSCREEN);
assertEquals("Transition to fullscreen mode",
animator.getTransitionDirection(), TRANSITION_DIRECTION_TO_FULLSCREEN);
} }
@Test @Test
@SuppressWarnings("unchecked")
public void pipTransitionAnimator_updateEndValue() { public void pipTransitionAnimator_updateEndValue() {
final Rect startValue = new Rect(0, 0, 100, 100); final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue1 = new Rect(100, 100, 200, 200); final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300); final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue1); .getAnimator(mLeash, startValue, endValue1);
animator.updateEndValue(endValue2); animator.updateEndValue(endValue2);
@@ -127,7 +133,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
final Rect startValue = new Rect(0, 0, 100, 100); final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue = new Rect(100, 100, 200, 200); final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue); .getAnimator(mLeash, startValue, endValue);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
animator.setPipAnimationCallback(mPipAnimationCallback); animator.setPipAnimationCallback(mPipAnimationCallback);
@@ -166,6 +172,11 @@ public class PipAnimationControllerTest extends SysuiTestCase {
return this; return this;
} }
@Override
public SurfaceControl.Transaction setCornerRadius(SurfaceControl leash, float radius) {
return this;
}
@Override @Override
public void apply() {} public void apply() {}
} }