Restore support for source hint rect
- Also fix one issue with the wrong insets being used for the inset
bounds, we need to update display layout whenever rotation happens
not only if we are in PIP since we will next use the display layout
insets to calculate the inset bounds
Change-Id: Iab209fe67318f94aad3bd78e5c30a80663bbb101
Bug: 156669445
Test: Enter pip from youtube, ensure it crops to the video during the
animation
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.pip;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.RectEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.IntDef;
|
||||
import android.content.Context;
|
||||
@@ -26,6 +27,7 @@ import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.Interpolators;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -74,15 +76,12 @@ public class PipAnimationController {
|
||||
|| direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN;
|
||||
}
|
||||
|
||||
private final Interpolator mFastOutSlowInInterpolator;
|
||||
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
|
||||
|
||||
private PipTransitionAnimator mCurrentAnimator;
|
||||
|
||||
@Inject
|
||||
PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
|
||||
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
|
||||
com.android.internal.R.interpolator.fast_out_slow_in);
|
||||
mSurfaceTransactionHelper = helper;
|
||||
}
|
||||
|
||||
@@ -104,10 +103,11 @@ public class PipAnimationController {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds) {
|
||||
PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds,
|
||||
Rect sourceHintRect) {
|
||||
if (mCurrentAnimator == null) {
|
||||
mCurrentAnimator = setupPipTransitionAnimator(
|
||||
PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
|
||||
PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect));
|
||||
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
|
||||
&& mCurrentAnimator.isRunning()) {
|
||||
// If we are still animating the fade into pip, then just move the surface and ensure
|
||||
@@ -122,7 +122,7 @@ public class PipAnimationController {
|
||||
} else {
|
||||
mCurrentAnimator.cancel();
|
||||
mCurrentAnimator = setupPipTransitionAnimator(
|
||||
PipTransitionAnimator.ofBounds(leash, startBounds, endBounds));
|
||||
PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect));
|
||||
}
|
||||
return mCurrentAnimator;
|
||||
}
|
||||
@@ -133,7 +133,7 @@ public class PipAnimationController {
|
||||
|
||||
private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
|
||||
animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
|
||||
animator.setInterpolator(mFastOutSlowInInterpolator);
|
||||
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
animator.setFloatValues(FRACTION_START, FRACTION_END);
|
||||
return animator;
|
||||
}
|
||||
@@ -331,6 +331,7 @@ public class PipAnimationController {
|
||||
@Override
|
||||
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
|
||||
getSurfaceTransactionHelper()
|
||||
.resetScale(tx, leash, getDestinationBounds())
|
||||
.crop(tx, leash, getDestinationBounds())
|
||||
.round(tx, leash, shouldApplyCornerRadius());
|
||||
tx.show(leash);
|
||||
@@ -346,35 +347,46 @@ public class PipAnimationController {
|
||||
}
|
||||
|
||||
static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
|
||||
Rect startValue, Rect endValue) {
|
||||
Rect startValue, Rect endValue, Rect sourceHintRect) {
|
||||
// Just for simplicity we'll interpolate between the source rect hint insets and empty
|
||||
// insets to calculate the window crop
|
||||
final Rect initialStartValue = new Rect(startValue);
|
||||
final Rect sourceHintRectInsets = sourceHintRect != null
|
||||
? new Rect(sourceHintRect.left - startValue.left,
|
||||
sourceHintRect.top - startValue.top,
|
||||
startValue.right - sourceHintRect.right,
|
||||
startValue.bottom - sourceHintRect.bottom)
|
||||
: null;
|
||||
final Rect sourceInsets = new Rect(0, 0, 0, 0);
|
||||
|
||||
// construct new Rect instances in case they are recycled
|
||||
return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
|
||||
endValue, new Rect(startValue), new Rect(endValue)) {
|
||||
private final Rect mTmpRect = new Rect();
|
||||
|
||||
private int getCastedFractionValue(float start, float end, float fraction) {
|
||||
return (int) (start * (1 - fraction) + end * fraction + .5f);
|
||||
}
|
||||
private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
|
||||
private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
|
||||
|
||||
@Override
|
||||
void applySurfaceControlTransaction(SurfaceControl leash,
|
||||
SurfaceControl.Transaction tx, float fraction) {
|
||||
final Rect start = getStartValue();
|
||||
final Rect end = getEndValue();
|
||||
mTmpRect.set(
|
||||
getCastedFractionValue(start.left, end.left, fraction),
|
||||
getCastedFractionValue(start.top, end.top, fraction),
|
||||
getCastedFractionValue(start.right, end.right, fraction),
|
||||
getCastedFractionValue(start.bottom, end.bottom, fraction));
|
||||
setCurrentValue(mTmpRect);
|
||||
Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
|
||||
setCurrentValue(bounds);
|
||||
if (inScaleTransition()) {
|
||||
if (isOutPipDirection(getTransitionDirection())) {
|
||||
getSurfaceTransactionHelper().scale(tx, leash, end, mTmpRect);
|
||||
getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
|
||||
} else {
|
||||
getSurfaceTransactionHelper().scale(tx, leash, start, mTmpRect);
|
||||
getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
|
||||
}
|
||||
} else {
|
||||
getSurfaceTransactionHelper().crop(tx, leash, mTmpRect);
|
||||
if (sourceHintRectInsets != null) {
|
||||
Rect insets = mInsetsEvaluator.evaluate(fraction, sourceInsets,
|
||||
sourceHintRectInsets);
|
||||
getSurfaceTransactionHelper().scaleAndCrop(tx, leash, initialStartValue,
|
||||
bounds, insets);
|
||||
} else {
|
||||
getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
|
||||
}
|
||||
}
|
||||
tx.apply();
|
||||
}
|
||||
@@ -390,11 +402,11 @@ public class PipAnimationController {
|
||||
|
||||
@Override
|
||||
void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
|
||||
if (!inScaleTransition()) return;
|
||||
// NOTE: intentionally does not apply the transaction here.
|
||||
// this end transaction should get executed synchronously with the final
|
||||
// WindowContainerTransaction in task organizer
|
||||
getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds())
|
||||
getSurfaceTransactionHelper()
|
||||
.resetScale(tx, leash, getDestinationBounds())
|
||||
.crop(tx, leash, getDestinationBounds());
|
||||
}
|
||||
|
||||
|
||||
@@ -296,6 +296,14 @@ public class PipBoundsHandler {
|
||||
*/
|
||||
public boolean onDisplayRotationChanged(Rect outBounds, Rect oldBounds, Rect outInsetBounds,
|
||||
int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) {
|
||||
// Calculate the snap fraction of the current stack along the old movement bounds
|
||||
final Rect postChangeStackBounds = new Rect(oldBounds);
|
||||
final float snapFraction = getSnapFraction(postChangeStackBounds);
|
||||
|
||||
// Update the display layout, note that we have to do this on every rotation even if we
|
||||
// aren't in PIP since we need to update the display layout to get the right resources
|
||||
mDisplayLayout.rotateTo(mContext.getResources(), toRotation);
|
||||
|
||||
// Bail early if the event is not sent to current {@link #mDisplayInfo}
|
||||
if ((displayId != mDisplayInfo.displayId) || (fromRotation == toRotation)) {
|
||||
return false;
|
||||
@@ -312,13 +320,6 @@ public class PipBoundsHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate the snap fraction of the current stack along the old movement bounds
|
||||
final Rect postChangeStackBounds = new Rect(oldBounds);
|
||||
final float snapFraction = getSnapFraction(postChangeStackBounds);
|
||||
|
||||
// Update the display layout
|
||||
mDisplayLayout.rotateTo(mContext.getResources(), toRotation);
|
||||
|
||||
// Populate the new {@link #mDisplayInfo}.
|
||||
// The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
|
||||
// therefore, the width/height may require a swap first.
|
||||
|
||||
@@ -44,6 +44,7 @@ public class PipSurfaceTransactionHelper implements ConfigurationController.Conf
|
||||
private final float[] mTmpFloat9 = new float[9];
|
||||
private final RectF mTmpSourceRectF = new RectF();
|
||||
private final RectF mTmpDestinationRectF = new RectF();
|
||||
private final Rect mTmpDestinationRect = new Rect();
|
||||
|
||||
@Inject
|
||||
public PipSurfaceTransactionHelper(Context context, ConfigurationController configController) {
|
||||
@@ -90,7 +91,30 @@ public class PipSurfaceTransactionHelper implements ConfigurationController.Conf
|
||||
mTmpDestinationRectF.set(destinationBounds);
|
||||
mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
|
||||
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
|
||||
.setPosition(leash, destinationBounds.left, destinationBounds.top);
|
||||
.setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Operates the scale (setMatrix) on a given transaction and leash
|
||||
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
|
||||
*/
|
||||
PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||
Rect sourceBounds, Rect destinationBounds, Rect insets) {
|
||||
mTmpSourceRectF.set(sourceBounds);
|
||||
mTmpDestinationRect.set(sourceBounds);
|
||||
mTmpDestinationRect.inset(insets);
|
||||
// Scale by the shortest edge and offset such that the top/left of the scaled inset source
|
||||
// rect aligns with the top/left of the destination bounds
|
||||
final float scale = sourceBounds.width() <= sourceBounds.height()
|
||||
? (float) destinationBounds.width() / sourceBounds.width()
|
||||
: (float) destinationBounds.height() / sourceBounds.height();
|
||||
final float left = destinationBounds.left - insets.left * scale;
|
||||
final float top = destinationBounds.top - insets.top * scale;
|
||||
mTmpTransform.setScale(scale, scale);
|
||||
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
|
||||
.setWindowCrop(leash, mTmpDestinationRect)
|
||||
.setPosition(leash, left, top);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -143,8 +143,10 @@ public class PipTaskOrganizer extends TaskOrganizer implements
|
||||
case MSG_RESIZE_ANIMATE: {
|
||||
Rect currentBounds = (Rect) args.arg2;
|
||||
Rect toBounds = (Rect) args.arg3;
|
||||
Rect sourceHintRect = (Rect) args.arg4;
|
||||
int duration = args.argi2;
|
||||
animateResizePip(currentBounds, toBounds, args.argi1 /* direction */, duration);
|
||||
animateResizePip(currentBounds, toBounds, sourceHintRect,
|
||||
args.argi1 /* direction */, duration);
|
||||
if (updateBoundsCallback != null) {
|
||||
updateBoundsCallback.accept(toBounds);
|
||||
}
|
||||
@@ -294,7 +296,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements
|
||||
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
|
||||
t.apply();
|
||||
scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
|
||||
direction, animationDurationMs, null /* updateBoundsCallback */);
|
||||
null /* sourceHintRect */, direction, animationDurationMs,
|
||||
null /* updateBoundsCallback */);
|
||||
mInPip = false;
|
||||
}
|
||||
});
|
||||
@@ -357,7 +360,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements
|
||||
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
|
||||
|
||||
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
|
||||
scheduleAnimateResizePip(currentBounds, destinationBounds,
|
||||
final Rect sourceHintRect = getValidSourceHintRect(info, currentBounds);
|
||||
scheduleAnimateResizePip(currentBounds, destinationBounds, sourceHintRect,
|
||||
TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
|
||||
null /* updateBoundsCallback */);
|
||||
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
||||
@@ -368,6 +372,21 @@ public class PipTaskOrganizer extends TaskOrganizer implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source hint rect if it is valid (if provided and is contained by the current
|
||||
* task bounds).
|
||||
*/
|
||||
private Rect getValidSourceHintRect(ActivityManager.RunningTaskInfo info, Rect sourceBounds) {
|
||||
final Rect sourceHintRect = info.pictureInPictureParams != null
|
||||
&& info.pictureInPictureParams.hasSourceBoundsHint()
|
||||
? info.pictureInPictureParams.getSourceRectHint()
|
||||
: null;
|
||||
if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) {
|
||||
return sourceHintRect;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) {
|
||||
// If we are fading the PIP in, then we should move the pip to the final location as
|
||||
// soon as possible, but set the alpha immediately since the transaction can take a
|
||||
@@ -552,13 +571,13 @@ public class PipTaskOrganizer extends TaskOrganizer implements
|
||||
Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
|
||||
return;
|
||||
}
|
||||
scheduleAnimateResizePip(mLastReportedBounds, toBounds,
|
||||
scheduleAnimateResizePip(mLastReportedBounds, toBounds, null /* sourceHintRect */,
|
||||
TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
|
||||
}
|
||||
|
||||
private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
|
||||
@PipAnimationController.TransitionDirection int direction, int durationMs,
|
||||
Consumer<Rect> updateBoundsCallback) {
|
||||
Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction,
|
||||
int durationMs, Consumer<Rect> updateBoundsCallback) {
|
||||
if (!mInPip) {
|
||||
// can be initiated in other component, ignore if we are no longer in PIP
|
||||
return;
|
||||
@@ -568,6 +587,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
|
||||
args.arg1 = updateBoundsCallback;
|
||||
args.arg2 = currentBounds;
|
||||
args.arg3 = destinationBounds;
|
||||
args.arg4 = sourceHintRect;
|
||||
args.argi1 = direction;
|
||||
args.argi2 = durationMs;
|
||||
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
|
||||
@@ -667,7 +687,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements
|
||||
}
|
||||
final Rect destinationBounds = new Rect(originalBounds);
|
||||
destinationBounds.offset(xOffset, yOffset);
|
||||
animateResizePip(originalBounds, destinationBounds, TRANSITION_DIRECTION_SAME, durationMs);
|
||||
animateResizePip(originalBounds, destinationBounds, null /* sourceHintRect */,
|
||||
TRANSITION_DIRECTION_SAME, durationMs);
|
||||
}
|
||||
|
||||
private void resizePip(Rect destinationBounds) {
|
||||
@@ -745,7 +766,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
|
||||
WindowOrganizer.applyTransaction(wct);
|
||||
}
|
||||
|
||||
private void animateResizePip(Rect currentBounds, Rect destinationBounds,
|
||||
private void animateResizePip(Rect currentBounds, Rect destinationBounds, Rect sourceHintRect,
|
||||
@PipAnimationController.TransitionDirection int direction, int durationMs) {
|
||||
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
|
||||
throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
|
||||
@@ -757,7 +778,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements
|
||||
return;
|
||||
}
|
||||
mPipAnimationController
|
||||
.getAnimator(mLeash, currentBounds, destinationBounds)
|
||||
.getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect)
|
||||
.setTransitionDirection(direction)
|
||||
.setPipAnimationCallback(mPipAnimationCallback)
|
||||
.setDuration(durationMs)
|
||||
|
||||
@@ -82,7 +82,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void getAnimator_withBounds_returnBoundsAnimator() {
|
||||
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
|
||||
.getAnimator(mLeash, new Rect(), new Rect());
|
||||
.getAnimator(mLeash, new Rect(), new Rect(), null);
|
||||
|
||||
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
|
||||
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
|
||||
@@ -94,12 +94,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, startValue, endValue1);
|
||||
.getAnimator(mLeash, startValue, endValue1, null);
|
||||
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
|
||||
oldAnimator.start();
|
||||
|
||||
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
|
||||
.getAnimator(mLeash, startValue, endValue2);
|
||||
.getAnimator(mLeash, startValue, endValue2, null);
|
||||
|
||||
assertEquals("getAnimator with same type returns same animator",
|
||||
oldAnimator, newAnimator);
|
||||
@@ -129,7 +129,7 @@ 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 animator = mPipAnimationController
|
||||
.getAnimator(mLeash, startValue, endValue1);
|
||||
.getAnimator(mLeash, startValue, endValue1, null);
|
||||
|
||||
animator.updateEndValue(endValue2);
|
||||
|
||||
@@ -141,7 +141,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, startValue, endValue);
|
||||
.getAnimator(mLeash, startValue, endValue, null);
|
||||
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
|
||||
|
||||
animator.setPipAnimationCallback(mPipAnimationCallback);
|
||||
|
||||
Reference in New Issue
Block a user