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

Commit 822a1130 authored by Graciela Putri's avatar Graciela Putri Committed by Android (Google) Code Review
Browse files

Merge "Scale apps in cross display drag without caption insets" into main

parents e8827def 79c29435
Loading
Loading
Loading
Loading
+23 −10
Original line number Diff line number Diff line
@@ -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.
@@ -91,6 +92,7 @@ object MultiDisplayDragMoveBoundsCalculator {
        displayLayout: DisplayLayout?,
        isResizeable: Boolean,
        pointerX: Float,
        captionInsets: Int,
    ): Rect {
        if (displayLayout == null) {
            return originalBounds
@@ -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)
@@ -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.
@@ -145,6 +150,7 @@ object MultiDisplayDragMoveBoundsCalculator {
                originalBounds,
                scaleFactor,
                originalBounds.right.toFloat(),
                captionInsets,
            )
        }

@@ -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
@@ -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)
    }

+13 −1
Original line number Diff line number Diff line
@@ -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)
+80 −10
Original line number Diff line number Diff line
@@ -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() {
@@ -90,6 +91,7 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() {
                displayLayout = null,
                isResizeable = false,
                pointerX = 50f,
                captionInsets = 0,
            )
        assertEquals(originalBounds, result)
    }
@@ -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)
@@ -125,6 +148,7 @@ class MultiDisplayDragMoveBoundsCalculatorTest : ShellTestCase() {
                displayLayout,
                isResizeable = true,
                pointerX = 10f,
                captionInsets = 0,
            )

        val expectedBounds = Rect(10, 20, 124, 324)
@@ -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(
@@ -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
@@ -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(
@@ -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
@@ -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(
@@ -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
@@ -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(
@@ -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
@@ -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(
@@ -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()
    }
}