Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 59415ea4 authored by Winson Chung's avatar Winson Chung
Browse files

Fix some issues with pinch to resize

- Tweak calculation of resize bounds, calculate the scale from the change
  in distance between the pointers, and scale about the centroid of the
  pip
- Fix issue with rotation not being about the center of the pip
- Fix issue with jumping rotation on start of gesture
- Fix issue with animating to top when max bounds are used
- Fix issue with resizing while the menu is open not actually resizing
  for a brief period (the menu fading was triggering another pip
  animation)

http://recall/-/g6hkB0pXjyQSvfA8STqRAT/cW6W0tdqDzisBndgU2JnM7

Bug: 178881304
Test: Pinch to resize in the cases above
Change-Id: I3bdf535cda64b9ce1926c7081f6cc40d4d79ab69
parent 69dd9831
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -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;
    }

+99 −95
Original line number Diff line number Diff line
@@ -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) {

        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);

        width = Math.max(minVisibleWidth, Math.min(width + widthDelta, maxSize.x));
        height = Math.max(minVisibleHeight, Math.min(height + heightDelta, maxSize.y));

        // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
        // drag axis. What ever is producing the bigger rectangle will be chosen.
        int width1;
        int width2;
        int height1;
        int height2;
        if (aspect > 1.0f) {
            // Assuming that the width is our target we calculate the height.
            width1 = Math.max(minVisibleWidth, Math.min(maxSize.x, width));
            height1 = Math.round((float) width1 / aspect);
            if (height1 < minVisibleHeight) {
                // If the resulting height is too small we adjust to the minimal size.
                height1 = minVisibleHeight;
                width1 = Math.max(minVisibleWidth,
                        Math.min(maxSize.x, Math.round((float) height1 * aspect)));
    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));

        // Scale the bounds by the change in distance between the points
        resizeBoundsOut.set(initialBounds);
        scaleRectAboutCenter(resizeBoundsOut, scale);

        // 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 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));
    }
            // 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)));

    private float getMinScale(Rect bounds, Point minSize) {
        return Math.max((float) minSize.x / bounds.width(), (float) minSize.y / bounds.height());
    }
        } 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)));

    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);
            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)));

    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;
    }

    /**
     * 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
        // 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;
    private float dot(PointF p1, PointF p2) {
        return p1.x * p2.x + p1.y * p2.y;
    }

        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;
    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);
        }
    }
}
+51 −130
Original line number Diff line number Diff line
@@ -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,131 +395,34 @@ 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) {
                mInputMonitor.pilferPointers();
                mThresholdCrossed0 = true;
                // Reset the down to begin resizing from this point
                mDownPoint.set(x0, y0);
            }
            if (hypot1 > mTouchSlop && !mThresholdCrossed1) {
            if (!mThresholdCrossed
                    && (distanceBetween(mDownSecondPoint, mLastSecondPoint) > mTouchSlop
                            || distanceBetween(mDownPoint, mLastPoint) > mTouchSlop)) {
                mInputMonitor.pilferPointers();
                mThresholdCrossed1 = true;
                mThresholdCrossed = true;
                // 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()) {
                    mPhonePipMenuController.hideMenu();
                }

                x0 = mThresholdCrossed0 ? x0 : mDownPoint.x;
                y0 = mThresholdCrossed0 ? y0 : mDownPoint.y;
                x1 = mThresholdCrossed1 ? x1 : mDownSecondaryPoint.x;
                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;
                mAngle = mPinchResizingAlgorithm.calculateBoundsAndAngle(mDownPoint,
                        mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize,
                        mDownBounds, mLastResizeBounds);

        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);
                mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
                        mAngle, null);
                mPipBoundsState.setHasUserResizedPip(true);
            }
        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) {
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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