PiP pinch-to-resize: add double-tap to max/min size.

Per spec, max size would be the biggest possible bounds within the
movement bounds (displayWidth - 2 * insetWidth), and min size would be
40% of the width.

This also fixes the issue of portrait PiP not resizing correctly due to
wrong math being done in the resize algorithm.

Bug: 166478885
Test: Double-tap on PiP
Change-Id: I2e239d23a0e2519c0b482005614a1faddb1f808a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index d0d9b26..9595b5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Size;
 import android.view.DisplayInfo;
@@ -59,6 +60,8 @@
     private final @NonNull Rect mExpandedBounds = new Rect();
     private final @NonNull Rect mNormalMovementBounds = new Rect();
     private final @NonNull Rect mExpandedMovementBounds = new Rect();
+    private final Point mMaxSize = new Point();
+    private final Point mMinSize = new Point();
     private final @NonNull Context mContext;
     private float mAspectRatio;
     private int mStashedState = STASH_TYPE_NONE;
@@ -151,6 +154,24 @@
         mExpandedMovementBounds.set(bounds);
     }
 
+    /** Sets the max possible size for resize. */
+    public void setMaxSize(int width, int height) {
+        mMaxSize.set(width, height);
+    }
+
+    /** Sets the min possible size for resize. */
+    public void setMinSize(int width, int height) {
+        mMinSize.set(width, height);
+    }
+
+    public Point getMaxSize() {
+        return mMaxSize;
+    }
+
+    public Point getMinSize() {
+        return mMinSize;
+    }
+
     /** Returns the expanded movement bounds. */
     @NonNull
     public Rect getExpandedMovementBounds() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
index 28cbe35..721f621 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
@@ -85,21 +85,21 @@
         } 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);
+            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)));
+                        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);
+            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)));
+                        Math.min(maxSize.y, Math.round((float) width2 / aspect)));
             }
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 6b6bf5e..6a26eab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -359,9 +359,13 @@
                 mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
                 bottomOffset);
 
-        mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
-        mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
-                mPipBoundsState.getExpandedBounds().height());
+        if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
+            updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
+        } else {
+            mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
+            mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
+                    mPipBoundsState.getExpandedBounds().height());
+        }
 
         // The extra offset does not really affect the movement bounds, but are applied based on the
         // current state (ime showing, or shelf offset) when we need to actually shift
@@ -430,6 +434,30 @@
         }
     }
 
+    private void updatePinchResizeSizeConstraints(Rect insetBounds, Rect normalBounds,
+            float aspectRatio) {
+        final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
+                mPipBoundsState.getDisplayBounds().height());
+        final int padding = insetBounds.left;
+        final int minWidth, minHeight, maxWidth, maxHeight;
+        if (aspectRatio > 1f) {
+            minWidth = (int) Math.min(normalBounds.width(), shorterLength * 0.4);
+            minHeight = (int) (minWidth / aspectRatio);
+            maxWidth = (int) Math.max(normalBounds.width(), shorterLength - 2 * padding);
+            maxHeight = (int) (maxWidth / aspectRatio);
+        } else {
+            minHeight = (int) Math.min(normalBounds.height(), shorterLength * 0.4);
+            minWidth = (int) (minHeight * aspectRatio);
+            maxHeight = (int) Math.max(normalBounds.height(), shorterLength - 2 * padding);
+            maxWidth = (int) (maxHeight * aspectRatio);
+        }
+
+        mPipResizeGestureHandler.updateMinSize(minWidth, minHeight);
+        mPipResizeGestureHandler.updateMaxSize(maxWidth, maxHeight);
+        mPipBoundsState.setMaxSize(maxWidth, maxHeight);
+        mPipBoundsState.setMinSize(minWidth, minHeight);
+    }
+
     /**
      * TODO Add appropriate description
      */
@@ -640,13 +668,36 @@
         }
     }
 
-    private void animateToExpandedState(Runnable callback) {
-        Rect expandedBounds = new Rect(mPipBoundsState.getExpandedBounds());
-        mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
-                mPipBoundsState.getMovementBounds(), mPipBoundsState.getExpandedMovementBounds(),
+    private void animateToMaximizedState(Runnable callback) {
+        Rect maxMovementBounds = new Rect();
+        Rect maxBounds = new Rect(0, 0, mPipBoundsState.getMaxSize().x,
+                mPipBoundsState.getMaxSize().y);
+        mPipBoundsAlgorithm.getMovementBounds(maxBounds, mInsetBounds, maxMovementBounds,
+                mIsImeShowing ? mImeHeight : 0);
+        mSavedSnapFraction = mMotionHelper.animateToExpandedState(maxBounds,
+                mPipBoundsState.getMovementBounds(), maxMovementBounds,
                 callback);
     }
 
+    private void animateToMinimizedState() {
+        animateToUnexpandedState(new Rect(0, 0, mPipBoundsState.getMinSize().x,
+                mPipBoundsState.getMinSize().y));
+    }
+
+    private void animateToExpandedState(Runnable callback) {
+        mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
+        final Rect currentBounds = mPipBoundsState.getBounds();
+        final Rect expandedBounds = mPipBoundsState.getExpandedBounds();
+        Rect finalExpandedBounds = new Rect(expandedBounds.width() > expandedBounds.width()
+                        && expandedBounds.height() > expandedBounds.height()
+                        ? currentBounds : expandedBounds);
+        Rect restoredMovementBounds = new Rect();
+        mPipBoundsAlgorithm.getMovementBounds(finalExpandedBounds,
+                mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
+        mSavedSnapFraction = mMotionHelper.animateToExpandedState(finalExpandedBounds,
+                mPipBoundsState.getMovementBounds(), restoredMovementBounds, callback);
+    }
+
     private void animateToUnexpandedState(Rect restoreBounds) {
         Rect restoredMovementBounds = new Rect();
         mPipBoundsAlgorithm.getMovementBounds(restoreBounds,
@@ -789,17 +840,15 @@
                 // If using pinch to zoom, double-tap functions as resizing between max/min size
                 if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
                     final boolean toExpand = mPipBoundsState.getBounds().width()
-                            < mPipBoundsState.getExpandedBounds().width()
+                            < mPipBoundsState.getMaxSize().x
                             && mPipBoundsState.getBounds().height()
-                            < mPipBoundsState.getExpandedBounds().height();
-                    mPipResizeGestureHandler.setUserResizeBounds(toExpand
-                            ? mPipBoundsState.getExpandedBounds()
-                            : mPipBoundsState.getNormalBounds());
+                            < mPipBoundsState.getMaxSize().y;
                     if (toExpand) {
-                        animateToExpandedState(null);
+                        animateToMaximizedState(null);
                     } else {
-                        animateToUnexpandedState(mPipBoundsState.getNormalBounds());
+                        animateToMinimizedState();
                     }
+                    mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds());
                 } else {
                     // Expand to fullscreen if this is a double tap
                     // the PiP should be frozen until the transition ends