Merge "Re-implement pinch-to-resize gesture."

This commit is contained in:
Ben Lin
2020-12-05 04:55:55 +00:00
committed by Android (Google) Code Review
6 changed files with 318 additions and 120 deletions

View File

@@ -54,6 +54,7 @@ public class PipAnimationController {
public static final int TRANSITION_DIRECTION_LEAVE_PIP = 3;
public static final int TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN = 4;
public static final int TRANSITION_DIRECTION_REMOVE_STACK = 5;
public static final int TRANSITION_DIRECTION_SNAP_AFTER_RESIZE = 6;
@IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = {
TRANSITION_DIRECTION_NONE,
@@ -61,7 +62,8 @@ public class PipAnimationController {
TRANSITION_DIRECTION_TO_PIP,
TRANSITION_DIRECTION_LEAVE_PIP,
TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN,
TRANSITION_DIRECTION_REMOVE_STACK
TRANSITION_DIRECTION_REMOVE_STACK,
TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
})
@Retention(RetentionPolicy.SOURCE)
public @interface TransitionDirection {}
@@ -109,13 +111,27 @@ public class PipAnimationController {
}
@SuppressWarnings("unchecked")
/**
* 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 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
* to the correct snap fraction region), then provide the base bounds, which is current PiP
* leash bounds before transformation/any animation. This is so when we try to construct
* the different transformation matrices for the animation, we are constructing this based off
* the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
*/
@VisibleForTesting
public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds,
Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction) {
public PipTransitionAnimator getAnimator(SurfaceControl leash, Rect baseBounds,
Rect startBounds, Rect endBounds, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect,
direction));
PipTransitionAnimator.ofBounds(leash, startBounds, startBounds, endBounds,
sourceHintRect, direction));
} 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
@@ -130,8 +146,8 @@ public class PipAnimationController {
} else {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect,
direction));
PipTransitionAnimator.ofBounds(leash, baseBounds, startBounds, endBounds,
sourceHintRect, direction));
}
return mCurrentAnimator;
}
@@ -180,6 +196,7 @@ public class PipAnimationController {
private final @AnimationType int mAnimationType;
private final Rect mDestinationBounds = new Rect();
private T mBaseValue;
protected T mCurrentValue;
protected T mStartValue;
private T mEndValue;
@@ -190,10 +207,11 @@ public class PipAnimationController {
private @TransitionDirection int mTransitionDirection;
private PipTransitionAnimator(SurfaceControl leash, @AnimationType int animationType,
Rect destinationBounds, T startValue, T endValue) {
Rect destinationBounds, T baseValue, T startValue, T endValue) {
mLeash = leash;
mAnimationType = animationType;
mDestinationBounds.set(destinationBounds);
mBaseValue = baseValue;
mStartValue = startValue;
mEndValue = endValue;
addListener(this);
@@ -263,6 +281,10 @@ public class PipAnimationController {
return mStartValue;
}
T getBaseValue() {
return mBaseValue;
}
@VisibleForTesting
public T getEndValue() {
return mEndValue;
@@ -334,7 +356,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, endValue) {
destinationBounds, startValue, startValue, endValue) {
@Override
void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction) {
@@ -367,7 +389,7 @@ public class PipAnimationController {
}
static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
Rect startValue, Rect endValue, Rect sourceHintRect,
Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction) {
// Just for simplicity we'll interpolate between the source rect hint insets and empty
// insets to calculate the window crop
@@ -375,7 +397,7 @@ public class PipAnimationController {
if (isOutPipDirection(direction)) {
initialSourceValue = new Rect(endValue);
} else {
initialSourceValue = new Rect(startValue);
initialSourceValue = new Rect(baseValue);
}
final Rect sourceHintRectInsets;
@@ -391,22 +413,24 @@ public class PipAnimationController {
// construct new Rect instances in case they are recycled
return new PipTransitionAnimator<Rect>(leash, ANIM_TYPE_BOUNDS,
endValue, new Rect(startValue), new Rect(endValue)) {
endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) {
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 base = getBaseValue();
final Rect start = getStartValue();
final Rect end = getEndValue();
Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
setCurrentValue(bounds);
if (inScaleTransition() || sourceHintRect == null) {
if (isOutPipDirection(direction)) {
getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
} else {
getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
getSurfaceTransactionHelper().scale(tx, leash, base, bounds);
}
} else {
final Rect insets;

View File

@@ -30,6 +30,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SNAP_AFTER_RESIZE;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
@@ -814,6 +815,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
}
/**
* Animates resizing of the pinned stack given the duration and start bounds.
* 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) {
if (mShouldDeferEnteringPip) {
Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
return;
}
scheduleAnimateResizePip(fromBounds, toBounds, null /* sourceHintRect */,
TRANSITION_DIRECTION_SNAP_AFTER_RESIZE, duration, updateBoundsCallback);
}
private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction,
int durationMs, Consumer<Rect> updateBoundsCallback) {
@@ -1073,8 +1088,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Log.w(TAG, "Abort animation, invalid leash");
return;
}
Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
? mPipBoundsState.getBounds() : currentBounds;
mPipAnimationController
.getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect, direction)
.getAnimator(mLeash, baseBounds, currentBounds, destinationBounds, sourceHintRect,
direction)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.pip.phone;
import android.graphics.Point;
import android.graphics.Rect;
/**
* Helper class to calculate the new size given two-fingers pinch to resize.
*/
public class PipPinchResizingAlgorithm {
private static final Rect TMP_RECT = new Rect();
/**
* Given inputs and requirements and current PiP bounds, return the new size.
*
* @param x0 x-coordinate of the primary input.
* @param y0 y-coordinate of the primary input.
* @param x1 x-coordinate of the secondary input.
* @param y1 y-coordinate of the secondary input.
* @param downx0 x-coordinate of the original down point of the primary input.
* @param downy0 y-coordinate of the original down ponit of the primary input.
* @param downx1 x-coordinate of the original down point of the secondary input.
* @param downy1 y-coordinate of the original down point of the secondary input.
* @param currentPipBounds current PiP bounds.
* @param minVisibleWidth minimum visible width.
* @param minVisibleHeight minimum visible height.
* @param maxSize max size.
* @return The new resized PiP bounds, sharing the same center.
*/
public static Rect pinchResize(float x0, float y0, float x1, float y1,
float downx0, float downy0, float downx1, float downy1, Rect currentPipBounds,
int minVisibleWidth, int minVisibleHeight, Point maxSize) {
int width = currentPipBounds.width();
int height = currentPipBounds.height();
int left = currentPipBounds.left;
int top = currentPipBounds.top;
int right = currentPipBounds.right;
int bottom = currentPipBounds.bottom;
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));
width = Math.max(minVisibleWidth, Math.min(width + widthDelta, maxSize.x));
height = Math.max(minVisibleHeight, Math.min(height + heightDelta, maxSize.y));
// Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
// drag axis. What ever is producing the bigger rectangle will be chosen.
int width1;
int width2;
int height1;
int height2;
if (aspect > 1.0f) {
// 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);
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)));
}
// 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);
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)));
}
} 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);
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)));
}
// 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);
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)));
}
}
// Use the bigger of the two rectangles if the major change was positive, otherwise
// do the opposite.
final boolean grows = width > (right - left) || height > (bottom - top);
if (grows == (width1 * height1 > width2 * height2)) {
width = width1;
height = height1;
} else {
width = width2;
height = height2;
}
TMP_RECT.set(currentPipBounds.centerX() - width / 2,
currentPipBounds.centerY() - height / 2,
currentPipBounds.centerX() + width / 2,
currentPipBounds.centerY() + height / 2);
return TMP_RECT;
}
}

View File

@@ -39,7 +39,6 @@ import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InputMonitor;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ViewConfiguration;
import androidx.annotation.VisibleForTesting;
@@ -62,19 +61,21 @@ import java.util.function.Function;
public class PipResizeGestureHandler {
private static final String TAG = "PipResizeGestureHandler";
private static final float PINCH_THRESHOLD = 0.05f;
private static final float STARTING_SCALE_FACTOR = 1.0f;
private static final int PINCH_RESIZE_SNAP_DURATION = 250;
private final Context mContext;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final PipMotionHelper mMotionHelper;
private final PipBoundsState mPipBoundsState;
private final PipTaskOrganizer mPipTaskOrganizer;
private final PhonePipMenuController mPhonePipMenuController;
private final PipUiEventLogger mPipUiEventLogger;
private final int mDisplayId;
private final Executor mMainExecutor;
private final ScaleGestureDetector mScaleGestureDetector;
private final Region mTmpRegion = new Region();
private final PointF mDownPoint = new PointF();
private final PointF mDownSecondaryPoint = new PointF();
private final Point mMaxSize = new Point();
private final Point mMinSize = new Point();
private final Rect mLastResizeBounds = new Rect();
@@ -88,6 +89,7 @@ public class PipResizeGestureHandler {
private final Rect mDisplayBounds = new Rect();
private final Function<Rect, Rect> mMovementBoundsSupplier;
private final Runnable mUpdateMovementBoundsRunnable;
private final Handler mHandler;
private int mDelta;
private float mTouchSlop;
@@ -96,15 +98,17 @@ public class PipResizeGestureHandler {
private boolean mIsEnabled;
private boolean mEnablePinchResize;
private boolean mIsSysUiStateValid;
// For drag-resize
private boolean mThresholdCrossed;
// For pinch-resize
private boolean mThresholdCrossed0;
private boolean mThresholdCrossed1;
private boolean mUsingPinchToZoom = false;
private float mScaleFactor = STARTING_SCALE_FACTOR;
int mFirstIndex = -1;
int mSecondIndex = -1;
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
private PipTaskOrganizer mPipTaskOrganizer;
private PhonePipMenuController mPhonePipMenuController;
private PipUiEventLogger mPipUiEventLogger;
private int mCtrlType;
@@ -124,66 +128,11 @@ public class PipResizeGestureHandler {
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
mHandler = new Handler(Looper.getMainLooper());
context.getDisplay().getRealSize(mMaxSize);
reloadResources();
mScaleGestureDetector = new ScaleGestureDetector(context,
new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
if (!mThresholdCrossed
&& (mScaleFactor > (STARTING_SCALE_FACTOR + PINCH_THRESHOLD)
|| mScaleFactor < (STARTING_SCALE_FACTOR - PINCH_THRESHOLD))) {
mThresholdCrossed = true;
mInputMonitor.pilferPointers();
}
if (mThresholdCrossed) {
int height = Math.min(mMaxSize.y, Math.max(mMinSize.y,
(int) (mScaleFactor * mLastDownBounds.height())));
int width = Math.min(mMaxSize.x, Math.max(mMinSize.x,
(int) (mScaleFactor * mLastDownBounds.width())));
int top, bottom, left, right;
if ((mCtrlType & CTRL_TOP) != 0) {
top = mLastDownBounds.bottom - height;
bottom = mLastDownBounds.bottom;
} else {
top = mLastDownBounds.top;
bottom = mLastDownBounds.top + height;
}
if ((mCtrlType & CTRL_LEFT) != 0) {
left = mLastDownBounds.right - width;
right = mLastDownBounds.right;
} else {
left = mLastDownBounds.left;
right = mLastDownBounds.left + width;
}
mLastResizeBounds.set(left, top, right, bottom);
mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds,
mLastResizeBounds,
null);
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
setCtrlTypeForPinchToZoom();
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
mScaleFactor = STARTING_SCALE_FACTOR;
finishResize();
}
});
mEnablePinchResize = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_PINCH_RESIZE,
@@ -274,13 +223,20 @@ public class PipResizeGestureHandler {
if (ev instanceof MotionEvent) {
if (mUsingPinchToZoom) {
mScaleGestureDetector.onTouchEvent((MotionEvent) ev);
onPinchResize((MotionEvent) ev);
} else {
onDragCornerResize((MotionEvent) ev);
}
}
}
/**
* Checks if there is currently an on-going gesture, either drag-resize or pinch-resize.
*/
public boolean hasOngoingGesture() {
return mCtrlType != CTRL_NONE || mUsingPinchToZoom;
}
/**
* Check whether the current x,y coordinate is within the region in which drag-resize should
* start.
@@ -295,7 +251,7 @@ public class PipResizeGestureHandler {
* |_|_|_________|_|_|
* |_|_| |_|_|
*/
public boolean isWithinTouchRegion(int x, int y) {
public boolean isWithinDragResizeRegion(int x, int y) {
final Rect currentPipBounds = mPipBoundsState.getBounds();
if (currentPipBounds == null) {
return false;
@@ -327,15 +283,14 @@ public class PipResizeGestureHandler {
if (isInValidSysUiState()) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// Always pass the DOWN event to the ScaleGestureDetector
mScaleGestureDetector.onTouchEvent(ev);
if (isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY())) {
if (isWithinDragResizeRegion((int) ev.getRawX(), (int) ev.getRawY())) {
return true;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (mEnablePinchResize && ev.getPointerCount() == 2) {
onPinchResize(ev);
mUsingPinchToZoom = true;
return true;
}
@@ -348,33 +303,11 @@ public class PipResizeGestureHandler {
return false;
}
private void setCtrlTypeForPinchToZoom() {
final Rect currentPipBounds = mPipBoundsState.getBounds();
mLastDownBounds.set(mPipBoundsState.getBounds());
Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
mDisplayBounds.set(movementBounds.left,
movementBounds.top,
movementBounds.right + currentPipBounds.width(),
movementBounds.bottom + currentPipBounds.height());
if (currentPipBounds.left == mDisplayBounds.left) {
mCtrlType |= CTRL_RIGHT;
} else {
mCtrlType |= CTRL_LEFT;
}
if (currentPipBounds.top > mDisplayBounds.top + mDisplayBounds.height()) {
mCtrlType |= CTRL_TOP;
} else {
mCtrlType |= CTRL_BOTTOM;
}
}
private void setCtrlType(int x, int y) {
final Rect currentPipBounds = mPipBoundsState.getBounds();
Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
mDisplayBounds.set(movementBounds.left,
movementBounds.top,
movementBounds.right + currentPipBounds.width(),
@@ -408,6 +341,78 @@ public class PipResizeGestureHandler {
return mIsSysUiStateValid;
}
private void onPinchResize(MotionEvent ev) {
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
mFirstIndex = -1;
mSecondIndex = -1;
finishResize();
}
if (ev.getPointerCount() != 2) {
return;
}
if (action == MotionEvent.ACTION_POINTER_DOWN) {
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());
}
}
if (action == MotionEvent.ACTION_MOVE) {
if (mFirstIndex == -1 || mSecondIndex == -1) {
return;
}
float x0 = ev.getRawX(mFirstIndex);
float y0 = ev.getRawY(mFirstIndex);
float x1 = ev.getRawX(mSecondIndex);
float y1 = ev.getRawY(mSecondIndex);
double hypot0 = Math.hypot(x0 - mDownPoint.x, y0 - mDownPoint.y);
double hypot1 = Math.hypot(x1 - mDownSecondaryPoint.x, y1 - mDownSecondaryPoint.y);
// Capture inputs
if (hypot0 > mTouchSlop && !mThresholdCrossed0) {
mInputMonitor.pilferPointers();
mThresholdCrossed0 = true;
// Reset the down to begin resizing from this point
mDownPoint.set(x0, y0);
}
if (hypot1 > mTouchSlop && !mThresholdCrossed1) {
mInputMonitor.pilferPointers();
mThresholdCrossed1 = true;
// Reset the down to begin resizing from this point
mDownSecondaryPoint.set(x1, y1);
}
if (mThresholdCrossed0 || mThresholdCrossed1) {
if (mPhonePipMenuController.isMenuVisible()) {
mPhonePipMenuController.hideMenu();
}
x0 = mThresholdCrossed0 ? x0 : mDownPoint.x;
y0 = mThresholdCrossed0 ? y0 : mDownPoint.y;
x1 = mThresholdCrossed1 ? x1 : mDownSecondaryPoint.x;
y1 = mThresholdCrossed1 ? y1 : mDownSecondaryPoint.y;
final Rect currentPipBounds = mPipBoundsState.getBounds();
mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1,
mDownPoint.x, mDownPoint.y, mDownSecondaryPoint.x, mDownSecondaryPoint.y,
currentPipBounds, mMinSize.x, mMinSize.y, mMaxSize));
mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
null);
}
}
}
private void onDragCornerResize(MotionEvent ev) {
int action = ev.getActionMasked();
float x = ev.getX();
@@ -415,7 +420,7 @@ public class PipResizeGestureHandler {
if (action == MotionEvent.ACTION_DOWN) {
final Rect currentPipBounds = mPipBoundsState.getBounds();
mLastResizeBounds.setEmpty();
mAllowGesture = isInValidSysUiState() && isWithinTouchRegion((int) x, (int) y);
mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y);
if (mAllowGesture) {
setCtrlType((int) x, (int) y);
mDownPoint.set(x, y);
@@ -468,15 +473,30 @@ public class PipResizeGestureHandler {
private void finishResize() {
if (!mLastResizeBounds.isEmpty()) {
mUserResizeBounds.set(mLastResizeBounds);
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
(Rect bounds) -> {
new Handler(Looper.getMainLooper()).post(() -> {
mMotionHelper.synchronizePinnedStackBounds();
mUpdateMovementBoundsRunnable.run();
resetState();
final Runnable callback = () -> {
mUserResizeBounds.set(mLastResizeBounds);
mMotionHelper.synchronizePinnedStackBounds();
mUpdateMovementBoundsRunnable.run();
resetState();
};
// Pinch-to-resize needs to re-calculate snap fraction and animate to the snapped
// position correctly. Drag-resize does not need to move, so just finalize resize.
if (mUsingPinchToZoom) {
final Rect startBounds = new Rect(mLastResizeBounds);
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds,
mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()));
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
PINCH_RESIZE_SNAP_DURATION,
(Rect rect) -> {
mHandler.post(callback);
});
});
} else {
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
(Rect bounds) -> {
mHandler.post(callback);
});
}
mPipUiEventLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
} else {

View File

@@ -470,6 +470,11 @@ public class PipTouchHandler {
return true;
}
if (mPipResizeGestureHandler.hasOngoingGesture()) {
mPipDismissTargetHandler.hideDismissTargetMaybe();
return true;
}
if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
&& mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) {
// If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event

View File

@@ -79,7 +79,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
@Test
public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mLeash, new Rect(), new Rect(), null, TRANSITION_DIRECTION_TO_PIP);
.getAnimator(mLeash, new Rect(), new Rect(), new Rect(), null,
TRANSITION_DIRECTION_TO_PIP);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -87,16 +88,19 @@ public class PipAnimationControllerTest extends ShellTestCase {
@Test
public void getAnimator_whenSameTypeRunning_updateExistingAnimator() {
final Rect baseValue = new Rect(0, 0, 100, 100);
final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
.getAnimator(mLeash, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP);
.getAnimator(mLeash, baseValue, startValue, endValue1, null,
TRANSITION_DIRECTION_TO_PIP);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
.getAnimator(mLeash, startValue, endValue2, null, TRANSITION_DIRECTION_TO_PIP);
.getAnimator(mLeash, baseValue, startValue, endValue2, null,
TRANSITION_DIRECTION_TO_PIP);
assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator);
@@ -122,11 +126,13 @@ public class PipAnimationControllerTest extends ShellTestCase {
@Test
@SuppressWarnings("unchecked")
public void pipTransitionAnimator_updateEndValue() {
final Rect baseValue = new Rect(0, 0, 100, 100);
final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mLeash, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP);
.getAnimator(mLeash, baseValue, startValue, endValue1, null,
TRANSITION_DIRECTION_TO_PIP);
animator.updateEndValue(endValue2);
@@ -135,10 +141,12 @@ public class PipAnimationControllerTest extends ShellTestCase {
@Test
public void pipTransitionAnimator_setPipAnimationCallback() {
final Rect baseValue = new Rect(0, 0, 100, 100);
final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mLeash, startValue, endValue, null, TRANSITION_DIRECTION_TO_PIP);
.getAnimator(mLeash, baseValue, startValue, endValue, null,
TRANSITION_DIRECTION_TO_PIP);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
animator.setPipAnimationCallback(mPipAnimationCallback);