Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java +18 −12 Original line number Original line Diff line number Diff line Loading @@ -29,8 +29,6 @@ import android.util.DisplayMetrics; import android.view.SurfaceControl; import android.view.SurfaceControl; import android.window.DesktopModeFlags; import android.window.DesktopModeFlags; import androidx.annotation.NonNull; import com.android.wm.shell.R; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; Loading Loading @@ -129,13 +127,15 @@ public class DragPositioningCallbackUtility { // If width or height are negative or exceeding the width or height constraints, revert the // If width or height are negative or exceeding the width or height constraints, revert the // respective bounds to use previous bound dimensions. // respective bounds to use previous bound dimensions. if (isExceedingWidthConstraint(repositionTaskBounds, stableBounds, displayController, if (isExceedingWidthConstraint(repositionTaskBounds.width(), /* startingWidth= */ oldRight - oldLeft, stableBounds, displayController, windowDecoration)) { windowDecoration)) { repositionTaskBounds.right = oldRight; repositionTaskBounds.right = oldRight; repositionTaskBounds.left = oldLeft; repositionTaskBounds.left = oldLeft; isAspectRatioMaintained = false; isAspectRatioMaintained = false; } } if (isExceedingHeightConstraint(repositionTaskBounds, stableBounds, displayController, if (isExceedingHeightConstraint(repositionTaskBounds.height(), /* startingHeight= */oldBottom - oldTop, stableBounds, displayController, windowDecoration)) { windowDecoration)) { repositionTaskBounds.top = oldTop; repositionTaskBounds.top = oldTop; repositionTaskBounds.bottom = oldBottom; repositionTaskBounds.bottom = oldBottom; Loading Loading @@ -208,28 +208,34 @@ public class DragPositioningCallbackUtility { return result; return result; } } private static boolean isExceedingWidthConstraint(@NonNull Rect repositionTaskBounds, private static boolean isExceedingWidthConstraint(int repositionedWidth, int startingWidth, Rect maxResizeBounds, DisplayController displayController, Rect maxResizeBounds, DisplayController displayController, WindowDecoration windowDecoration) { WindowDecoration windowDecoration) { boolean isSizeIncreasing = (repositionedWidth - startingWidth) > 0; // Check if width is less than the minimum width constraint. // Check if width is less than the minimum width constraint. if (repositionTaskBounds.width() < getMinWidth(displayController, windowDecoration)) { if (repositionedWidth < getMinWidth(displayController, windowDecoration)) { return true; // Only allow width to be increased if it is already below minimum. return !isSizeIncreasing; } } // Check if width is more than the maximum resize bounds on desktop windowing mode. // Check if width is more than the maximum resize bounds on desktop windowing mode. // Only allow width to be decreased if it already exceeds maximum. return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) && repositionTaskBounds.width() > maxResizeBounds.width(); && repositionedWidth > maxResizeBounds.width() && isSizeIncreasing; } } private static boolean isExceedingHeightConstraint(@NonNull Rect repositionTaskBounds, private static boolean isExceedingHeightConstraint(int repositionedHeight, int startingHeight, Rect maxResizeBounds, DisplayController displayController, Rect maxResizeBounds, DisplayController displayController, WindowDecoration windowDecoration) { WindowDecoration windowDecoration) { boolean isSizeIncreasing = (repositionedHeight - startingHeight) > 0; // Check if height is less than the minimum height constraint. // Check if height is less than the minimum height constraint. if (repositionTaskBounds.height() < getMinHeight(displayController, windowDecoration)) { if (repositionedHeight < getMinHeight(displayController, windowDecoration)) { return true; // Only allow height to be increased if it is already below minimum. return !isSizeIncreasing; } } // Check if height is more than the maximum resize bounds on desktop windowing mode. // Check if height is more than the maximum resize bounds on desktop windowing mode. // Only allow height to be decreased if it already exceeds maximum. return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) && repositionTaskBounds.height() > maxResizeBounds.height(); && repositionedHeight > maxResizeBounds.height() && isSizeIncreasing; } } private static float getMinWidth(DisplayController displayController, private static float getMinWidth(DisplayController displayController, Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt +227 −2 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat Loading @@ -48,9 +49,9 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.any import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations import org.mockito.quality.Strictness import org.mockito.quality.Strictness import org.mockito.Mockito.`when` as whenever /** /** * Tests for [DragPositioningCallbackUtility]. * Tests for [DragPositioningCallbackUtility]. Loading Loading @@ -191,6 +192,62 @@ class DragPositioningCallbackUtilityTest { assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) } } @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_initialHeightLessThanMin_increasingBounds_resizeAllowed() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(BELOW_MIN_HEIGHT_BOUNDS.right.toFloat(), BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat()) val repositionTaskBounds = Rect(BELOW_MIN_HEIGHT_BOUNDS) // Resize to increased bounds val newX = BELOW_MIN_HEIGHT_BOUNDS.right.toFloat() + 20 val newY = BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat() + 10 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is in direction of desired range assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, repositionTaskBounds, BELOW_MIN_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.right + 20) assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.bottom + 10) } @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_initialHeightMoreThanMax_decreasingBounds_resizeAllowed() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat(), EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat()) val repositionTaskBounds = Rect(EXCEEDS_MAX_HEIGHT_BOUNDS) // Resize to decreased bounds. val newX = EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat() - 10 val newY = EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat() + 20 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is in direction of desired range. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, repositionTaskBounds, EXCEEDS_MAX_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.top + 20) assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.right - 10) assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.bottom) } @Test @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_widthLessThanMin_resetToStartingBounds() { fun testChangeBounds_unresizeableApp_widthLessThanMin_resetToStartingBounds() { Loading @@ -211,13 +268,68 @@ class DragPositioningCallbackUtilityTest { ) ) ) ) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) } } @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_initialWidthLessThanMin_increasingBounds_resizeAllowed() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(BELOW_MIN_WIDTH_BOUNDS.right.toFloat(), BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat()) val repositionTaskBounds = Rect(BELOW_MIN_WIDTH_BOUNDS) // Resize to increased bounds. val newX = BELOW_MIN_WIDTH_BOUNDS.right.toFloat() + 10 val newY = BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat() + 20 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is in direction of desired range. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, repositionTaskBounds, BELOW_MIN_WIDTH_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.right + 10) assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.bottom + 20) } @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_initialWidthMoreThanMax_decreasingBounds_resizeAllowed() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat(), EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat()) val repositionTaskBounds = Rect(EXCEEDS_MAX_WIDTH_BOUNDS) // Resize to decreased bounds. val newX = EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat() + 20 val newY = EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat() + 10 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is in direction of desired range. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_LEFT or CTRL_TYPE_TOP, repositionTaskBounds, EXCEEDS_MAX_WIDTH_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.left + 20) assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.top + 10) assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.right) assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.bottom) } @Test @Test fun testChangeBoundsDoesNotChangeHeightWhenNegative() { fun testChangeBoundsDoesNotChangeHeightWhenNegative() { Loading Loading @@ -425,6 +537,60 @@ class DragPositioningCallbackUtilityTest { assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) } } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun testMinHeight_initialHeightLessThanMin_increasingHeight_resizeAllowed() { val startingPoint = PointF(BELOW_MIN_HEIGHT_BOUNDS.right.toFloat(), BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat()) val repositionTaskBounds = Rect(BELOW_MIN_HEIGHT_BOUNDS) // Attempt to increase height. val newX = BELOW_MIN_HEIGHT_BOUNDS.right.toFloat() val newY = BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat() + 10 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is increasing height closer to valid region. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, repositionTaskBounds, BELOW_MIN_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.right) assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.bottom + 10) } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun testMinWidth_initialWidthLessThanMin_increasingBounds_resizeAllowed() { val startingPoint = PointF(BELOW_MIN_WIDTH_BOUNDS.right.toFloat(), BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat()) val repositionTaskBounds = Rect(BELOW_MIN_WIDTH_BOUNDS) // Attempt to increase width. val newX = BELOW_MIN_WIDTH_BOUNDS.right.toFloat() + 10 val newY = BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat() val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is increasing width closer to valid region. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, repositionTaskBounds, BELOW_MIN_WIDTH_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.right + 10) assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.bottom) } @Test @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() { fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() { Loading Loading @@ -547,6 +713,61 @@ class DragPositioningCallbackUtilityTest { assertThat(repositionTaskBounds.height()).isLessThan(STABLE_BOUNDS.bottom) assertThat(repositionTaskBounds.height()).isLessThan(STABLE_BOUNDS.bottom) } } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun testMaxHeight_initialHeightMoreThanMax_decreasingHeight_resizeAllowed() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat(), EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat()) val repositionTaskBounds = Rect(EXCEEDS_MAX_HEIGHT_BOUNDS) // Attempt to decrease height val newX = EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat() - 10 val newY = EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat() val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is decreasing height closer to valid region. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, repositionTaskBounds, EXCEEDS_MAX_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.right - 10) assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.bottom ) } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun testMaxHeight_initialWidthMoreThanMax_decreasingBounds_resizeAllowed() { val startingPoint = PointF(EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat(), EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat()) val repositionTaskBounds = Rect(EXCEEDS_MAX_WIDTH_BOUNDS) // Attempt to decrease width. val newX = EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat() + 20 val newY = EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat() val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is decreasing width closer to valid region. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_LEFT or CTRL_TYPE_TOP, repositionTaskBounds, EXCEEDS_MAX_WIDTH_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.left + 20) assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.right) assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.bottom) } private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) { private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) { mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = TASK_ID taskId = TASK_ID Loading @@ -571,6 +792,10 @@ class DragPositioningCallbackUtilityTest { private const val NAVBAR_HEIGHT = 50 private const val NAVBAR_HEIGHT = 50 private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600) private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600) private val STARTING_BOUNDS = Rect(0, 0, 100, 100) private val STARTING_BOUNDS = Rect(0, 0, 100, 100) private val BELOW_MIN_WIDTH_BOUNDS = Rect(0, 0, 50, 100) private val BELOW_MIN_HEIGHT_BOUNDS = Rect(0, 0, 100, 50) private val EXCEEDS_MAX_WIDTH_BOUNDS = Rect(0, 0, 3000, 1500) private val EXCEEDS_MAX_HEIGHT_BOUNDS = Rect(0, 0, 1000, 2000) private val OFF_CENTER_STARTING_BOUNDS = Rect(-100, -100, 10, 10) private val OFF_CENTER_STARTING_BOUNDS = Rect(-100, -100, 10, 10) private val DISALLOWED_RESIZE_AREA = Rect( private val DISALLOWED_RESIZE_AREA = Rect( DISPLAY_BOUNDS.left, DISPLAY_BOUNDS.left, Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java +18 −12 Original line number Original line Diff line number Diff line Loading @@ -29,8 +29,6 @@ import android.util.DisplayMetrics; import android.view.SurfaceControl; import android.view.SurfaceControl; import android.window.DesktopModeFlags; import android.window.DesktopModeFlags; import androidx.annotation.NonNull; import com.android.wm.shell.R; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; Loading Loading @@ -129,13 +127,15 @@ public class DragPositioningCallbackUtility { // If width or height are negative or exceeding the width or height constraints, revert the // If width or height are negative or exceeding the width or height constraints, revert the // respective bounds to use previous bound dimensions. // respective bounds to use previous bound dimensions. if (isExceedingWidthConstraint(repositionTaskBounds, stableBounds, displayController, if (isExceedingWidthConstraint(repositionTaskBounds.width(), /* startingWidth= */ oldRight - oldLeft, stableBounds, displayController, windowDecoration)) { windowDecoration)) { repositionTaskBounds.right = oldRight; repositionTaskBounds.right = oldRight; repositionTaskBounds.left = oldLeft; repositionTaskBounds.left = oldLeft; isAspectRatioMaintained = false; isAspectRatioMaintained = false; } } if (isExceedingHeightConstraint(repositionTaskBounds, stableBounds, displayController, if (isExceedingHeightConstraint(repositionTaskBounds.height(), /* startingHeight= */oldBottom - oldTop, stableBounds, displayController, windowDecoration)) { windowDecoration)) { repositionTaskBounds.top = oldTop; repositionTaskBounds.top = oldTop; repositionTaskBounds.bottom = oldBottom; repositionTaskBounds.bottom = oldBottom; Loading Loading @@ -208,28 +208,34 @@ public class DragPositioningCallbackUtility { return result; return result; } } private static boolean isExceedingWidthConstraint(@NonNull Rect repositionTaskBounds, private static boolean isExceedingWidthConstraint(int repositionedWidth, int startingWidth, Rect maxResizeBounds, DisplayController displayController, Rect maxResizeBounds, DisplayController displayController, WindowDecoration windowDecoration) { WindowDecoration windowDecoration) { boolean isSizeIncreasing = (repositionedWidth - startingWidth) > 0; // Check if width is less than the minimum width constraint. // Check if width is less than the minimum width constraint. if (repositionTaskBounds.width() < getMinWidth(displayController, windowDecoration)) { if (repositionedWidth < getMinWidth(displayController, windowDecoration)) { return true; // Only allow width to be increased if it is already below minimum. return !isSizeIncreasing; } } // Check if width is more than the maximum resize bounds on desktop windowing mode. // Check if width is more than the maximum resize bounds on desktop windowing mode. // Only allow width to be decreased if it already exceeds maximum. return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) && repositionTaskBounds.width() > maxResizeBounds.width(); && repositionedWidth > maxResizeBounds.width() && isSizeIncreasing; } } private static boolean isExceedingHeightConstraint(@NonNull Rect repositionTaskBounds, private static boolean isExceedingHeightConstraint(int repositionedHeight, int startingHeight, Rect maxResizeBounds, DisplayController displayController, Rect maxResizeBounds, DisplayController displayController, WindowDecoration windowDecoration) { WindowDecoration windowDecoration) { boolean isSizeIncreasing = (repositionedHeight - startingHeight) > 0; // Check if height is less than the minimum height constraint. // Check if height is less than the minimum height constraint. if (repositionTaskBounds.height() < getMinHeight(displayController, windowDecoration)) { if (repositionedHeight < getMinHeight(displayController, windowDecoration)) { return true; // Only allow height to be increased if it is already below minimum. return !isSizeIncreasing; } } // Check if height is more than the maximum resize bounds on desktop windowing mode. // Check if height is more than the maximum resize bounds on desktop windowing mode. // Only allow height to be decreased if it already exceeds maximum. return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) return isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext) && repositionTaskBounds.height() > maxResizeBounds.height(); && repositionedHeight > maxResizeBounds.height() && isSizeIncreasing; } } private static float getMinWidth(DisplayController displayController, private static float getMinWidth(DisplayController displayController, Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt +227 −2 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat Loading @@ -48,9 +49,9 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.any import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations import org.mockito.quality.Strictness import org.mockito.quality.Strictness import org.mockito.Mockito.`when` as whenever /** /** * Tests for [DragPositioningCallbackUtility]. * Tests for [DragPositioningCallbackUtility]. Loading Loading @@ -191,6 +192,62 @@ class DragPositioningCallbackUtilityTest { assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) } } @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_initialHeightLessThanMin_increasingBounds_resizeAllowed() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(BELOW_MIN_HEIGHT_BOUNDS.right.toFloat(), BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat()) val repositionTaskBounds = Rect(BELOW_MIN_HEIGHT_BOUNDS) // Resize to increased bounds val newX = BELOW_MIN_HEIGHT_BOUNDS.right.toFloat() + 20 val newY = BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat() + 10 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is in direction of desired range assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, repositionTaskBounds, BELOW_MIN_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.right + 20) assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.bottom + 10) } @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_initialHeightMoreThanMax_decreasingBounds_resizeAllowed() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat(), EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat()) val repositionTaskBounds = Rect(EXCEEDS_MAX_HEIGHT_BOUNDS) // Resize to decreased bounds. val newX = EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat() - 10 val newY = EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat() + 20 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is in direction of desired range. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, repositionTaskBounds, EXCEEDS_MAX_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.top + 20) assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.right - 10) assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.bottom) } @Test @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_widthLessThanMin_resetToStartingBounds() { fun testChangeBounds_unresizeableApp_widthLessThanMin_resetToStartingBounds() { Loading @@ -211,13 +268,68 @@ class DragPositioningCallbackUtilityTest { ) ) ) ) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) } } @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_initialWidthLessThanMin_increasingBounds_resizeAllowed() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(BELOW_MIN_WIDTH_BOUNDS.right.toFloat(), BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat()) val repositionTaskBounds = Rect(BELOW_MIN_WIDTH_BOUNDS) // Resize to increased bounds. val newX = BELOW_MIN_WIDTH_BOUNDS.right.toFloat() + 10 val newY = BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat() + 20 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is in direction of desired range. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, repositionTaskBounds, BELOW_MIN_WIDTH_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.right + 10) assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.bottom + 20) } @Test @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_SCALED_RESIZING) fun testChangeBounds_unresizeableApp_initialWidthMoreThanMax_decreasingBounds_resizeAllowed() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat(), EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat()) val repositionTaskBounds = Rect(EXCEEDS_MAX_WIDTH_BOUNDS) // Resize to decreased bounds. val newX = EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat() + 20 val newY = EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat() + 10 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is in direction of desired range. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_LEFT or CTRL_TYPE_TOP, repositionTaskBounds, EXCEEDS_MAX_WIDTH_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.left + 20) assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.top + 10) assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.right) assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.bottom) } @Test @Test fun testChangeBoundsDoesNotChangeHeightWhenNegative() { fun testChangeBoundsDoesNotChangeHeightWhenNegative() { Loading Loading @@ -425,6 +537,60 @@ class DragPositioningCallbackUtilityTest { assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom) } } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun testMinHeight_initialHeightLessThanMin_increasingHeight_resizeAllowed() { val startingPoint = PointF(BELOW_MIN_HEIGHT_BOUNDS.right.toFloat(), BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat()) val repositionTaskBounds = Rect(BELOW_MIN_HEIGHT_BOUNDS) // Attempt to increase height. val newX = BELOW_MIN_HEIGHT_BOUNDS.right.toFloat() val newY = BELOW_MIN_HEIGHT_BOUNDS.bottom.toFloat() + 10 val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is increasing height closer to valid region. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, repositionTaskBounds, BELOW_MIN_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.right) assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_HEIGHT_BOUNDS.bottom + 10) } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun testMinWidth_initialWidthLessThanMin_increasingBounds_resizeAllowed() { val startingPoint = PointF(BELOW_MIN_WIDTH_BOUNDS.right.toFloat(), BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat()) val repositionTaskBounds = Rect(BELOW_MIN_WIDTH_BOUNDS) // Attempt to increase width. val newX = BELOW_MIN_WIDTH_BOUNDS.right.toFloat() + 10 val newY = BELOW_MIN_WIDTH_BOUNDS.bottom.toFloat() val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is increasing width closer to valid region. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM, repositionTaskBounds, BELOW_MIN_WIDTH_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.right + 10) assertThat(repositionTaskBounds.bottom).isEqualTo(BELOW_MIN_WIDTH_BOUNDS.bottom) } @Test @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() { fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() { Loading Loading @@ -547,6 +713,61 @@ class DragPositioningCallbackUtilityTest { assertThat(repositionTaskBounds.height()).isLessThan(STABLE_BOUNDS.bottom) assertThat(repositionTaskBounds.height()).isLessThan(STABLE_BOUNDS.bottom) } } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun testMaxHeight_initialHeightMoreThanMax_decreasingHeight_resizeAllowed() { mockWindowDecoration.mTaskInfo.isResizeable = false val startingPoint = PointF(EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat(), EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat()) val repositionTaskBounds = Rect(EXCEEDS_MAX_HEIGHT_BOUNDS) // Attempt to decrease height val newX = EXCEEDS_MAX_HEIGHT_BOUNDS.right.toFloat() - 10 val newY = EXCEEDS_MAX_HEIGHT_BOUNDS.top.toFloat() val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is decreasing height closer to valid region. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_RIGHT or CTRL_TYPE_TOP, repositionTaskBounds, EXCEEDS_MAX_HEIGHT_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.left) assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.right - 10) assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_HEIGHT_BOUNDS.bottom ) } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS) fun testMaxHeight_initialWidthMoreThanMax_decreasingBounds_resizeAllowed() { val startingPoint = PointF(EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat(), EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat()) val repositionTaskBounds = Rect(EXCEEDS_MAX_WIDTH_BOUNDS) // Attempt to decrease width. val newX = EXCEEDS_MAX_WIDTH_BOUNDS.left.toFloat() + 20 val newY = EXCEEDS_MAX_WIDTH_BOUNDS.top.toFloat() val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint) // Resize should be allowed as drag is decreasing width closer to valid region. assertTrue( DragPositioningCallbackUtility.changeBounds( CTRL_TYPE_LEFT or CTRL_TYPE_TOP, repositionTaskBounds, EXCEEDS_MAX_WIDTH_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController, mockWindowDecoration ) ) assertThat(repositionTaskBounds.left).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.left + 20) assertThat(repositionTaskBounds.top).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.top) assertThat(repositionTaskBounds.right).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.right) assertThat(repositionTaskBounds.bottom).isEqualTo(EXCEEDS_MAX_WIDTH_BOUNDS.bottom) } private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) { private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) { mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply { taskId = TASK_ID taskId = TASK_ID Loading @@ -571,6 +792,10 @@ class DragPositioningCallbackUtilityTest { private const val NAVBAR_HEIGHT = 50 private const val NAVBAR_HEIGHT = 50 private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600) private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600) private val STARTING_BOUNDS = Rect(0, 0, 100, 100) private val STARTING_BOUNDS = Rect(0, 0, 100, 100) private val BELOW_MIN_WIDTH_BOUNDS = Rect(0, 0, 50, 100) private val BELOW_MIN_HEIGHT_BOUNDS = Rect(0, 0, 100, 50) private val EXCEEDS_MAX_WIDTH_BOUNDS = Rect(0, 0, 3000, 1500) private val EXCEEDS_MAX_HEIGHT_BOUNDS = Rect(0, 0, 1000, 2000) private val OFF_CENTER_STARTING_BOUNDS = Rect(-100, -100, 10, 10) private val OFF_CENTER_STARTING_BOUNDS = Rect(-100, -100, 10, 10) private val DISALLOWED_RESIZE_AREA = Rect( private val DISALLOWED_RESIZE_AREA = Rect( DISPLAY_BOUNDS.left, DISPLAY_BOUNDS.left, Loading