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:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user