Merge changes Ie9be757c,I2e239d23

* changes:
  PiP pinch-to-resize: Enable two finger movement and slight rotation.
  PiP pinch-to-resize: add double-tap to max/min size.
This commit is contained in:
Ben Lin
2021-01-23 19:20:49 +00:00
committed by Android (Google) Code Review
9 changed files with 213 additions and 55 deletions

View File

@@ -17,4 +17,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell">
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
</manifest>

View File

@@ -116,7 +116,8 @@ public class PipAnimationController {
* Construct and return an animator that animates from the {@param startBounds} to the
* {@param endBounds} with the given {@param direction}. If {@param direction} is type
* {@link ANIM_TYPE_BOUNDS}, then {@param sourceHintRect} will be used to animate
* in a better, more smooth manner.
* in a better, more smooth manner. If the original bound was rotated and a reset needs to
* happen, pass in {@param startingAngle}.
*
* In the case where one wants to start animation during an intermediate animation (for example,
* if the user is currently doing a pinch-resize, and upon letting go now PiP needs to animate
@@ -128,11 +129,11 @@ public class PipAnimationController {
@VisibleForTesting
public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect baseBounds,
Rect startBounds, Rect endBounds, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction) {
@PipAnimationController.TransitionDirection int direction, float startingAngle) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(leash, startBounds, startBounds, endBounds,
sourceHintRect, direction));
sourceHintRect, direction, 0 /* startingAngle */));
} 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
@@ -148,7 +149,7 @@ public class PipAnimationController {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(leash, baseBounds, startBounds, endBounds,
sourceHintRect, direction));
sourceHintRect, direction, startingAngle));
}
return mCurrentAnimator;
}
@@ -201,6 +202,7 @@ public class PipAnimationController {
protected T mCurrentValue;
protected T mStartValue;
private T mEndValue;
private float mStartingAngle;
private PipAnimationCallback mPipAnimationCallback;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
@@ -208,13 +210,15 @@ public class PipAnimationController {
private @TransitionDirection int mTransitionDirection;
private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
Rect destinationBounds, T baseValue, T startValue, T endValue) {
Rect destinationBounds, T baseValue, T startValue, T endValue,
float startingAngle) {
mLeash = leash;
mAnimationType = animationType;
mDestinationBounds.set(destinationBounds);
mBaseValue = baseValue;
mStartValue = startValue;
mEndValue = endValue;
mStartingAngle = startingAngle;
addListener(this);
addUpdateListener(this);
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
@@ -363,7 +367,7 @@ public class PipAnimationController {
static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash,
Rect destinationBounds, float startValue, float endValue) {
return new PipTransitionAnimator<Float>(leash, ANIM_TYPE_ALPHA,
destinationBounds, startValue, startValue, endValue) {
destinationBounds, startValue, startValue, endValue, 0) {
@Override
void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction) {
@@ -397,7 +401,7 @@ public class PipAnimationController {
static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction) {
@PipAnimationController.TransitionDirection int direction, float startingAngle) {
// Just for simplicity we'll interpolate between the source rect hint insets and empty
// insets to calculate the window crop
final Rect initialSourceValue;
@@ -420,7 +424,8 @@ public class PipAnimationController {
// construct new Rect instances in case they are recycled
return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) {
endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
startingAngle) {
private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
@@ -431,13 +436,14 @@ public class PipAnimationController {
final Rect start = getStartValue();
final Rect end = getEndValue();
Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
float angle = (1.0f - fraction) * startingAngle;
setCurrentValue(bounds);
if (inScaleTransition() || sourceHintRect == null) {
if (isOutPipDirection(direction)) {
getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
} else {
getSurfaceTransactionHelper().scale(tx, leash, base, bounds);
getSurfaceTransactionHelper().scale(tx, leash, base, bounds, angle);
}
} else {
final Rect insets;

View File

@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Size;
import android.view.DisplayInfo;
@@ -59,6 +60,8 @@ public final class PipBoundsState {
private final @NonNull Rect mExpandedBounds = new Rect();
private final @NonNull Rect mNormalMovementBounds = new Rect();
private final @NonNull Rect mExpandedMovementBounds = new Rect();
private final Point mMaxSize = new Point();
private final Point mMinSize = new Point();
private final @NonNull Context mContext;
private float mAspectRatio;
private int mStashedState = STASH_TYPE_NONE;
@@ -151,6 +154,24 @@ public final class PipBoundsState {
mExpandedMovementBounds.set(bounds);
}
/** Sets the max possible size for resize. */
public void setMaxSize(int width, int height) {
mMaxSize.set(width, height);
}
/** Sets the min possible size for resize. */
public void setMinSize(int width, int height) {
mMinSize.set(width, height);
}
public Point getMaxSize() {
return mMaxSize;
}
public Point getMinSize() {
return mMinSize;
}
/** Returns the expanded movement bounds. */
@NonNull
public Rect getExpandedMovementBounds() {

View File

@@ -84,9 +84,19 @@ public class PipSurfaceTransactionHelper {
*/
public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect sourceBounds, Rect destinationBounds) {
return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */);
}
/**
* Operates the scale (setMatrix) on a given transaction and leash, along with a rotation.
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
Rect sourceBounds, Rect destinationBounds, float degrees) {
mTmpSourceRectF.set(sourceBounds);
mTmpDestinationRectF.set(destinationBounds);
mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
mTmpTransform.postRotate(degrees);
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
.setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top);
return this;

View File

@@ -195,9 +195,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Rect currentBounds = (Rect) args.arg2;
Rect toBounds = (Rect) args.arg3;
Rect sourceHintRect = (Rect) args.arg4;
float startingAngle = (float) args.arg5;
int duration = args.argi2;
animateResizePip(currentBounds, toBounds, sourceHintRect,
args.argi1 /* direction */, duration);
args.argi1 /* direction */, duration, startingAngle);
if (updateBoundsCallback != null) {
updateBoundsCallback.accept(toBounds);
}
@@ -227,7 +228,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
case MSG_RESIZE_USER: {
Rect startBounds = (Rect) args.arg2;
Rect toBounds = (Rect) args.arg3;
userResizePip(startBounds, toBounds);
float degrees = (float) args.arg4;
userResizePip(startBounds, toBounds, degrees);
if (updateBoundsCallback != null) {
updateBoundsCallback.accept(toBounds);
}
@@ -427,7 +429,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final Rect sourceHintRect = getValidSourceHintRect(mPictureInPictureParams,
destinationBounds);
scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
sourceHintRect, direction, animationDurationMs,
0 /* startingAngle */, sourceHintRect, direction, animationDurationMs,
null /* updateBoundsCallback */);
mState = State.EXITING_PIP;
}
@@ -535,8 +537,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipMenuController.attach(mLeash);
final Rect sourceHintRect = getValidSourceHintRect(info.pictureInPictureParams,
currentBounds);
scheduleAnimateResizePip(currentBounds, destinationBounds, sourceHintRect,
TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */,
sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
null /* updateBoundsCallback */);
mState = State.ENTERING_PIP;
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
@@ -822,8 +824,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
return;
}
scheduleAnimateResizePip(mPipBoundsState.getBounds(), toBounds, null /* sourceHintRect */,
TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
scheduleAnimateResizePip(mPipBoundsState.getBounds(), toBounds, 0 /* startingAngle */,
null /* sourceHintRect */, TRANSITION_DIRECTION_NONE, duration,
updateBoundsCallback);
}
/**
@@ -831,18 +834,23 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* This is used when the starting bounds is not the current PiP bounds.
*/
public void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration,
Consumer<Rect> updateBoundsCallback) {
float startingAngle, Consumer<Rect> updateBoundsCallback) {
if (mWaitForFixedRotation) {
Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
return;
}
scheduleAnimateResizePip(fromBounds, toBounds, null /* sourceHintRect */,
scheduleAnimateResizePip(fromBounds, toBounds, startingAngle, null /* sourceHintRect */,
TRANSITION_DIRECTION_SNAP_AFTER_RESIZE, duration, updateBoundsCallback);
}
/**
* Animates resizing of the pinned stack given the duration and start bounds.
* This always animates the angle to zero from the starting angle.
*/
private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction,
int durationMs, Consumer<Rect> updateBoundsCallback) {
float startingAngle, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, int durationMs,
Consumer<Rect> updateBoundsCallback) {
if (!mState.isInPip()) {
// TODO: tend to use shouldBlockResizeRequest here as well but need to consider
// the fact that when in exitPip, scheduleAnimateResizePip is executed in the window
@@ -855,6 +863,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
args.arg2 = currentBounds;
args.arg3 = destinationBounds;
args.arg4 = sourceHintRect;
args.arg5 = startingAngle;
args.argi1 = direction;
args.argi2 = durationMs;
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
@@ -871,16 +880,26 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args));
}
/**
* Directly perform manipulation/resize on the leash, along with rotation. This will not perform
* any {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
*/
public void scheduleUserResizePip(Rect startBounds, Rect toBounds,
Consumer<Rect> updateBoundsCallback) {
scheduleUserResizePip(startBounds, toBounds, 0 /* degrees */, updateBoundsCallback);
}
/**
* Directly perform a scaled matrix transformation on the leash. This will not perform any
* {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
*/
public void scheduleUserResizePip(Rect startBounds, Rect toBounds,
public void scheduleUserResizePip(Rect startBounds, Rect toBounds, float degrees,
Consumer<Rect> updateBoundsCallback) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = updateBoundsCallback;
args.arg2 = startBounds;
args.arg3 = toBounds;
args.arg4 = degrees;
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_USER, args));
}
@@ -959,7 +978,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final Rect destinationBounds = new Rect(originalBounds);
destinationBounds.offset(xOffset, yOffset);
animateResizePip(originalBounds, destinationBounds, null /* sourceHintRect */,
TRANSITION_DIRECTION_SAME, durationMs);
TRANSITION_DIRECTION_SAME, durationMs, 0);
}
private void resizePip(Rect destinationBounds) {
@@ -985,7 +1004,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
}
private void userResizePip(Rect startBounds, Rect destinationBounds) {
private void userResizePip(Rect startBounds, Rect destinationBounds, float degrees) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleUserResizePip() instead of "
+ "this directly");
@@ -1002,7 +1021,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds);
mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds, degrees);
if (mPipMenuController.isMenuVisible()) {
runOnMainHandler(() ->
mPipMenuController.movePipMenu(mLeash, tx, destinationBounds));
@@ -1089,7 +1108,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
private void animateResizePip(Rect currentBounds, Rect destinationBounds, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, int durationMs) {
@PipAnimationController.TransitionDirection int direction, int durationMs,
float startingAngle) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
+ "this directly");
@@ -1103,7 +1123,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
? mPipBoundsState.getBounds() : currentBounds;
mPipAnimationController
.getAnimator(mLeash, baseBounds, currentBounds, destinationBounds, sourceHintRect,
direction)
direction, startingAngle)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)

View File

@@ -53,6 +53,8 @@ public class PipPinchResizingAlgorithm {
final float aspect = (float) width / (float) height;
final int widthDelta = Math.round(Math.abs(x0 - x1) - Math.abs(downx0 - downx1));
final int heightDelta = Math.round(Math.abs(y0 - y1) - Math.abs(downy0 - downy1));
final int dx = (int) ((x0 - downx0 + x1 - downx1) / 2);
final int dy = (int) ((y0 - downy0 + y1 - downy1) / 2);
width = Math.max(minVisibleWidth, Math.min(width + widthDelta, maxSize.x));
height = Math.max(minVisibleHeight, Math.min(height + heightDelta, maxSize.y));
@@ -85,21 +87,21 @@ public class PipPinchResizingAlgorithm {
} else {
// Assuming that the width is our target we calculate the height.
width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width));
height1 = Math.round((float) width1 * aspect);
height1 = Math.round((float) width1 / aspect);
if (height1 < minVisibleHeight) {
// If the resulting height is too small we adjust to the minimal size.
height1 = minVisibleHeight;
width1 = Math.max(minVisibleWidth,
Math.min(maxSize.x, Math.round((float) height1 / aspect)));
Math.min(maxSize.x, Math.round((float) height1 * aspect)));
}
// Assuming that the height is our target we calculate the width.
height2 = Math.max(minVisibleHeight, Math.min(maxSize.y, height));
width2 = Math.round((float) height2 / aspect);
width2 = Math.round((float) height2 * aspect);
if (width2 < minVisibleWidth) {
// If the resulting width is too small we adjust to the minimal size.
width2 = minVisibleWidth;
height2 = Math.max(minVisibleHeight,
Math.min(maxSize.y, Math.round((float) width2 * aspect)));
Math.min(maxSize.y, Math.round((float) width2 / aspect)));
}
}
@@ -118,6 +120,7 @@ public class PipPinchResizingAlgorithm {
currentPipBounds.centerY() - height / 2,
currentPipBounds.centerX() + width / 2,
currentPipBounds.centerY() + height / 2);
TMP_RECT.offset(dx, dy);
return TMP_RECT;
}
}

View File

@@ -62,6 +62,7 @@ public class PipResizeGestureHandler {
private static final String TAG = "PipResizeGestureHandler";
private static final int PINCH_RESIZE_SNAP_DURATION = 250;
private static final int PINCH_RESIZE_MAX_ANGLE_ROTATION = 45;
private final Context mContext;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -359,12 +360,12 @@ public class PipResizeGestureHandler {
if (mFirstIndex == -1 && mSecondIndex == -1) {
mFirstIndex = 0;
mSecondIndex = 1;
mLastResizeBounds.setEmpty();
mDownPoint.set(ev.getRawX(mFirstIndex), ev.getRawY(mFirstIndex));
mDownSecondaryPoint.set(ev.getRawX(mSecondIndex), ev.getRawY(mSecondIndex));
mLastResizeBounds.setEmpty();
mLastDownBounds.set(mPipBoundsState.getBounds());
mLastResizeBounds.set(mLastDownBounds);
}
}
@@ -403,18 +404,63 @@ public class PipResizeGestureHandler {
x1 = mThresholdCrossed1 ? x1 : mDownSecondaryPoint.x;
y1 = mThresholdCrossed1 ? y1 : mDownSecondaryPoint.y;
final Rect currentPipBounds = mPipBoundsState.getBounds();
final Rect originalPipBounds = mPipBoundsState.getBounds();
int focusX = (int) originalPipBounds.centerX();
int focusY = (int) originalPipBounds.centerY();
float down0X = mDownPoint.x;
float down0Y = mDownPoint.y;
float down1X = mDownSecondaryPoint.x;
float down1Y = mDownSecondaryPoint.y;
// Top right + Bottom left pinch to zoom.
if ((down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY)
|| (down1X > focusX && down1Y < focusY
&& down0X < focusX && down0Y > focusY)) {
mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
mLastResizeBounds.centerY(), x0, y0, x1, y1, true);
} else if ((down0X < focusX && down0Y < focusY
&& down1X > focusX && down1Y > focusY)
|| (down1X < focusX && down1Y < focusY
&& down0X > focusX && down0Y > focusY)) {
mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
mLastResizeBounds.centerY(), x0, y0, x1, y1, false);
}
mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1,
mDownPoint.x, mDownPoint.y, mDownSecondaryPoint.x, mDownSecondaryPoint.y,
currentPipBounds, mMinSize.x, mMinSize.y, mMaxSize));
originalPipBounds, mMinSize.x, mMinSize.y, mMaxSize));
mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
null);
(float) -mAngle, null);
mPipBoundsState.setHasUserResizedPip(true);
}
}
}
private float mAngle = 0;
private float calculateRotationAngle(int focusX, int focusY, float x0, float y0,
float x1, float y1, boolean positive) {
// The base angle is the angle formed by taking the angle between the center horizontal
// and one of the corners.
double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - focusY),
Math.abs(mLastResizeBounds.right - focusX)));
double angle0 = mThresholdCrossed0
? Math.toDegrees(Math.atan2(Math.abs(y0 - focusY), Math.abs(x0 - focusX)))
: baseAngle;
double angle1 = mThresholdCrossed1
? Math.toDegrees(Math.atan2(Math.abs(y1 - focusY), Math.abs(x1 - focusX)))
: baseAngle;
// Calculate the percentage difference of [0, 90] compare to the base angle.
double diff0 = (Math.max(0, Math.min(angle0, 90)) - baseAngle) / 90;
double diff1 = (Math.max(0, Math.min(angle1, 90)) - baseAngle) / 90;
return (float) (diff0 + diff1) / 2 * PINCH_RESIZE_MAX_ANGLE_ROTATION * (positive ? 1 : -1);
}
private void onDragCornerResize(MotionEvent ev) {
int action = ev.getActionMasked();
float x = ev.getX();
@@ -490,7 +536,7 @@ public class PipResizeGestureHandler {
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds,
mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()));
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
PINCH_RESIZE_SNAP_DURATION,
PINCH_RESIZE_SNAP_DURATION, mAngle,
(Rect rect) -> {
mHandler.post(callback);
});
@@ -509,6 +555,7 @@ public class PipResizeGestureHandler {
private void resetState() {
mCtrlType = CTRL_NONE;
mAngle = 0;
mUsingPinchToZoom = false;
mAllowGesture = false;
mThresholdCrossed = false;

View File

@@ -63,6 +63,7 @@ public class PipTouchHandler {
private static final String TAG = "PipTouchHandler";
private static final float STASH_MINIMUM_VELOCITY_X = 3000.f;
private static final float MINIMUM_SIZE_PERCENT = 0.4f;
// Allow PIP to resize to a slightly bigger state upon touch
private final boolean mEnableResize;
@@ -359,9 +360,13 @@ public class PipTouchHandler {
mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
bottomOffset);
mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
mPipBoundsState.getExpandedBounds().height());
if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
} else {
mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
mPipBoundsState.getExpandedBounds().height());
}
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
@@ -430,6 +435,30 @@ public class PipTouchHandler {
}
}
private void updatePinchResizeSizeConstraints(Rect insetBounds, Rect normalBounds,
float aspectRatio) {
final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
mPipBoundsState.getDisplayBounds().height());
final int totalPadding = insetBounds.left * 2;
final int minWidth, minHeight, maxWidth, maxHeight;
if (aspectRatio > 1f) {
minWidth = (int) Math.min(normalBounds.width(), shorterLength * MINIMUM_SIZE_PERCENT);
minHeight = (int) (minWidth / aspectRatio);
maxWidth = (int) Math.max(normalBounds.width(), shorterLength - totalPadding);
maxHeight = (int) (maxWidth / aspectRatio);
} else {
minHeight = (int) Math.min(normalBounds.height(), shorterLength * MINIMUM_SIZE_PERCENT);
minWidth = (int) (minHeight * aspectRatio);
maxHeight = (int) Math.max(normalBounds.height(), shorterLength - totalPadding);
maxWidth = (int) (maxHeight * aspectRatio);
}
mPipResizeGestureHandler.updateMinSize(minWidth, minHeight);
mPipResizeGestureHandler.updateMaxSize(maxWidth, maxHeight);
mPipBoundsState.setMaxSize(maxWidth, maxHeight);
mPipBoundsState.setMinSize(minWidth, minHeight);
}
/**
* TODO Add appropriate description
*/
@@ -640,13 +669,36 @@ public class PipTouchHandler {
}
}
private void animateToExpandedState(Runnable callback) {
Rect expandedBounds = new Rect(mPipBoundsState.getExpandedBounds());
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
mPipBoundsState.getMovementBounds(), mPipBoundsState.getExpandedMovementBounds(),
private void animateToMaximizedState(Runnable callback) {
Rect maxMovementBounds = new Rect();
Rect maxBounds = new Rect(0, 0, mPipBoundsState.getMaxSize().x,
mPipBoundsState.getMaxSize().y);
mPipBoundsAlgorithm.getMovementBounds(maxBounds, mInsetBounds, maxMovementBounds,
mIsImeShowing ? mImeHeight : 0);
mSavedSnapFraction = mMotionHelper.animateToExpandedState(maxBounds,
mPipBoundsState.getMovementBounds(), maxMovementBounds,
callback);
}
private void animateToMinimizedState() {
animateToUnexpandedState(new Rect(0, 0, mPipBoundsState.getMinSize().x,
mPipBoundsState.getMinSize().y));
}
private void animateToExpandedState(Runnable callback) {
mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
final Rect currentBounds = mPipBoundsState.getBounds();
final Rect expandedBounds = mPipBoundsState.getExpandedBounds();
Rect finalExpandedBounds = new Rect(expandedBounds.width() > expandedBounds.width()
&& expandedBounds.height() > expandedBounds.height()
? currentBounds : expandedBounds);
Rect restoredMovementBounds = new Rect();
mPipBoundsAlgorithm.getMovementBounds(finalExpandedBounds,
mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
mSavedSnapFraction = mMotionHelper.animateToExpandedState(finalExpandedBounds,
mPipBoundsState.getMovementBounds(), restoredMovementBounds, callback);
}
private void animateToUnexpandedState(Rect restoreBounds) {
Rect restoredMovementBounds = new Rect();
mPipBoundsAlgorithm.getMovementBounds(restoreBounds,
@@ -789,17 +841,15 @@ public class PipTouchHandler {
// If using pinch to zoom, double-tap functions as resizing between max/min size
if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
final boolean toExpand = mPipBoundsState.getBounds().width()
< mPipBoundsState.getExpandedBounds().width()
< mPipBoundsState.getMaxSize().x
&& mPipBoundsState.getBounds().height()
< mPipBoundsState.getExpandedBounds().height();
mPipResizeGestureHandler.setUserResizeBounds(toExpand
? mPipBoundsState.getExpandedBounds()
: mPipBoundsState.getNormalBounds());
< mPipBoundsState.getMaxSize().y;
if (toExpand) {
animateToExpandedState(null);
animateToMaximizedState(null);
} else {
animateToUnexpandedState(mPipBoundsState.getNormalBounds());
animateToMinimizedState();
}
mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
} else {
// Expand to fullscreen if this is a double tap
// the PiP should be frozen until the transition ends

View File

@@ -80,7 +80,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mLeash, new Rect(), new Rect(), new Rect(), null,
TRANSITION_DIRECTION_TO_PIP);
TRANSITION_DIRECTION_TO_PIP, 0);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -94,13 +94,13 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
.getAnimator(mLeash, baseValue, startValue, endValue1, null,
TRANSITION_DIRECTION_TO_PIP);
TRANSITION_DIRECTION_TO_PIP, 0);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
.getAnimator(mLeash, baseValue, startValue, endValue2, null,
TRANSITION_DIRECTION_TO_PIP);
TRANSITION_DIRECTION_TO_PIP, 0);
assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator);
@@ -132,7 +132,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mLeash, baseValue, startValue, endValue1, null,
TRANSITION_DIRECTION_TO_PIP);
TRANSITION_DIRECTION_TO_PIP, 0);
animator.updateEndValue(endValue2);
@@ -146,7 +146,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mLeash, baseValue, startValue, endValue, null,
TRANSITION_DIRECTION_TO_PIP);
TRANSITION_DIRECTION_TO_PIP, 0);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
animator.setPipAnimationCallback(mPipAnimationCallback);