Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +6 −3 Original line number Original line Diff line number Diff line Loading @@ -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; } } Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java +99 −95 Original line number Original line Diff line number Diff line Loading @@ -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, int width = currentPipBounds.width(); downSecondPoint.y - downPoint.y); int height = currentPipBounds.height(); float dist = (float) Math.hypot(lastSecondPoint.x - lastPoint.x, int left = currentPipBounds.left; lastSecondPoint.y - lastPoint.y); int top = currentPipBounds.top; float minScale = getMinScale(initialBounds, minSize); int right = currentPipBounds.right; float maxScale = getMaxScale(initialBounds, maxSize); int bottom = currentPipBounds.bottom; float scale = Math.max(minScale, Math.min(maxScale, dist / downDist)); final float aspect = (float) width / (float) height; final int widthDelta = Math.round(Math.abs(x0 - x1) - Math.abs(downx0 - downx1)); // Scale the bounds by the change in distance between the points final int heightDelta = Math.round(Math.abs(y0 - y1) - Math.abs(downy0 - downy1)); resizeBoundsOut.set(initialBounds); final int dx = (int) ((x0 - downx0 + x1 - downx1) / 2); scaleRectAboutCenter(resizeBoundsOut, scale); final int dy = (int) ((y0 - downy0 + y1 - downy1) / 2); // Translate by the centroid movement width = Math.max(minVisibleWidth, Math.min(width + widthDelta, maxSize.x)); getCentroid(downPoint, downSecondPoint, mTmpDownCentroid); height = Math.max(minVisibleHeight, Math.min(height + heightDelta, maxSize.y)); getCentroid(lastPoint, lastSecondPoint, mTmpLastCentroid); resizeBoundsOut.offset((int) (mTmpLastCentroid.x - mTmpDownCentroid.x), // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major (int) (mTmpLastCentroid.y - mTmpDownCentroid.y)); // drag axis. What ever is producing the bigger rectangle will be chosen. int width1; // Calculate the angle int width2; mTmpDownVector.set(downSecondPoint.x - downPoint.x, int height1; downSecondPoint.y - downPoint.y); int height2; mTmpLastVector.set(lastSecondPoint.x - lastPoint.x, if (aspect > 1.0f) { lastSecondPoint.y - lastPoint.y); // Assuming that the width is our target we calculate the height. float angle = (float) Math.atan2(cross(mTmpDownVector, mTmpLastVector), width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width)); dot(mTmpDownVector, mTmpLastVector)); height1 = Math.round((float) width1 / aspect); return constrainRotationAngle((float) Math.toDegrees(angle)); 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)); private float getMinScale(Rect bounds, Point minSize) { width2 = Math.round((float) height2 * aspect); return Math.max((float) minSize.x / bounds.width(), (float) minSize.y / bounds.height()); 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. private float getMaxScale(Rect bounds, Point maxSize) { width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width)); return Math.min((float) maxSize.x / bounds.width(), (float) maxSize.y / bounds.height()); 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)); private float constrainRotationAngle(float angle) { width2 = Math.round((float) height2 * aspect); // Remove some degrees so that user doesn't immediately start rotating until a threshold if (width2 < minVisibleWidth) { return Math.signum(angle) * Math.max(0, (Math.abs(dampedRotate(angle)) - ANGLE_THRESHOLD)); // 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))); } } /** * 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 getCentroid(PointF p1, PointF p2, PointF centroidOut) { centroidOut.set((p2.x + p1.x) / 2, (p2.y + p1.y) / 2); } } // Use the bigger of the two rectangles if the major change was positive, otherwise private float dot(PointF p1, PointF p2) { // do the opposite. return p1.x * p2.x + p1.y * p2.y; 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, private float cross(PointF p1, PointF p2) { currentPipBounds.centerY() - height / 2, return p1.x * p2.y - p1.y * p2.x; currentPipBounds.centerX() + width / 2, } currentPipBounds.centerY() + height / 2); TMP_RECT.offset(dx, dy); private void scaleRectAboutCenter(Rect r, float scale) { return TMP_RECT; if (scale != 1.0f) { int cx = r.centerX(); int cy = r.centerY(); r.offset(-cx, -cy); r.scale(scale); r.offset(cx, cy); } } } } } libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +51 −130 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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(); Loading @@ -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; Loading Loading @@ -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() { Loading Loading @@ -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()) { Loading Loading @@ -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(); } } Loading @@ -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); } } } } Loading @@ -391,131 +395,34 @@ 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 mInputMonitor.pilferPointers(); && (distanceBetween(mDownSecondPoint, mLastSecondPoint) > mTouchSlop mThresholdCrossed0 = true; || distanceBetween(mDownPoint, mLastPoint) > mTouchSlop)) { // Reset the down to begin resizing from this point mDownPoint.set(x0, y0); } if (hypot1 > mTouchSlop && !mThresholdCrossed1) { mInputMonitor.pilferPointers(); mInputMonitor.pilferPointers(); mThresholdCrossed1 = true; mThresholdCrossed = true; // Reset the down to begin resizing from this point // Reset the down to begin resizing from this point mDownSecondaryPoint.set(x1, y1); mDownPoint.set(mLastPoint); mDownSecondPoint.set(mLastSecondPoint); } } if (mThresholdCrossed0 || mThresholdCrossed1) { if (mThresholdCrossed) { 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(); 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); 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; mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds, f = f / (Math.abs(f)) * (overRotateInfluenceCurve(Math.abs(f))); mAngle, null); mPipBoundsState.setHasUserResizedPip(true); // 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) { Loading @@ -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()) { Loading Loading @@ -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); } } Loading Loading @@ -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); Loading Loading @@ -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); Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -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 Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +6 −3 Original line number Original line Diff line number Diff line Loading @@ -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; } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java +99 −95 Original line number Original line Diff line number Diff line Loading @@ -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, int width = currentPipBounds.width(); downSecondPoint.y - downPoint.y); int height = currentPipBounds.height(); float dist = (float) Math.hypot(lastSecondPoint.x - lastPoint.x, int left = currentPipBounds.left; lastSecondPoint.y - lastPoint.y); int top = currentPipBounds.top; float minScale = getMinScale(initialBounds, minSize); int right = currentPipBounds.right; float maxScale = getMaxScale(initialBounds, maxSize); int bottom = currentPipBounds.bottom; float scale = Math.max(minScale, Math.min(maxScale, dist / downDist)); final float aspect = (float) width / (float) height; final int widthDelta = Math.round(Math.abs(x0 - x1) - Math.abs(downx0 - downx1)); // Scale the bounds by the change in distance between the points final int heightDelta = Math.round(Math.abs(y0 - y1) - Math.abs(downy0 - downy1)); resizeBoundsOut.set(initialBounds); final int dx = (int) ((x0 - downx0 + x1 - downx1) / 2); scaleRectAboutCenter(resizeBoundsOut, scale); final int dy = (int) ((y0 - downy0 + y1 - downy1) / 2); // Translate by the centroid movement width = Math.max(minVisibleWidth, Math.min(width + widthDelta, maxSize.x)); getCentroid(downPoint, downSecondPoint, mTmpDownCentroid); height = Math.max(minVisibleHeight, Math.min(height + heightDelta, maxSize.y)); getCentroid(lastPoint, lastSecondPoint, mTmpLastCentroid); resizeBoundsOut.offset((int) (mTmpLastCentroid.x - mTmpDownCentroid.x), // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major (int) (mTmpLastCentroid.y - mTmpDownCentroid.y)); // drag axis. What ever is producing the bigger rectangle will be chosen. int width1; // Calculate the angle int width2; mTmpDownVector.set(downSecondPoint.x - downPoint.x, int height1; downSecondPoint.y - downPoint.y); int height2; mTmpLastVector.set(lastSecondPoint.x - lastPoint.x, if (aspect > 1.0f) { lastSecondPoint.y - lastPoint.y); // Assuming that the width is our target we calculate the height. float angle = (float) Math.atan2(cross(mTmpDownVector, mTmpLastVector), width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width)); dot(mTmpDownVector, mTmpLastVector)); height1 = Math.round((float) width1 / aspect); return constrainRotationAngle((float) Math.toDegrees(angle)); 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)); private float getMinScale(Rect bounds, Point minSize) { width2 = Math.round((float) height2 * aspect); return Math.max((float) minSize.x / bounds.width(), (float) minSize.y / bounds.height()); 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. private float getMaxScale(Rect bounds, Point maxSize) { width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width)); return Math.min((float) maxSize.x / bounds.width(), (float) maxSize.y / bounds.height()); 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)); private float constrainRotationAngle(float angle) { width2 = Math.round((float) height2 * aspect); // Remove some degrees so that user doesn't immediately start rotating until a threshold if (width2 < minVisibleWidth) { return Math.signum(angle) * Math.max(0, (Math.abs(dampedRotate(angle)) - ANGLE_THRESHOLD)); // 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))); } } /** * 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 getCentroid(PointF p1, PointF p2, PointF centroidOut) { centroidOut.set((p2.x + p1.x) / 2, (p2.y + p1.y) / 2); } } // Use the bigger of the two rectangles if the major change was positive, otherwise private float dot(PointF p1, PointF p2) { // do the opposite. return p1.x * p2.x + p1.y * p2.y; 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, private float cross(PointF p1, PointF p2) { currentPipBounds.centerY() - height / 2, return p1.x * p2.y - p1.y * p2.x; currentPipBounds.centerX() + width / 2, } currentPipBounds.centerY() + height / 2); TMP_RECT.offset(dx, dy); private void scaleRectAboutCenter(Rect r, float scale) { return TMP_RECT; if (scale != 1.0f) { int cx = r.centerX(); int cy = r.centerY(); r.offset(-cx, -cy); r.scale(scale); r.offset(cx, cy); } } } } }
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +51 −130 Original line number Original line Diff line number Diff line Loading @@ -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; Loading @@ -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(); Loading @@ -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; Loading Loading @@ -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() { Loading Loading @@ -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()) { Loading Loading @@ -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(); } } Loading @@ -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); } } } } Loading @@ -391,131 +395,34 @@ 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 mInputMonitor.pilferPointers(); && (distanceBetween(mDownSecondPoint, mLastSecondPoint) > mTouchSlop mThresholdCrossed0 = true; || distanceBetween(mDownPoint, mLastPoint) > mTouchSlop)) { // Reset the down to begin resizing from this point mDownPoint.set(x0, y0); } if (hypot1 > mTouchSlop && !mThresholdCrossed1) { mInputMonitor.pilferPointers(); mInputMonitor.pilferPointers(); mThresholdCrossed1 = true; mThresholdCrossed = true; // Reset the down to begin resizing from this point // Reset the down to begin resizing from this point mDownSecondaryPoint.set(x1, y1); mDownPoint.set(mLastPoint); mDownSecondPoint.set(mLastSecondPoint); } } if (mThresholdCrossed0 || mThresholdCrossed1) { if (mThresholdCrossed) { 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(); 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); 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; mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds, f = f / (Math.abs(f)) * (overRotateInfluenceCurve(Math.abs(f))); mAngle, null); mPipBoundsState.setHasUserResizedPip(true); // 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) { Loading @@ -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()) { Loading Loading @@ -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); } } Loading Loading @@ -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); Loading Loading @@ -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); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -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 Loading