Merge "Maintain aspect ratio in DragPositioningCallbackUtility.changeBounds()" into main
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index cb9781e..cad3462 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -84,22 +84,47 @@
 
         repositionTaskBounds.set(taskBoundsAtDragStart);
 
+        boolean isAspectRatioMaintained = true;
         // Make sure the new resizing destination in any direction falls within the stable bounds.
         if ((ctrlType & CTRL_TYPE_LEFT) != 0) {
             repositionTaskBounds.left = Math.max(repositionTaskBounds.left + (int) delta.x,
                     stableBounds.left);
+            if (repositionTaskBounds.left == stableBounds.left
+                    && repositionTaskBounds.left + (int) delta.x != stableBounds.left) {
+                // If the task edge have been set to the stable bounds and not due to the users
+                // drag, the aspect ratio of the task will not be maintained.
+                isAspectRatioMaintained = false;
+            }
         }
         if ((ctrlType & CTRL_TYPE_RIGHT) != 0) {
             repositionTaskBounds.right = Math.min(repositionTaskBounds.right + (int) delta.x,
                     stableBounds.right);
+            if (repositionTaskBounds.right == stableBounds.right
+                    && repositionTaskBounds.right + (int) delta.x != stableBounds.right) {
+                // If the task edge have been set to the stable bounds and not due to the users
+                // drag, the aspect ratio of the task will not be maintained.
+                isAspectRatioMaintained = false;
+            }
         }
         if ((ctrlType & CTRL_TYPE_TOP) != 0) {
             repositionTaskBounds.top = Math.max(repositionTaskBounds.top + (int) delta.y,
                     stableBounds.top);
+            if (repositionTaskBounds.top == stableBounds.top
+                    && repositionTaskBounds.top + (int) delta.y != stableBounds.top) {
+                // If the task edge have been set to the stable bounds and not due to the users
+                // drag, the aspect ratio of the task will not be maintained.
+                isAspectRatioMaintained = false;
+            }
         }
         if ((ctrlType & CTRL_TYPE_BOTTOM) != 0) {
             repositionTaskBounds.bottom = Math.min(repositionTaskBounds.bottom + (int) delta.y,
                     stableBounds.bottom);
+            if (repositionTaskBounds.bottom == stableBounds.bottom
+                    && repositionTaskBounds.bottom + (int) delta.y != stableBounds.bottom) {
+                // If the task edge have been set to the stable bounds and not due to the users
+                // drag, the aspect ratio of the task will not be maintained.
+                isAspectRatioMaintained = false;
+            }
         }
 
         // If width or height are negative or exceeding the width or height constraints, revert the
@@ -108,11 +133,24 @@
                 windowDecoration)) {
             repositionTaskBounds.right = oldRight;
             repositionTaskBounds.left = oldLeft;
+            isAspectRatioMaintained = false;
         }
         if (isExceedingHeightConstraint(repositionTaskBounds, stableBounds, displayController,
                 windowDecoration)) {
             repositionTaskBounds.top = oldTop;
             repositionTaskBounds.bottom = oldBottom;
+            isAspectRatioMaintained = false;
+        }
+
+        // If the application is unresizeable and any bounds have been set back to their old
+        // location or to a stable bound edge, reset all the bounds to maintain the applications
+        // aspect ratio.
+        if (DesktopModeFlags.SCALED_RESIZING.isEnabled(windowDecoration.mDecorWindowContext)
+                && !isAspectRatioMaintained && !windowDecoration.mTaskInfo.isResizeable) {
+            repositionTaskBounds.top = oldTop;
+            repositionTaskBounds.bottom = oldBottom;
+            repositionTaskBounds.right = oldRight;
+            repositionTaskBounds.left = oldLeft;
         }
 
         // If there are no changes to the bounds after checking new bounds against minimum and
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index 1f33ae6..24f6bec 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -39,6 +39,7 @@
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
 import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import org.junit.After
 import org.junit.Before
@@ -105,6 +106,7 @@
         initializeTaskInfo()
         mockWindowDecoration.mDisplay = mockDisplay
         mockWindowDecoration.mDecorWindowContext = mockContext
+        mockWindowDecoration.mTaskInfo.isResizeable = true
         whenever(mockContext.getResources()).thenReturn(mockResources)
         whenever(mockWindowDecoration.mDecorWindowContext.resources).thenReturn(mockResources)
         whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_width))
@@ -164,6 +166,60 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING)
+    fun testChangeBounds_unresizeableApp_heightLessThanMin_resetToStartingBounds() {
+        mockWindowDecoration.mTaskInfo.isResizeable = false
+        val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
+        val repositionTaskBounds = Rect(STARTING_BOUNDS)
+
+        // Resize to width of 95px and height of 5px with min width of 10px
+        val newX = STARTING_BOUNDS.right.toFloat() - 5
+        val newY = STARTING_BOUNDS.top.toFloat() + 95
+        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+        assertFalse(
+            DragPositioningCallbackUtility.changeBounds(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+                mockWindowDecoration
+            )
+        )
+
+        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
+        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING)
+    fun testChangeBounds_unresizeableApp_widthLessThanMin_resetToStartingBounds() {
+        mockWindowDecoration.mTaskInfo.isResizeable = false
+        val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
+        val repositionTaskBounds = Rect(STARTING_BOUNDS)
+
+        // Resize to height of 95px and width of 5px with min width of 10px
+        val newX = STARTING_BOUNDS.right.toFloat() - 95
+        val newY = STARTING_BOUNDS.top.toFloat() + 5
+        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+        assertFalse(
+            DragPositioningCallbackUtility.changeBounds(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+                mockWindowDecoration
+            )
+        )
+
+
+        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
+        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
+    }
+
+
+    @Test
     fun testChangeBoundsDoesNotChangeHeightWhenNegative() {
         val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
         val repositionTaskBounds = Rect(STARTING_BOUNDS)
@@ -317,6 +373,34 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING)
+    fun testChangeBounds_unresizeableApp_beyondStableBounds_resetToStartingBounds() {
+        mockWindowDecoration.mTaskInfo.isResizeable = false
+        val startingPoint = PointF(
+            STARTING_BOUNDS.right.toFloat(),
+            STARTING_BOUNDS.bottom.toFloat()
+        )
+        val repositionTaskBounds = Rect(STARTING_BOUNDS)
+
+        // Resize to beyond stable bounds.
+        val newX = STARTING_BOUNDS.right.toFloat() + STABLE_BOUNDS.width()
+        val newY = STARTING_BOUNDS.bottom.toFloat() + STABLE_BOUNDS.height()
+
+        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+        assertFalse(
+            DragPositioningCallbackUtility.changeBounds(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+                mockWindowDecoration
+            )
+        )
+        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
+        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
     fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeLessThanMin_shouldNotChangeBounds() {
         doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(mockContext) }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index 3a3e965..7543fed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -121,6 +121,7 @@
             displayId = DISPLAY_ID
             configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
             configuration.windowConfiguration.displayRotation = ROTATION_90
+            isResizeable = true
         }
         `when`(mockWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA)
         mockWindowDecoration.mDisplay = mockDisplay
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 6ae16ed..7784af6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -141,6 +141,7 @@
             displayId = DISPLAY_ID
             configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
             configuration.windowConfiguration.displayRotation = ROTATION_90
+            isResizeable = true
         }
         `when`(mockDesktopWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA)
         mockDesktopWindowDecoration.mDisplay = mockDisplay