Merge "Re-implement pinch-to-resize gesture."
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user