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

Commit 4ab3c75b 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...

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

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



Change-Id: I83f704fdaf73e37e1df3cb9c8e612e57fcc710ff
Ignore-AOSP-First: this is an automerge
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents ffc939e9 bc356e61
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)