Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculator.kt +23 −10 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.graphics.PointF import android.graphics.Rect import android.graphics.RectF import android.util.MathUtils.min import kotlin.math.ceil /** * Utility class for calculating bounds during multi-display drag operations. Loading Loading @@ -91,6 +92,7 @@ object MultiDisplayDragMoveBoundsCalculator { displayLayout: DisplayLayout?, isResizeable: Boolean, pointerX: Float, captionInsets: Int, ): Rect { if (displayLayout == null) { return originalBounds Loading Loading @@ -123,7 +125,9 @@ object MultiDisplayDragMoveBoundsCalculator { } val scaleFactorHorizontal = intersectBounds.width().toFloat() / originalBounds.width() val scaleFactorVertical = intersectBounds.height().toFloat() / originalBounds.height() val scaleFactorVertical = (intersectBounds.height() - captionInsets).toFloat() / (originalBounds.height() - captionInsets) val scaleFactor = min(scaleFactorHorizontal, scaleFactorVertical) val isLeftCornerIn = displayBoundsOverhang.contains(originalBounds.left, originalBounds.top) Loading @@ -131,13 +135,14 @@ object MultiDisplayDragMoveBoundsCalculator { displayBoundsOverhang.contains(originalBounds.right, originalBounds.top) if (isLeftCornerIn && isRightCornerIn) { // Case 1: Both top corners are on-screen. Anchor to the pointer's horizontal position. return scaleWithHorizontalOrigin(originalBounds, scaleFactor, pointerX) return scaleWithHorizontalOrigin(originalBounds, scaleFactor, pointerX, captionInsets) } else if (isLeftCornerIn) { // Case 2: Only the top-left corner is on-screen. Anchor to that corner. return scaleWithHorizontalOrigin( originalBounds, scaleFactor, originalBounds.left.toFloat(), captionInsets, ) } else if (isRightCornerIn) { // Case 3: Only the top-right corner is on-screen. Anchor to that corner. Loading @@ -145,6 +150,7 @@ object MultiDisplayDragMoveBoundsCalculator { originalBounds, scaleFactor, originalBounds.right.toFloat(), captionInsets, ) } Loading @@ -152,7 +158,12 @@ object MultiDisplayDragMoveBoundsCalculator { if (scaleFactorHorizontal > scaleFactorVertical) { // The height is the limiting factor. We can still safely anchor to the pointer's // horizontal position while scaling to fit vertically. return scaleWithHorizontalOrigin(originalBounds, scaleFactorVertical, pointerX) return scaleWithHorizontalOrigin( originalBounds, scaleFactorVertical, pointerX, captionInsets, ) } // The width is the limiting factor. To prevent anchoring to a potentially far-off-screen // point, we force the window's width to match the allowed display width, and then scales Loading @@ -161,21 +172,23 @@ object MultiDisplayDragMoveBoundsCalculator { displayBoundsOverhang.left, originalBounds.top, displayBoundsOverhang.right, originalBounds.top + (originalBounds.height() * scaleFactorHorizontal).toInt(), originalBounds.top + ceil((originalBounds.height() - captionInsets) * scaleFactorHorizontal).toInt() + captionInsets, ) } /** * Scales a Rect from a horizontal anchor point, keeping the top edge fixed. */ /** Scales a Rect from a horizontal anchor point, keeping the top edge fixed. */ private fun scaleWithHorizontalOrigin( originalBounds: Rect, scaleFactor: Float, originX: Float, captionInsets: Int, ): Rect { val height = (originalBounds.height() * scaleFactor).toInt() val left = (originX + (originalBounds.left - originX) * scaleFactor).toInt() val right = (originX + (originalBounds.right - originX) * scaleFactor).toInt() val left = ceil(originX + (originalBounds.left - originX) * scaleFactor).toInt() val right = ceil(originX + (originalBounds.right - originX) * scaleFactor).toInt() val height = ceil((originalBounds.height() - captionInsets) * scaleFactor).toInt() + captionInsets return Rect(left, originalBounds.top, right, originalBounds.top + height) } Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +13 −1 Original line number Diff line number Diff line Loading @@ -5524,15 +5524,27 @@ class DesktopTasksController( displayAreaInfo != null if (isCrossDisplayDrag) { val prevCaptionInsets = taskInfo.configuration.windowConfiguration.appBounds?.let { it.top - taskInfo.configuration.windowConfiguration.bounds.top } ?: 0 val captionInsetsDp = displayController .getDisplayLayout(taskInfo.getDisplayId()) ?.pxToDp(prevCaptionInsets) ?.toInt() ?: 0 val destDisplayLayout = displayController.getDisplayLayout(newDisplayId) val captionInsets = destDisplayLayout?.dpToPx(captionInsetsDp)?.toInt() ?: 0 val constrainedBounds = if ( DesktopExperienceFlags.ENABLE_SHRINK_WINDOW_BOUNDS_AFTER_DRAG.isTrue() ) { MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( destinationBounds, displayController.getDisplayLayout(newDisplayId), destDisplayLayout, taskInfo.isResizeable, inputCoordinate.x, captionInsets, ) } else { Rect(destinationBounds) Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculatorTest.kt +80 −10 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import org.junit.Test */ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { private lateinit var resources: TestableResources private val ASPECT_RATIO_TOLERANCE = 0.05f @Before fun setUp() { Loading Loading @@ -90,6 +91,7 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout = null, isResizeable = false, pointerX = 50f, captionInsets = 0, ) assertEquals(originalBounds, result) } Loading @@ -107,6 +109,27 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = true, pointerX = 50f, captionInsets = 0, ) assertEquals(originalBounds, result) } @Test fun constrainBoundsForDisplay_nonResizeableFullyContained_returnsOriginalBounds() { // displayBounds: (0, 0, 100, 300), displayBoundsOverhang: (-24, -24, 124, 324) val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // Fits well within DISPLAY + overhang val originalBounds = Rect(10, 20, 110, 120) val captionInsetsPx = 10 val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( originalBounds, displayLayout, isResizeable = false, pointerX = 50f, captionInsets = captionInsetsPx, ) assertEquals(originalBounds, result) Loading @@ -125,6 +148,7 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = true, pointerX = 10f, captionInsets = 0, ) val expectedBounds = Rect(10, 20, 124, 324) Loading @@ -137,6 +161,8 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // width=50, height=100, exceeding bottom. val originalBounds = Rect(10, 254, 60, 354) val captionInsetsPx = 10 val originalAspectRatio = calculateAspectRatio(originalBounds, captionInsetsPx) val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( Loading @@ -144,11 +170,17 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = false, pointerX = 40f, captionInsets = captionInsetsPx, ) // width=35, height=70 val expectedBounds = Rect(19, 254, 54, 324) // width=34, height=70 val expectedBounds = Rect(20, 254, 54, 324) assertEquals(expectedBounds, result) assertEquals( originalAspectRatio, calculateAspectRatio(expectedBounds, captionInsetsPx), ASPECT_RATIO_TOLERANCE, ) } @Test Loading @@ -157,6 +189,8 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // width=88, height=100, exceeding right. val originalBounds = Rect(80, 100, 168, 200) val captionInsetsPx = 10 val originalAspectRatio = calculateAspectRatio(originalBounds, captionInsetsPx) val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( Loading @@ -164,11 +198,17 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = false, pointerX = 100f, captionInsets = captionInsetsPx, ) // width=44, height=50 val expectedBounds = Rect(80, 100, 124, 150) // width=44, height=55 val expectedBounds = Rect(80, 100, 124, 155) assertEquals(expectedBounds, result) assertEquals( originalAspectRatio, calculateAspectRatio(expectedBounds, captionInsetsPx), ASPECT_RATIO_TOLERANCE, ) } @Test Loading @@ -177,6 +217,8 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // width=106, height=200, exceeding left and bottom. val originalBounds = Rect(-30, 224, 76, 424) val captionInsetsPx = 10 val originalAspectRatio = calculateAspectRatio(originalBounds, captionInsetsPx) val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( Loading @@ -184,11 +226,17 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = false, pointerX = 100f, captionInsets = captionInsetsPx, ) // width=53, height=100 val expectedBounds = Rect(23, 224, 76, 324) // width=50, height=100 val expectedBounds = Rect(26, 224, 76, 324) assertEquals(expectedBounds, result) assertEquals( originalAspectRatio, calculateAspectRatio(expectedBounds, captionInsetsPx), ASPECT_RATIO_TOLERANCE, ) } @Test Loading @@ -197,6 +245,8 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // width=180, height=200, exceeding left, right and bottom. val originalBounds = Rect(-30, 224, 150, 424) val captionInsetsPx = 10 val originalAspectRatio = calculateAspectRatio(originalBounds, captionInsetsPx) val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( Loading @@ -204,11 +254,17 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = false, pointerX = 40f, captionInsets = captionInsetsPx, ) // width=90, height=100 val expectedBounds = Rect(5, 224, 95, 324) // width=86, height=100 val expectedBounds = Rect(7, 224, 93, 324) assertEquals(expectedBounds, result) assertEquals( originalAspectRatio, calculateAspectRatio(expectedBounds, captionInsetsPx), ASPECT_RATIO_TOLERANCE, ) } @Test Loading @@ -217,6 +273,8 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // width=296, height=100, exceeding left, right and bottom. val originalBounds = Rect(-30, 54, 266, 154) val captionInsetsPx = 10 val originalAspectRatio = calculateAspectRatio(originalBounds, captionInsetsPx) val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( Loading @@ -224,10 +282,22 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = false, pointerX = 40f, captionInsets = captionInsetsPx, ) // width=148, height=50 val expectedBounds = Rect(-24, 54, 124, 104) // width=148, height=55 val expectedBounds = Rect(-24, 54, 124, 109) assertEquals(expectedBounds, result) assertEquals( originalAspectRatio, calculateAspectRatio(expectedBounds, captionInsetsPx), ASPECT_RATIO_TOLERANCE, ) } private fun calculateAspectRatio(bounds: Rect, captionInsets: Int): Float { val appBounds = Rect(bounds).apply { top += captionInsets } return maxOf(appBounds.height(), appBounds.width()) / minOf(appBounds.height(), appBounds.width()).toFloat() } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculator.kt +23 −10 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.graphics.PointF import android.graphics.Rect import android.graphics.RectF import android.util.MathUtils.min import kotlin.math.ceil /** * Utility class for calculating bounds during multi-display drag operations. Loading Loading @@ -91,6 +92,7 @@ object MultiDisplayDragMoveBoundsCalculator { displayLayout: DisplayLayout?, isResizeable: Boolean, pointerX: Float, captionInsets: Int, ): Rect { if (displayLayout == null) { return originalBounds Loading Loading @@ -123,7 +125,9 @@ object MultiDisplayDragMoveBoundsCalculator { } val scaleFactorHorizontal = intersectBounds.width().toFloat() / originalBounds.width() val scaleFactorVertical = intersectBounds.height().toFloat() / originalBounds.height() val scaleFactorVertical = (intersectBounds.height() - captionInsets).toFloat() / (originalBounds.height() - captionInsets) val scaleFactor = min(scaleFactorHorizontal, scaleFactorVertical) val isLeftCornerIn = displayBoundsOverhang.contains(originalBounds.left, originalBounds.top) Loading @@ -131,13 +135,14 @@ object MultiDisplayDragMoveBoundsCalculator { displayBoundsOverhang.contains(originalBounds.right, originalBounds.top) if (isLeftCornerIn && isRightCornerIn) { // Case 1: Both top corners are on-screen. Anchor to the pointer's horizontal position. return scaleWithHorizontalOrigin(originalBounds, scaleFactor, pointerX) return scaleWithHorizontalOrigin(originalBounds, scaleFactor, pointerX, captionInsets) } else if (isLeftCornerIn) { // Case 2: Only the top-left corner is on-screen. Anchor to that corner. return scaleWithHorizontalOrigin( originalBounds, scaleFactor, originalBounds.left.toFloat(), captionInsets, ) } else if (isRightCornerIn) { // Case 3: Only the top-right corner is on-screen. Anchor to that corner. Loading @@ -145,6 +150,7 @@ object MultiDisplayDragMoveBoundsCalculator { originalBounds, scaleFactor, originalBounds.right.toFloat(), captionInsets, ) } Loading @@ -152,7 +158,12 @@ object MultiDisplayDragMoveBoundsCalculator { if (scaleFactorHorizontal > scaleFactorVertical) { // The height is the limiting factor. We can still safely anchor to the pointer's // horizontal position while scaling to fit vertically. return scaleWithHorizontalOrigin(originalBounds, scaleFactorVertical, pointerX) return scaleWithHorizontalOrigin( originalBounds, scaleFactorVertical, pointerX, captionInsets, ) } // The width is the limiting factor. To prevent anchoring to a potentially far-off-screen // point, we force the window's width to match the allowed display width, and then scales Loading @@ -161,21 +172,23 @@ object MultiDisplayDragMoveBoundsCalculator { displayBoundsOverhang.left, originalBounds.top, displayBoundsOverhang.right, originalBounds.top + (originalBounds.height() * scaleFactorHorizontal).toInt(), originalBounds.top + ceil((originalBounds.height() - captionInsets) * scaleFactorHorizontal).toInt() + captionInsets, ) } /** * Scales a Rect from a horizontal anchor point, keeping the top edge fixed. */ /** Scales a Rect from a horizontal anchor point, keeping the top edge fixed. */ private fun scaleWithHorizontalOrigin( originalBounds: Rect, scaleFactor: Float, originX: Float, captionInsets: Int, ): Rect { val height = (originalBounds.height() * scaleFactor).toInt() val left = (originX + (originalBounds.left - originX) * scaleFactor).toInt() val right = (originX + (originalBounds.right - originX) * scaleFactor).toInt() val left = ceil(originX + (originalBounds.left - originX) * scaleFactor).toInt() val right = ceil(originX + (originalBounds.right - originX) * scaleFactor).toInt() val height = ceil((originalBounds.height() - captionInsets) * scaleFactor).toInt() + captionInsets return Rect(left, originalBounds.top, right, originalBounds.top + height) } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +13 −1 Original line number Diff line number Diff line Loading @@ -5524,15 +5524,27 @@ class DesktopTasksController( displayAreaInfo != null if (isCrossDisplayDrag) { val prevCaptionInsets = taskInfo.configuration.windowConfiguration.appBounds?.let { it.top - taskInfo.configuration.windowConfiguration.bounds.top } ?: 0 val captionInsetsDp = displayController .getDisplayLayout(taskInfo.getDisplayId()) ?.pxToDp(prevCaptionInsets) ?.toInt() ?: 0 val destDisplayLayout = displayController.getDisplayLayout(newDisplayId) val captionInsets = destDisplayLayout?.dpToPx(captionInsetsDp)?.toInt() ?: 0 val constrainedBounds = if ( DesktopExperienceFlags.ENABLE_SHRINK_WINDOW_BOUNDS_AFTER_DRAG.isTrue() ) { MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( destinationBounds, displayController.getDisplayLayout(newDisplayId), destDisplayLayout, taskInfo.isResizeable, inputCoordinate.x, captionInsets, ) } else { Rect(destinationBounds) Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveBoundsCalculatorTest.kt +80 −10 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import org.junit.Test */ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { private lateinit var resources: TestableResources private val ASPECT_RATIO_TOLERANCE = 0.05f @Before fun setUp() { Loading Loading @@ -90,6 +91,7 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout = null, isResizeable = false, pointerX = 50f, captionInsets = 0, ) assertEquals(originalBounds, result) } Loading @@ -107,6 +109,27 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = true, pointerX = 50f, captionInsets = 0, ) assertEquals(originalBounds, result) } @Test fun constrainBoundsForDisplay_nonResizeableFullyContained_returnsOriginalBounds() { // displayBounds: (0, 0, 100, 300), displayBoundsOverhang: (-24, -24, 124, 324) val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // Fits well within DISPLAY + overhang val originalBounds = Rect(10, 20, 110, 120) val captionInsetsPx = 10 val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( originalBounds, displayLayout, isResizeable = false, pointerX = 50f, captionInsets = captionInsetsPx, ) assertEquals(originalBounds, result) Loading @@ -125,6 +148,7 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = true, pointerX = 10f, captionInsets = 0, ) val expectedBounds = Rect(10, 20, 124, 324) Loading @@ -137,6 +161,8 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // width=50, height=100, exceeding bottom. val originalBounds = Rect(10, 254, 60, 354) val captionInsetsPx = 10 val originalAspectRatio = calculateAspectRatio(originalBounds, captionInsetsPx) val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( Loading @@ -144,11 +170,17 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = false, pointerX = 40f, captionInsets = captionInsetsPx, ) // width=35, height=70 val expectedBounds = Rect(19, 254, 54, 324) // width=34, height=70 val expectedBounds = Rect(20, 254, 54, 324) assertEquals(expectedBounds, result) assertEquals( originalAspectRatio, calculateAspectRatio(expectedBounds, captionInsetsPx), ASPECT_RATIO_TOLERANCE, ) } @Test Loading @@ -157,6 +189,8 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // width=88, height=100, exceeding right. val originalBounds = Rect(80, 100, 168, 200) val captionInsetsPx = 10 val originalAspectRatio = calculateAspectRatio(originalBounds, captionInsetsPx) val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( Loading @@ -164,11 +198,17 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = false, pointerX = 100f, captionInsets = captionInsetsPx, ) // width=44, height=50 val expectedBounds = Rect(80, 100, 124, 150) // width=44, height=55 val expectedBounds = Rect(80, 100, 124, 155) assertEquals(expectedBounds, result) assertEquals( originalAspectRatio, calculateAspectRatio(expectedBounds, captionInsetsPx), ASPECT_RATIO_TOLERANCE, ) } @Test Loading @@ -177,6 +217,8 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // width=106, height=200, exceeding left and bottom. val originalBounds = Rect(-30, 224, 76, 424) val captionInsetsPx = 10 val originalAspectRatio = calculateAspectRatio(originalBounds, captionInsetsPx) val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( Loading @@ -184,11 +226,17 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = false, pointerX = 100f, captionInsets = captionInsetsPx, ) // width=53, height=100 val expectedBounds = Rect(23, 224, 76, 324) // width=50, height=100 val expectedBounds = Rect(26, 224, 76, 324) assertEquals(expectedBounds, result) assertEquals( originalAspectRatio, calculateAspectRatio(expectedBounds, captionInsetsPx), ASPECT_RATIO_TOLERANCE, ) } @Test Loading @@ -197,6 +245,8 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // width=180, height=200, exceeding left, right and bottom. val originalBounds = Rect(-30, 224, 150, 424) val captionInsetsPx = 10 val originalAspectRatio = calculateAspectRatio(originalBounds, captionInsetsPx) val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( Loading @@ -204,11 +254,17 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = false, pointerX = 40f, captionInsets = captionInsetsPx, ) // width=90, height=100 val expectedBounds = Rect(5, 224, 95, 324) // width=86, height=100 val expectedBounds = Rect(7, 224, 93, 324) assertEquals(expectedBounds, result) assertEquals( originalAspectRatio, calculateAspectRatio(expectedBounds, captionInsetsPx), ASPECT_RATIO_TOLERANCE, ) } @Test Loading @@ -217,6 +273,8 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { val displayLayout = TestDisplay.DISPLAY_3.getSpyDisplayLayout(resources.resources) // width=296, height=100, exceeding left, right and bottom. val originalBounds = Rect(-30, 54, 266, 154) val captionInsetsPx = 10 val originalAspectRatio = calculateAspectRatio(originalBounds, captionInsetsPx) val result = MultiDisplayDragMoveBoundsCalculator.constrainBoundsForDisplay( Loading @@ -224,10 +282,22 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() { displayLayout, isResizeable = false, pointerX = 40f, captionInsets = captionInsetsPx, ) // width=148, height=50 val expectedBounds = Rect(-24, 54, 124, 104) // width=148, height=55 val expectedBounds = Rect(-24, 54, 124, 109) assertEquals(expectedBounds, result) assertEquals( originalAspectRatio, calculateAspectRatio(expectedBounds, captionInsetsPx), ASPECT_RATIO_TOLERANCE, ) } private fun calculateAspectRatio(bounds: Rect, captionInsets: Int): Float { val appBounds = Rect(bounds).apply { top += captionInsets } return maxOf(appBounds.height(), appBounds.width()) / minOf(appBounds.height(), appBounds.width()).toFloat() } }