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:
Winson Chung
2020-06-02 21:22:37 -07:00
parent 60312c6aa1
commit 5175580c2c
5 changed files with 104 additions and 46 deletions

View File

@@ -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());
}

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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);