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

Commit 91f67dfe authored by Robert Horvath's avatar Robert Horvath Committed by Automerger Merge Worker
Browse files

Merge "Improve handling of TV PiPs bigger than movement bounds" into tm-dev am: 943ffdb6

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17717063



Change-Id: Ic7a2992b54492197bb685ab750743ea55c3b1855
Ignore-AOSP-First: this is an automerge
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents eaddf99e 943ffdb6
Loading
Loading
Loading
Loading
+71 −53
Original line number Diff line number Diff line
@@ -150,7 +150,7 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
        return Placement(
            pipBounds,
            anchorBounds,
            getStashType(pipBounds, movementBounds),
            getStashType(pipBounds, unstashedDestBounds),
            unstashedDestBounds,
            result.unstashTime
        )
@@ -185,7 +185,10 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
        restrictedAreas: Set<Rect>,
        unrestrictedAreas: Set<Rect>
    ): Placement {
        if (restrictedAreas.isEmpty() && unrestrictedAreas.isEmpty()) {
        // If PiP is not covered by any keep clear areas, we can leave it at the anchor bounds
        val keepClearAreas = restrictedAreas + unrestrictedAreas
        if (keepClearAreas.none { it.intersects(pipAnchorBounds) }) {
            lastAreasOverlappingUnstashPosition = emptySet()
            return Placement(pipAnchorBounds, pipAnchorBounds)
        }

@@ -204,9 +207,8 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
                ?: findFreeMovePosition(pipAnchorBounds, emptySet(), unrestrictedAreas)
                ?: pipAnchorBounds

        val keepClearAreas = restrictedAreas + unrestrictedAreas
        val areasOverlappingUnstashPosition =
            keepClearAreas.filter { Rect.intersects(it, unstashBounds) }.toSet()
            keepClearAreas.filterTo(mutableSetOf()) { it.intersects(unstashBounds) }
        val areasOverlappingUnstashPositionChanged =
            !lastAreasOverlappingUnstashPosition.containsAll(areasOverlappingUnstashPosition)
        lastAreasOverlappingUnstashPosition = areasOverlappingUnstashPosition
@@ -228,19 +230,22 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
        return Placement(
            stashedBounds,
            pipAnchorBounds,
            getStashType(stashedBounds, transformedMovementBounds),
            getStashType(stashedBounds, unstashBounds),
            unstashBounds,
            unstashTime
        )
    }

    @PipBoundsState.StashType
    private fun getStashType(stashedBounds: Rect, movementBounds: Rect): Int {
    private fun getStashType(stashedBounds: Rect, unstashedDestBounds: Rect?): Int {
        if (unstashedDestBounds == null) {
            return STASH_TYPE_NONE
        }
        return when {
            stashedBounds.left < movementBounds.left -> STASH_TYPE_LEFT
            stashedBounds.right > movementBounds.right -> STASH_TYPE_RIGHT
            stashedBounds.top < movementBounds.top -> STASH_TYPE_TOP
            stashedBounds.bottom > movementBounds.bottom -> STASH_TYPE_BOTTOM
            stashedBounds.left < unstashedDestBounds.left -> STASH_TYPE_LEFT
            stashedBounds.right > unstashedDestBounds.right -> STASH_TYPE_RIGHT
            stashedBounds.top < unstashedDestBounds.top -> STASH_TYPE_TOP
            stashedBounds.bottom > unstashedDestBounds.bottom -> STASH_TYPE_BOTTOM
            else -> STASH_TYPE_NONE
        }
    }
@@ -368,57 +373,69 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
        val areasOverlappingPipX = keepClearAreas.filter { it.intersectsX(bounds) }
        val areasOverlappingPipY = keepClearAreas.filter { it.intersectsY(bounds) }

        if (areasOverlappingPipX.isNotEmpty()) {
            if (screenBounds.bottom - bounds.bottom <= bounds.top - screenBounds.top) {
                val fullStashTop = screenBounds.bottom - stashOffset

                val maxBottom = areasOverlappingPipX.maxByOrNull { it.bottom }!!.bottom
                val partialStashTop = maxBottom + pipAreaPadding

                val newTop = min(fullStashTop, partialStashTop)
                if (newTop > bounds.top) {
                    val downPosition = Rect(bounds)
            downPosition.offsetTo(bounds.left, min(fullStashTop, partialStashTop))
                    downPosition.offsetTo(bounds.left, newTop)
                    stashCandidates += downPosition
                }
            }
            if (screenBounds.bottom - bounds.bottom >= bounds.top - screenBounds.top) {
                val fullStashBottom = screenBounds.top - bounds.height() + stashOffset

                val minTop = areasOverlappingPipX.minByOrNull { it.top }!!.top
                val partialStashBottom = minTop - bounds.height() - pipAreaPadding

                val newTop = max(fullStashBottom, partialStashBottom)
                if (newTop < bounds.top) {
                    val upPosition = Rect(bounds)
            upPosition.offsetTo(bounds.left, max(fullStashBottom, partialStashBottom))
                    upPosition.offsetTo(bounds.left, newTop)
                    stashCandidates += upPosition
                }
            }
        }

        if (areasOverlappingPipY.isNotEmpty()) {
            if (screenBounds.right - bounds.right <= bounds.left - screenBounds.left) {
                val fullStashRight = screenBounds.right - stashOffset

                val maxRight = areasOverlappingPipY.maxByOrNull { it.right }!!.right
                val partialStashRight = maxRight + pipAreaPadding

                val newLeft = min(fullStashRight, partialStashRight)
                if (newLeft > bounds.left) {
                    val rightPosition = Rect(bounds)
            rightPosition.offsetTo(min(fullStashRight, partialStashRight), bounds.top)
                    rightPosition.offsetTo(newLeft, bounds.top)
                    stashCandidates += rightPosition
                }
            }
            if (screenBounds.right - bounds.right >= bounds.left - screenBounds.left) {
                val fullStashLeft = screenBounds.left - bounds.width() + stashOffset

                val minLeft = areasOverlappingPipY.minByOrNull { it.left }!!.left
                val partialStashLeft = minLeft - bounds.width() - pipAreaPadding

                val newLeft = max(fullStashLeft, partialStashLeft)
                if (newLeft < bounds.left) {
                    val leftPosition = Rect(bounds)
            leftPosition.offsetTo(max(fullStashLeft, partialStashLeft), bounds.top)
                    leftPosition.offsetTo(newLeft, bounds.top)
                    stashCandidates += leftPosition
                }

        if (stashCandidates.isEmpty()) {
            return bounds
            }
        }

        return stashCandidates.minByOrNull {
            val dx = abs(it.left - bounds.left)
            val dy = abs(it.top - bounds.top)
            return@minByOrNull dx + dy
        }!!
        } ?: bounds
    }

    /**
@@ -768,7 +785,7 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
    }

    /**
     * Adds space around [size] to leave space for decorations that will be drawn around the pip
     * Adds space around [size] to leave space for decorations that will be drawn around the PiP
     */
    private fun addDecors(size: Size): Size {
        val bounds = Rect(0, 0, size.width, size.height)
@@ -779,7 +796,7 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
    }

    /**
     * Removes the space that was reserved for permanent decorations around the pip
     * Removes the space that was reserved for permanent decorations around the PiP
     * @param bounds the bounds (in screen space) to remove the insets from
     */
    private fun removePermanentDecors(bounds: Rect): Rect {
@@ -789,19 +806,20 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
    }

    /**
     * Removes the space that was reserved for temporary decorations around the pip
     * Removes the space that was reserved for temporary decorations around the PiP
     * @param bounds the bounds (in base case) to remove the insets from
     */
    private fun removeTemporaryDecorsTransformed(bounds: Rect): Rect {
        if (pipTemporaryDecorInsets == Insets.NONE) return bounds

        var reverseInsets = Insets.subtract(Insets.NONE, pipTemporaryDecorInsets)
        var boundsInScreenSpace = fromTransformedSpace(bounds)
        val reverseInsets = Insets.subtract(Insets.NONE, pipTemporaryDecorInsets)
        val boundsInScreenSpace = fromTransformedSpace(bounds)
        boundsInScreenSpace.inset(reverseInsets)
        return toTransformedSpace(boundsInScreenSpace)
    }

    private fun Rect.offsetCopy(dx: Int, dy: Int) = Rect(this).apply { offset(dx, dy) }
    private fun Rect.intersectsY(other: Rect) = bottom >= other.top && top <= other.bottom
    private fun Rect.intersectsX(other: Rect) = right >= other.left && left <= other.right
    private fun Rect.intersectsY(other: Rect) = bottom >= other.top && top <= other.bottom
    private fun Rect.intersects(other: Rect) = intersectsX(other) && intersectsY(other)
}
+23 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import org.junit.runner.RunWith
import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE
import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_BOTTOM
import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT
import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_TOP
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement
import org.junit.Before
import org.junit.Test
@@ -433,6 +434,28 @@ class TvPipKeepClearAlgorithmTest {
        assertEquals(currentTime + algorithm.stashDuration, placement.unstashTime)
    }

    @Test
    fun test_ExpandedPiPHeightExceedsMovementBounds_AtAnchor() {
        gravity = Gravity.RIGHT or Gravity.CENTER_VERTICAL
        pipSize = Size(DEFAULT_PIP_SIZE.width, SCREEN_SIZE.height)
        testAnchorPosition()
    }

    @Test
    fun test_ExpandedPiPHeightExceedsMovementBounds_BottomBar_StashedUp() {
        gravity = Gravity.RIGHT or Gravity.CENTER_VERTICAL
        pipSize = Size(DEFAULT_PIP_SIZE.width, SCREEN_SIZE.height)
        val bottomBar = makeBottomBar(96)
        unrestrictedAreas.add(bottomBar)

        val expectedBounds = getExpectedAnchorBounds()
        expectedBounds.offset(0, -bottomBar.height() - PADDING)
        val placement = getActualPlacement()
        assertEquals(expectedBounds, placement.bounds)
        assertEquals(STASH_TYPE_TOP, placement.stashType)
        assertEquals(getExpectedAnchorBounds(), placement.unstashDestinationBounds)
    }

    @Test
    fun test_PipInsets() {
        val permInsets = Insets.of(-1, -2, -3, -4)