Merge "Fix some issues with pinch to resize" into sc-dev
This commit is contained in:
@@ -94,11 +94,14 @@ public class PipSurfaceTransactionHelper {
|
|||||||
public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
|
public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||||
Rect sourceBounds, Rect destinationBounds, float degrees) {
|
Rect sourceBounds, Rect destinationBounds, float degrees) {
|
||||||
mTmpSourceRectF.set(sourceBounds);
|
mTmpSourceRectF.set(sourceBounds);
|
||||||
|
// We want the matrix to position the surface relative to the screen coordinates so offset
|
||||||
|
// the source to 0,0
|
||||||
|
mTmpSourceRectF.offsetTo(0, 0);
|
||||||
mTmpDestinationRectF.set(destinationBounds);
|
mTmpDestinationRectF.set(destinationBounds);
|
||||||
mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
|
mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
|
||||||
mTmpTransform.postRotate(degrees);
|
mTmpTransform.postRotate(degrees,
|
||||||
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
|
mTmpDestinationRectF.centerX(), mTmpDestinationRectF.centerY());
|
||||||
.setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top);
|
tx.setMatrix(leash, mTmpTransform, mTmpFloat9);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,111 +16,115 @@
|
|||||||
package com.android.wm.shell.pip.phone;
|
package com.android.wm.shell.pip.phone;
|
||||||
|
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
|
import android.graphics.PointF;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to calculate the new size given two-fingers pinch to resize.
|
* Helper class to calculate the new size given two-fingers pinch to resize.
|
||||||
*/
|
*/
|
||||||
public class PipPinchResizingAlgorithm {
|
public class PipPinchResizingAlgorithm {
|
||||||
private static final Rect TMP_RECT = new Rect();
|
|
||||||
|
private static final int PINCH_RESIZE_MAX_ANGLE_ROTATION = 45;
|
||||||
|
private static final float OVERROTATE_DAMP_FACTOR = 0.4f;
|
||||||
|
private static final float ANGLE_THRESHOLD = 5f;
|
||||||
|
|
||||||
|
private final PointF mTmpDownVector = new PointF();
|
||||||
|
private final PointF mTmpLastVector = new PointF();
|
||||||
|
private final PointF mTmpDownCentroid = new PointF();
|
||||||
|
private final PointF mTmpLastCentroid = new PointF();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given inputs and requirements and current PiP bounds, return the new size.
|
* Updates {@param resizeBoundsOut} with the new bounds of the PIP, and returns the angle in
|
||||||
*
|
* degrees that the PIP should be rotated.
|
||||||
* @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,
|
public float calculateBoundsAndAngle(PointF downPoint, PointF downSecondPoint,
|
||||||
float downx0, float downy0, float downx1, float downy1, Rect currentPipBounds,
|
PointF lastPoint, PointF lastSecondPoint, Point minSize, Point maxSize,
|
||||||
int minVisibleWidth, int minVisibleHeight, Point maxSize) {
|
Rect initialBounds, Rect resizeBoundsOut) {
|
||||||
|
float downDist = (float) Math.hypot(downSecondPoint.x - downPoint.x,
|
||||||
|
downSecondPoint.y - downPoint.y);
|
||||||
|
float dist = (float) Math.hypot(lastSecondPoint.x - lastPoint.x,
|
||||||
|
lastSecondPoint.y - lastPoint.y);
|
||||||
|
float minScale = getMinScale(initialBounds, minSize);
|
||||||
|
float maxScale = getMaxScale(initialBounds, maxSize);
|
||||||
|
float scale = Math.max(minScale, Math.min(maxScale, dist / downDist));
|
||||||
|
|
||||||
int width = currentPipBounds.width();
|
// Scale the bounds by the change in distance between the points
|
||||||
int height = currentPipBounds.height();
|
resizeBoundsOut.set(initialBounds);
|
||||||
int left = currentPipBounds.left;
|
scaleRectAboutCenter(resizeBoundsOut, scale);
|
||||||
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));
|
|
||||||
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));
|
// Translate by the centroid movement
|
||||||
height = Math.max(minVisibleHeight, Math.min(height + heightDelta, maxSize.y));
|
getCentroid(downPoint, downSecondPoint, mTmpDownCentroid);
|
||||||
|
getCentroid(lastPoint, lastSecondPoint, mTmpLastCentroid);
|
||||||
|
resizeBoundsOut.offset((int) (mTmpLastCentroid.x - mTmpDownCentroid.x),
|
||||||
|
(int) (mTmpLastCentroid.y - mTmpDownCentroid.y));
|
||||||
|
|
||||||
// Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
|
// Calculate the angle
|
||||||
// drag axis. What ever is producing the bigger rectangle will be chosen.
|
mTmpDownVector.set(downSecondPoint.x - downPoint.x,
|
||||||
int width1;
|
downSecondPoint.y - downPoint.y);
|
||||||
int width2;
|
mTmpLastVector.set(lastSecondPoint.x - lastPoint.x,
|
||||||
int height1;
|
lastSecondPoint.y - lastPoint.y);
|
||||||
int height2;
|
float angle = (float) Math.atan2(cross(mTmpDownVector, mTmpLastVector),
|
||||||
if (aspect > 1.0f) {
|
dot(mTmpDownVector, mTmpLastVector));
|
||||||
// Assuming that the width is our target we calculate the height.
|
return constrainRotationAngle((float) Math.toDegrees(angle));
|
||||||
width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width));
|
}
|
||||||
height1 = Math.round((float) width1 / aspect);
|
|
||||||
if (height1 < minVisibleHeight) {
|
private float getMinScale(Rect bounds, Point minSize) {
|
||||||
// If the resulting height is too small we adjust to the minimal size.
|
return Math.max((float) minSize.x / bounds.width(), (float) minSize.y / bounds.height());
|
||||||
height1 = minVisibleHeight;
|
}
|
||||||
width1 = Math.max(minVisibleWidth,
|
|
||||||
Math.min(maxSize.x, Math.round((float) height1 * aspect)));
|
private float getMaxScale(Rect bounds, Point maxSize) {
|
||||||
}
|
return Math.min((float) maxSize.x / bounds.width(), (float) maxSize.y / bounds.height());
|
||||||
// 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);
|
private float constrainRotationAngle(float angle) {
|
||||||
if (width2 < minVisibleWidth) {
|
// Remove some degrees so that user doesn't immediately start rotating until a threshold
|
||||||
// If the resulting width is too small we adjust to the minimal size.
|
return Math.signum(angle) * Math.max(0, (Math.abs(dampedRotate(angle)) - ANGLE_THRESHOLD));
|
||||||
width2 = minVisibleWidth;
|
}
|
||||||
height2 = Math.max(minVisibleHeight,
|
|
||||||
Math.min(maxSize.y, Math.round((float) width2 / aspect)));
|
/**
|
||||||
}
|
* Given the current rotation angle, dampen it so that as it approaches the maximum angle,
|
||||||
} else {
|
* dampen it.
|
||||||
// Assuming that the width is our target we calculate the height.
|
*/
|
||||||
width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width));
|
private float dampedRotate(float amount) {
|
||||||
height1 = Math.round((float) width1 / aspect);
|
if (Float.compare(amount, 0) == 0) return 0;
|
||||||
if (height1 < minVisibleHeight) {
|
|
||||||
// If the resulting height is too small we adjust to the minimal size.
|
float f = amount / PINCH_RESIZE_MAX_ANGLE_ROTATION;
|
||||||
height1 = minVisibleHeight;
|
f = f / (Math.abs(f)) * (overRotateInfluenceCurve(Math.abs(f)));
|
||||||
width1 = Math.max(minVisibleWidth,
|
|
||||||
Math.min(maxSize.x, Math.round((float) height1 * aspect)));
|
// Clamp this factor, f, to -1 < f < 1
|
||||||
}
|
if (Math.abs(f) >= 1) {
|
||||||
// Assuming that the height is our target we calculate the width.
|
f /= Math.abs(f);
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return OVERROTATE_DAMP_FACTOR * f * PINCH_RESIZE_MAX_ANGLE_ROTATION;
|
||||||
|
}
|
||||||
|
|
||||||
// Use the bigger of the two rectangles if the major change was positive, otherwise
|
/**
|
||||||
// do the opposite.
|
* Returns a value that corresponds to y = (f - 1)^3 + 1.
|
||||||
final boolean grows = width > (right - left) || height > (bottom - top);
|
*/
|
||||||
if (grows == (width1 * height1 > width2 * height2)) {
|
private float overRotateInfluenceCurve(float f) {
|
||||||
width = width1;
|
f -= 1.0f;
|
||||||
height = height1;
|
return f * f * f + 1.0f;
|
||||||
} else {
|
}
|
||||||
width = width2;
|
|
||||||
height = height2;
|
private void getCentroid(PointF p1, PointF p2, PointF centroidOut) {
|
||||||
|
centroidOut.set((p2.x + p1.x) / 2, (p2.y + p1.y) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float dot(PointF p1, PointF p2) {
|
||||||
|
return p1.x * p2.x + p1.y * p2.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float cross(PointF p1, PointF p2) {
|
||||||
|
return p1.x * p2.y - p1.y * p2.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scaleRectAboutCenter(Rect r, float scale) {
|
||||||
|
if (scale != 1.0f) {
|
||||||
|
int cx = r.centerX();
|
||||||
|
int cy = r.centerY();
|
||||||
|
r.offset(-cx, -cy);
|
||||||
|
r.scale(scale);
|
||||||
|
r.offset(cx, cy);
|
||||||
}
|
}
|
||||||
|
|
||||||
TMP_RECT.set(currentPipBounds.centerX() - width / 2,
|
|
||||||
currentPipBounds.centerY() - height / 2,
|
|
||||||
currentPipBounds.centerX() + width / 2,
|
|
||||||
currentPipBounds.centerY() + height / 2);
|
|
||||||
TMP_RECT.offset(dx, dy);
|
|
||||||
return TMP_RECT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,10 +63,7 @@ public class PipResizeGestureHandler {
|
|||||||
|
|
||||||
private static final String TAG = "PipResizeGestureHandler";
|
private static final String TAG = "PipResizeGestureHandler";
|
||||||
private static final int PINCH_RESIZE_SNAP_DURATION = 250;
|
private static final int PINCH_RESIZE_SNAP_DURATION = 250;
|
||||||
private static final int PINCH_RESIZE_MAX_ANGLE_ROTATION = 45;
|
|
||||||
private static final float PINCH_RESIZE_AUTO_MAX_RATIO = 0.9f;
|
private static final float PINCH_RESIZE_AUTO_MAX_RATIO = 0.9f;
|
||||||
private static final float OVERROTATE_DAMP_FACTOR = 0.4f;
|
|
||||||
private static final float ANGLE_THRESHOLD = 5f;
|
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
|
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
|
||||||
@@ -75,17 +72,20 @@ public class PipResizeGestureHandler {
|
|||||||
private final PipTaskOrganizer mPipTaskOrganizer;
|
private final PipTaskOrganizer mPipTaskOrganizer;
|
||||||
private final PhonePipMenuController mPhonePipMenuController;
|
private final PhonePipMenuController mPhonePipMenuController;
|
||||||
private final PipUiEventLogger mPipUiEventLogger;
|
private final PipUiEventLogger mPipUiEventLogger;
|
||||||
|
private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
|
||||||
private final int mDisplayId;
|
private final int mDisplayId;
|
||||||
private final ShellExecutor mMainExecutor;
|
private final ShellExecutor mMainExecutor;
|
||||||
private final Region mTmpRegion = new Region();
|
private final Region mTmpRegion = new Region();
|
||||||
|
|
||||||
private final PointF mDownPoint = new PointF();
|
private final PointF mDownPoint = new PointF();
|
||||||
private final PointF mDownSecondaryPoint = new PointF();
|
private final PointF mDownSecondPoint = new PointF();
|
||||||
|
private final PointF mLastPoint = new PointF();
|
||||||
|
private final PointF mLastSecondPoint = new PointF();
|
||||||
private final Point mMaxSize = new Point();
|
private final Point mMaxSize = new Point();
|
||||||
private final Point mMinSize = new Point();
|
private final Point mMinSize = new Point();
|
||||||
private final Rect mLastResizeBounds = new Rect();
|
private final Rect mLastResizeBounds = new Rect();
|
||||||
private final Rect mUserResizeBounds = new Rect();
|
private final Rect mUserResizeBounds = new Rect();
|
||||||
private final Rect mLastDownBounds = new Rect();
|
private final Rect mDownBounds = new Rect();
|
||||||
private final Rect mDragCornerSize = new Rect();
|
private final Rect mDragCornerSize = new Rect();
|
||||||
private final Rect mTmpTopLeftCorner = new Rect();
|
private final Rect mTmpTopLeftCorner = new Rect();
|
||||||
private final Rect mTmpTopRightCorner = new Rect();
|
private final Rect mTmpTopRightCorner = new Rect();
|
||||||
@@ -103,11 +103,7 @@ public class PipResizeGestureHandler {
|
|||||||
private boolean mIsEnabled;
|
private boolean mIsEnabled;
|
||||||
private boolean mEnablePinchResize;
|
private boolean mEnablePinchResize;
|
||||||
private boolean mIsSysUiStateValid;
|
private boolean mIsSysUiStateValid;
|
||||||
// For drag-resize
|
|
||||||
private boolean mThresholdCrossed;
|
private boolean mThresholdCrossed;
|
||||||
// For pinch-resize
|
|
||||||
private boolean mThresholdCrossed0;
|
|
||||||
private boolean mThresholdCrossed1;
|
|
||||||
private boolean mOngoingPinchToResize = false;
|
private boolean mOngoingPinchToResize = false;
|
||||||
private float mAngle = 0;
|
private float mAngle = 0;
|
||||||
int mFirstIndex = -1;
|
int mFirstIndex = -1;
|
||||||
@@ -135,6 +131,7 @@ public class PipResizeGestureHandler {
|
|||||||
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
|
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
|
||||||
mPhonePipMenuController = menuActivityController;
|
mPhonePipMenuController = menuActivityController;
|
||||||
mPipUiEventLogger = pipUiEventLogger;
|
mPipUiEventLogger = pipUiEventLogger;
|
||||||
|
mPinchResizingAlgorithm = new PipPinchResizingAlgorithm();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
@@ -294,6 +291,10 @@ public class PipResizeGestureHandler {
|
|||||||
return mEnablePinchResize;
|
return mEnablePinchResize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isResizing() {
|
||||||
|
return mAllowGesture;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean willStartResizeGesture(MotionEvent ev) {
|
public boolean willStartResizeGesture(MotionEvent ev) {
|
||||||
if (isInValidSysUiState()) {
|
if (isInValidSysUiState()) {
|
||||||
switch (ev.getActionMasked()) {
|
switch (ev.getActionMasked()) {
|
||||||
@@ -362,6 +363,7 @@ public class PipResizeGestureHandler {
|
|||||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||||
mFirstIndex = -1;
|
mFirstIndex = -1;
|
||||||
mSecondIndex = -1;
|
mSecondIndex = -1;
|
||||||
|
mAllowGesture = false;
|
||||||
finishResize();
|
finishResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,14 +373,16 @@ public class PipResizeGestureHandler {
|
|||||||
|
|
||||||
if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
||||||
if (mFirstIndex == -1 && mSecondIndex == -1) {
|
if (mFirstIndex == -1 && mSecondIndex == -1) {
|
||||||
|
mAllowGesture = true;
|
||||||
mFirstIndex = 0;
|
mFirstIndex = 0;
|
||||||
mSecondIndex = 1;
|
mSecondIndex = 1;
|
||||||
mDownPoint.set(ev.getRawX(mFirstIndex), ev.getRawY(mFirstIndex));
|
mDownPoint.set(ev.getRawX(mFirstIndex), ev.getRawY(mFirstIndex));
|
||||||
mDownSecondaryPoint.set(ev.getRawX(mSecondIndex), ev.getRawY(mSecondIndex));
|
mDownSecondPoint.set(ev.getRawX(mSecondIndex), ev.getRawY(mSecondIndex));
|
||||||
|
mDownBounds.set(mPipBoundsState.getBounds());
|
||||||
|
|
||||||
|
mLastPoint.set(mDownPoint);
|
||||||
mLastDownBounds.set(mPipBoundsState.getBounds());
|
mLastSecondPoint.set(mLastSecondPoint);
|
||||||
mLastResizeBounds.set(mLastDownBounds);
|
mLastResizeBounds.set(mDownBounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,133 +395,36 @@ public class PipResizeGestureHandler {
|
|||||||
float y0 = ev.getRawY(mFirstIndex);
|
float y0 = ev.getRawY(mFirstIndex);
|
||||||
float x1 = ev.getRawX(mSecondIndex);
|
float x1 = ev.getRawX(mSecondIndex);
|
||||||
float y1 = ev.getRawY(mSecondIndex);
|
float y1 = ev.getRawY(mSecondIndex);
|
||||||
|
mLastPoint.set(x0, y0);
|
||||||
|
mLastSecondPoint.set(x1, y1);
|
||||||
|
|
||||||
double hypot0 = Math.hypot(x0 - mDownPoint.x, y0 - mDownPoint.y);
|
|
||||||
double hypot1 = Math.hypot(x1 - mDownSecondaryPoint.x, y1 - mDownSecondaryPoint.y);
|
|
||||||
// Capture inputs
|
// Capture inputs
|
||||||
if (hypot0 > mTouchSlop && !mThresholdCrossed0) {
|
if (!mThresholdCrossed
|
||||||
|
&& (distanceBetween(mDownSecondPoint, mLastSecondPoint) > mTouchSlop
|
||||||
|
|| distanceBetween(mDownPoint, mLastPoint) > mTouchSlop)) {
|
||||||
mInputMonitor.pilferPointers();
|
mInputMonitor.pilferPointers();
|
||||||
mThresholdCrossed0 = true;
|
mThresholdCrossed = true;
|
||||||
// Reset the down to begin resizing from this point
|
// Reset the down to begin resizing from this point
|
||||||
mDownPoint.set(x0, y0);
|
mDownPoint.set(mLastPoint);
|
||||||
|
mDownSecondPoint.set(mLastSecondPoint);
|
||||||
}
|
}
|
||||||
if (hypot1 > mTouchSlop && !mThresholdCrossed1) {
|
|
||||||
mInputMonitor.pilferPointers();
|
if (mThresholdCrossed) {
|
||||||
mThresholdCrossed1 = true;
|
|
||||||
// Reset the down to begin resizing from this point
|
|
||||||
mDownSecondaryPoint.set(x1, y1);
|
|
||||||
}
|
|
||||||
if (mThresholdCrossed0 || mThresholdCrossed1) {
|
|
||||||
if (mPhonePipMenuController.isMenuVisible()) {
|
if (mPhonePipMenuController.isMenuVisible()) {
|
||||||
mPhonePipMenuController.hideMenu();
|
mPhonePipMenuController.hideMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
x0 = mThresholdCrossed0 ? x0 : mDownPoint.x;
|
mAngle = mPinchResizingAlgorithm.calculateBoundsAndAngle(mDownPoint,
|
||||||
y0 = mThresholdCrossed0 ? y0 : mDownPoint.y;
|
mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize,
|
||||||
x1 = mThresholdCrossed1 ? x1 : mDownSecondaryPoint.x;
|
mDownBounds, mLastResizeBounds);
|
||||||
y1 = mThresholdCrossed1 ? y1 : mDownSecondaryPoint.y;
|
|
||||||
|
|
||||||
final Rect originalPipBounds = mPipBoundsState.getBounds();
|
mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
|
||||||
int focusX = (int) originalPipBounds.centerX();
|
mAngle, null);
|
||||||
int focusY = (int) originalPipBounds.centerY();
|
|
||||||
|
|
||||||
float down0X = mDownPoint.x;
|
|
||||||
float down0Y = mDownPoint.y;
|
|
||||||
float down1X = mDownSecondaryPoint.x;
|
|
||||||
float down1Y = mDownSecondaryPoint.y;
|
|
||||||
|
|
||||||
float angle = 0;
|
|
||||||
if (down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY) {
|
|
||||||
// Top right + Bottom left pinch to zoom.
|
|
||||||
angle = calculateRotationAngle(mLastResizeBounds.centerX(),
|
|
||||||
mLastResizeBounds.centerY(), x0, y0, x1, y1, true);
|
|
||||||
} else if (down1X > focusX && down1Y < focusY
|
|
||||||
&& down0X < focusX && down0Y > focusY) {
|
|
||||||
// Top right + Bottom left pinch to zoom.
|
|
||||||
angle = calculateRotationAngle(mLastResizeBounds.centerX(),
|
|
||||||
mLastResizeBounds.centerY(), x1, y1, x0, y0, true);
|
|
||||||
} else if (down0X < focusX && down0Y < focusY
|
|
||||||
&& down1X > focusX && down1Y > focusY) {
|
|
||||||
// Top left + bottom right pinch to zoom.
|
|
||||||
angle = calculateRotationAngle(mLastResizeBounds.centerX(),
|
|
||||||
mLastResizeBounds.centerY(), x0, y0, x1, y1, false);
|
|
||||||
} else if (down1X < focusX && down1Y < focusY
|
|
||||||
&& down0X > focusX && down0Y > focusY) {
|
|
||||||
// Top left + bottom right pinch to zoom.
|
|
||||||
angle = calculateRotationAngle(mLastResizeBounds.centerX(),
|
|
||||||
mLastResizeBounds.centerY(), x1, y1, x0, y0, false);
|
|
||||||
}
|
|
||||||
mAngle = angle;
|
|
||||||
|
|
||||||
mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1,
|
|
||||||
mDownPoint.x, mDownPoint.y, mDownSecondaryPoint.x, mDownSecondaryPoint.y,
|
|
||||||
originalPipBounds, mMinSize.x, mMinSize.y, mMaxSize));
|
|
||||||
|
|
||||||
mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
|
|
||||||
(float) -mAngle, null);
|
|
||||||
mPipBoundsState.setHasUserResizedPip(true);
|
mPipBoundsState.setHasUserResizedPip(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float calculateRotationAngle(int pivotX, int pivotY, float topX, float topY,
|
|
||||||
float bottomX, float bottomY, 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 - pivotY),
|
|
||||||
Math.abs(mLastResizeBounds.right - pivotX)));
|
|
||||||
|
|
||||||
double angle0 = mThresholdCrossed0
|
|
||||||
? Math.toDegrees(Math.atan2(pivotY - topY, topX - pivotX)) : baseAngle;
|
|
||||||
double angle1 = mThresholdCrossed0
|
|
||||||
? Math.toDegrees(Math.atan2(pivotY - bottomY, bottomX - pivotX)) : baseAngle;
|
|
||||||
|
|
||||||
|
|
||||||
if (positive) {
|
|
||||||
angle1 = angle1 < 0 ? 180 + angle1 : angle1 - 180;
|
|
||||||
} else {
|
|
||||||
angle0 = angle0 < 0 ? -angle0 - 180 : 180 - angle0;
|
|
||||||
angle1 = -angle1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the percentage difference of [0, 90] compare to the base angle.
|
|
||||||
double diff0 = (Math.max(-90, Math.min(angle0, 90)) - baseAngle) / 90;
|
|
||||||
double diff1 = (Math.max(-90, Math.min(angle1, 90)) - baseAngle) / 90;
|
|
||||||
|
|
||||||
final float angle =
|
|
||||||
(float) (diff0 + diff1) / 2 * PINCH_RESIZE_MAX_ANGLE_ROTATION * (positive ? 1 : -1);
|
|
||||||
|
|
||||||
// Remove some degrees so that user doesn't immediately start rotating until a threshold
|
|
||||||
return angle / Math.abs(angle)
|
|
||||||
* Math.max(0, (Math.abs(dampedRotate(angle)) - ANGLE_THRESHOLD));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given the current rotation angle, dampen it so that as it approaches the maximum angle,
|
|
||||||
* dampen it.
|
|
||||||
*/
|
|
||||||
private float dampedRotate(float amount) {
|
|
||||||
if (Float.compare(amount, 0) == 0) return 0;
|
|
||||||
|
|
||||||
float f = amount / PINCH_RESIZE_MAX_ANGLE_ROTATION;
|
|
||||||
f = f / (Math.abs(f)) * (overRotateInfluenceCurve(Math.abs(f)));
|
|
||||||
|
|
||||||
// Clamp this factor, f, to -1 < f < 1
|
|
||||||
if (Math.abs(f) >= 1) {
|
|
||||||
f /= Math.abs(f);
|
|
||||||
}
|
|
||||||
return OVERROTATE_DAMP_FACTOR * f * PINCH_RESIZE_MAX_ANGLE_ROTATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a value that corresponds to y = (f - 1)^3 + 1.
|
|
||||||
*/
|
|
||||||
private float overRotateInfluenceCurve(float f) {
|
|
||||||
f -= 1.0f;
|
|
||||||
return f * f * f + 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onDragCornerResize(MotionEvent ev) {
|
private void onDragCornerResize(MotionEvent ev) {
|
||||||
int action = ev.getActionMasked();
|
int action = ev.getActionMasked();
|
||||||
float x = ev.getX();
|
float x = ev.getX();
|
||||||
@@ -529,7 +436,7 @@ public class PipResizeGestureHandler {
|
|||||||
if (mAllowGesture) {
|
if (mAllowGesture) {
|
||||||
setCtrlType((int) x, (int) y);
|
setCtrlType((int) x, (int) y);
|
||||||
mDownPoint.set(x, y);
|
mDownPoint.set(x, y);
|
||||||
mLastDownBounds.set(mPipBoundsState.getBounds());
|
mDownBounds.set(mPipBoundsState.getBounds());
|
||||||
}
|
}
|
||||||
if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY())
|
if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY())
|
||||||
&& mPhonePipMenuController.isMenuVisible()) {
|
&& mPhonePipMenuController.isMenuVisible()) {
|
||||||
@@ -560,11 +467,11 @@ public class PipResizeGestureHandler {
|
|||||||
mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
|
mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
|
||||||
mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
|
mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
|
||||||
mMinSize.y, mMaxSize, true,
|
mMinSize.y, mMaxSize, true,
|
||||||
mLastDownBounds.width() > mLastDownBounds.height()));
|
mDownBounds.width() > mDownBounds.height()));
|
||||||
mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds,
|
mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds,
|
||||||
mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
|
mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
|
||||||
true /* useCurrentSize */);
|
true /* useCurrentSize */);
|
||||||
mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
|
mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
|
||||||
null);
|
null);
|
||||||
mPipBoundsState.setHasUserResizedPip(true);
|
mPipBoundsState.setHasUserResizedPip(true);
|
||||||
}
|
}
|
||||||
@@ -593,12 +500,12 @@ public class PipResizeGestureHandler {
|
|||||||
// If user resize is pretty close to max size, just auto resize to max.
|
// If user resize is pretty close to max size, just auto resize to max.
|
||||||
if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
|
if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
|
||||||
|| mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
|
|| mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
|
||||||
mLastResizeBounds.set(0, 0, mMaxSize.x, mMaxSize.y);
|
resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y);
|
||||||
}
|
}
|
||||||
final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mLastResizeBounds);
|
final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mLastResizeBounds);
|
||||||
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
|
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
|
||||||
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
|
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
|
||||||
PINCH_RESIZE_SNAP_DURATION, -mAngle, callback);
|
PINCH_RESIZE_SNAP_DURATION, mAngle, callback);
|
||||||
} else {
|
} else {
|
||||||
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
|
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
|
||||||
PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, callback);
|
PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, callback);
|
||||||
@@ -642,6 +549,20 @@ public class PipResizeGestureHandler {
|
|||||||
mOhmOffset = offset;
|
mOhmOffset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float distanceBetween(PointF p1, PointF p2) {
|
||||||
|
return (float) Math.hypot(p2.x - p1.x, p2.y - p1.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resizeRectAboutCenter(Rect rect, int w, int h) {
|
||||||
|
int cx = rect.centerX();
|
||||||
|
int cy = rect.centerY();
|
||||||
|
int l = cx - w / 2;
|
||||||
|
int r = l + w;
|
||||||
|
int t = cy - h / 2;
|
||||||
|
int b = t + h;
|
||||||
|
rect.set(l, t, r, b);
|
||||||
|
}
|
||||||
|
|
||||||
public void dump(PrintWriter pw, String prefix) {
|
public void dump(PrintWriter pw, String prefix) {
|
||||||
final String innerPrefix = prefix + " ";
|
final String innerPrefix = prefix + " ";
|
||||||
pw.println(prefix + TAG);
|
pw.println(prefix + TAG);
|
||||||
|
|||||||
@@ -664,7 +664,7 @@ public class PipTouchHandler {
|
|||||||
} else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
|
} else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
|
||||||
// Try and restore the PiP to the closest edge, using the saved snap fraction
|
// Try and restore the PiP to the closest edge, using the saved snap fraction
|
||||||
// if possible
|
// if possible
|
||||||
if (resize) {
|
if (resize && !mPipResizeGestureHandler.isResizing()) {
|
||||||
if (mDeferResizeToNormalBoundsUntilRotation == -1) {
|
if (mDeferResizeToNormalBoundsUntilRotation == -1) {
|
||||||
// This is a very special case: when the menu is expanded and visible,
|
// This is a very special case: when the menu is expanded and visible,
|
||||||
// navigating to another activity can trigger auto-enter PiP, and if the
|
// navigating to another activity can trigger auto-enter PiP, and if the
|
||||||
|
|||||||
Reference in New Issue
Block a user