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,
|
||||
Rect sourceBounds, Rect destinationBounds, float degrees) {
|
||||
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);
|
||||
mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
|
||||
mTmpTransform.postRotate(degrees);
|
||||
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
|
||||
.setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top);
|
||||
mTmpTransform.postRotate(degrees,
|
||||
mTmpDestinationRectF.centerX(), mTmpDestinationRectF.centerY());
|
||||
tx.setMatrix(leash, mTmpTransform, mTmpFloat9);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,111 +16,115 @@
|
||||
package com.android.wm.shell.pip.phone;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
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();
|
||||
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
* Updates {@param resizeBoundsOut} with the new bounds of the PIP, and returns the angle in
|
||||
* degrees that the PIP should be rotated.
|
||||
*/
|
||||
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) {
|
||||
public float calculateBoundsAndAngle(PointF downPoint, PointF downSecondPoint,
|
||||
PointF lastPoint, PointF lastSecondPoint, Point minSize, 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();
|
||||
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));
|
||||
final int dx = (int) ((x0 - downx0 + x1 - downx1) / 2);
|
||||
final int dy = (int) ((y0 - downy0 + y1 - downy1) / 2);
|
||||
// Scale the bounds by the change in distance between the points
|
||||
resizeBoundsOut.set(initialBounds);
|
||||
scaleRectAboutCenter(resizeBoundsOut, scale);
|
||||
|
||||
width = Math.max(minVisibleWidth, Math.min(width + widthDelta, maxSize.x));
|
||||
height = Math.max(minVisibleHeight, Math.min(height + heightDelta, maxSize.y));
|
||||
// Translate by the centroid movement
|
||||
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
|
||||
// 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)));
|
||||
}
|
||||
// Calculate the angle
|
||||
mTmpDownVector.set(downSecondPoint.x - downPoint.x,
|
||||
downSecondPoint.y - downPoint.y);
|
||||
mTmpLastVector.set(lastSecondPoint.x - lastPoint.x,
|
||||
lastSecondPoint.y - lastPoint.y);
|
||||
float angle = (float) Math.atan2(cross(mTmpDownVector, mTmpLastVector),
|
||||
dot(mTmpDownVector, mTmpLastVector));
|
||||
return constrainRotationAngle((float) Math.toDegrees(angle));
|
||||
}
|
||||
|
||||
private float getMinScale(Rect bounds, Point minSize) {
|
||||
return Math.max((float) minSize.x / bounds.width(), (float) minSize.y / bounds.height());
|
||||
}
|
||||
|
||||
private float getMaxScale(Rect bounds, Point maxSize) {
|
||||
return Math.min((float) maxSize.x / bounds.width(), (float) maxSize.y / bounds.height());
|
||||
}
|
||||
|
||||
private float constrainRotationAngle(float angle) {
|
||||
// Remove some degrees so that user doesn't immediately start rotating until a threshold
|
||||
return Math.signum(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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
/**
|
||||
* 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 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 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 OVERROTATE_DAMP_FACTOR = 0.4f;
|
||||
private static final float ANGLE_THRESHOLD = 5f;
|
||||
|
||||
private final Context mContext;
|
||||
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
|
||||
@@ -75,17 +72,20 @@ public class PipResizeGestureHandler {
|
||||
private final PipTaskOrganizer mPipTaskOrganizer;
|
||||
private final PhonePipMenuController mPhonePipMenuController;
|
||||
private final PipUiEventLogger mPipUiEventLogger;
|
||||
private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
|
||||
private final int mDisplayId;
|
||||
private final ShellExecutor mMainExecutor;
|
||||
private final Region mTmpRegion = new Region();
|
||||
|
||||
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 mMinSize = new Point();
|
||||
private final Rect mLastResizeBounds = 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 mTmpTopLeftCorner = new Rect();
|
||||
private final Rect mTmpTopRightCorner = new Rect();
|
||||
@@ -103,11 +103,7 @@ 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 mOngoingPinchToResize = false;
|
||||
private float mAngle = 0;
|
||||
int mFirstIndex = -1;
|
||||
@@ -135,6 +131,7 @@ public class PipResizeGestureHandler {
|
||||
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
|
||||
mPhonePipMenuController = menuActivityController;
|
||||
mPipUiEventLogger = pipUiEventLogger;
|
||||
mPinchResizingAlgorithm = new PipPinchResizingAlgorithm();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
@@ -294,6 +291,10 @@ public class PipResizeGestureHandler {
|
||||
return mEnablePinchResize;
|
||||
}
|
||||
|
||||
public boolean isResizing() {
|
||||
return mAllowGesture;
|
||||
}
|
||||
|
||||
public boolean willStartResizeGesture(MotionEvent ev) {
|
||||
if (isInValidSysUiState()) {
|
||||
switch (ev.getActionMasked()) {
|
||||
@@ -362,6 +363,7 @@ public class PipResizeGestureHandler {
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||
mFirstIndex = -1;
|
||||
mSecondIndex = -1;
|
||||
mAllowGesture = false;
|
||||
finishResize();
|
||||
}
|
||||
|
||||
@@ -371,14 +373,16 @@ public class PipResizeGestureHandler {
|
||||
|
||||
if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
||||
if (mFirstIndex == -1 && mSecondIndex == -1) {
|
||||
mAllowGesture = true;
|
||||
mFirstIndex = 0;
|
||||
mSecondIndex = 1;
|
||||
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());
|
||||
|
||||
|
||||
mLastDownBounds.set(mPipBoundsState.getBounds());
|
||||
mLastResizeBounds.set(mLastDownBounds);
|
||||
mLastPoint.set(mDownPoint);
|
||||
mLastSecondPoint.set(mLastSecondPoint);
|
||||
mLastResizeBounds.set(mDownBounds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,133 +395,36 @@ public class PipResizeGestureHandler {
|
||||
float y0 = ev.getRawY(mFirstIndex);
|
||||
float x1 = ev.getRawX(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
|
||||
if (hypot0 > mTouchSlop && !mThresholdCrossed0) {
|
||||
if (!mThresholdCrossed
|
||||
&& (distanceBetween(mDownSecondPoint, mLastSecondPoint) > mTouchSlop
|
||||
|| distanceBetween(mDownPoint, mLastPoint) > mTouchSlop)) {
|
||||
mInputMonitor.pilferPointers();
|
||||
mThresholdCrossed0 = true;
|
||||
mThresholdCrossed = true;
|
||||
// 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();
|
||||
mThresholdCrossed1 = true;
|
||||
// Reset the down to begin resizing from this point
|
||||
mDownSecondaryPoint.set(x1, y1);
|
||||
}
|
||||
if (mThresholdCrossed0 || mThresholdCrossed1) {
|
||||
|
||||
if (mThresholdCrossed) {
|
||||
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;
|
||||
mAngle = mPinchResizingAlgorithm.calculateBoundsAndAngle(mDownPoint,
|
||||
mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize,
|
||||
mDownBounds, mLastResizeBounds);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
|
||||
mAngle, null);
|
||||
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) {
|
||||
int action = ev.getActionMasked();
|
||||
float x = ev.getX();
|
||||
@@ -529,7 +436,7 @@ public class PipResizeGestureHandler {
|
||||
if (mAllowGesture) {
|
||||
setCtrlType((int) x, (int) y);
|
||||
mDownPoint.set(x, y);
|
||||
mLastDownBounds.set(mPipBoundsState.getBounds());
|
||||
mDownBounds.set(mPipBoundsState.getBounds());
|
||||
}
|
||||
if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY())
|
||||
&& mPhonePipMenuController.isMenuVisible()) {
|
||||
@@ -560,11 +467,11 @@ public class PipResizeGestureHandler {
|
||||
mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
|
||||
mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
|
||||
mMinSize.y, mMaxSize, true,
|
||||
mLastDownBounds.width() > mLastDownBounds.height()));
|
||||
mDownBounds.width() > mDownBounds.height()));
|
||||
mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds,
|
||||
mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
|
||||
true /* useCurrentSize */);
|
||||
mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
|
||||
mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
|
||||
null);
|
||||
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 (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
|
||||
|| 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);
|
||||
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
|
||||
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
|
||||
PINCH_RESIZE_SNAP_DURATION, -mAngle, callback);
|
||||
PINCH_RESIZE_SNAP_DURATION, mAngle, callback);
|
||||
} else {
|
||||
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
|
||||
PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, callback);
|
||||
@@ -642,6 +549,20 @@ public class PipResizeGestureHandler {
|
||||
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) {
|
||||
final String innerPrefix = prefix + " ";
|
||||
pw.println(prefix + TAG);
|
||||
|
||||
@@ -664,7 +664,7 @@ public class PipTouchHandler {
|
||||
} else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
|
||||
// Try and restore the PiP to the closest edge, using the saved snap fraction
|
||||
// if possible
|
||||
if (resize) {
|
||||
if (resize && !mPipResizeGestureHandler.isResizing()) {
|
||||
if (mDeferResizeToNormalBoundsUntilRotation == -1) {
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user