Merge "Improve handling of TV PiPs bigger than movement bounds" into tm-dev am: 943ffdb6c1
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17717063 Change-Id: I9bf2336d9ac46ced3b1795c9e81e49857511c974 Ignore-AOSP-First: this is an automerge Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -150,7 +150,7 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
|
|||||||
return Placement(
|
return Placement(
|
||||||
pipBounds,
|
pipBounds,
|
||||||
anchorBounds,
|
anchorBounds,
|
||||||
getStashType(pipBounds, movementBounds),
|
getStashType(pipBounds, unstashedDestBounds),
|
||||||
unstashedDestBounds,
|
unstashedDestBounds,
|
||||||
result.unstashTime
|
result.unstashTime
|
||||||
)
|
)
|
||||||
@@ -185,7 +185,10 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
|
|||||||
restrictedAreas: Set<Rect>,
|
restrictedAreas: Set<Rect>,
|
||||||
unrestrictedAreas: Set<Rect>
|
unrestrictedAreas: Set<Rect>
|
||||||
): Placement {
|
): 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)
|
return Placement(pipAnchorBounds, pipAnchorBounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,9 +207,8 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
|
|||||||
?: findFreeMovePosition(pipAnchorBounds, emptySet(), unrestrictedAreas)
|
?: findFreeMovePosition(pipAnchorBounds, emptySet(), unrestrictedAreas)
|
||||||
?: pipAnchorBounds
|
?: pipAnchorBounds
|
||||||
|
|
||||||
val keepClearAreas = restrictedAreas + unrestrictedAreas
|
|
||||||
val areasOverlappingUnstashPosition =
|
val areasOverlappingUnstashPosition =
|
||||||
keepClearAreas.filter { Rect.intersects(it, unstashBounds) }.toSet()
|
keepClearAreas.filterTo(mutableSetOf()) { it.intersects(unstashBounds) }
|
||||||
val areasOverlappingUnstashPositionChanged =
|
val areasOverlappingUnstashPositionChanged =
|
||||||
!lastAreasOverlappingUnstashPosition.containsAll(areasOverlappingUnstashPosition)
|
!lastAreasOverlappingUnstashPosition.containsAll(areasOverlappingUnstashPosition)
|
||||||
lastAreasOverlappingUnstashPosition = areasOverlappingUnstashPosition
|
lastAreasOverlappingUnstashPosition = areasOverlappingUnstashPosition
|
||||||
@@ -228,19 +230,22 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
|
|||||||
return Placement(
|
return Placement(
|
||||||
stashedBounds,
|
stashedBounds,
|
||||||
pipAnchorBounds,
|
pipAnchorBounds,
|
||||||
getStashType(stashedBounds, transformedMovementBounds),
|
getStashType(stashedBounds, unstashBounds),
|
||||||
unstashBounds,
|
unstashBounds,
|
||||||
unstashTime
|
unstashTime
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@PipBoundsState.StashType
|
@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 {
|
return when {
|
||||||
stashedBounds.left < movementBounds.left -> STASH_TYPE_LEFT
|
stashedBounds.left < unstashedDestBounds.left -> STASH_TYPE_LEFT
|
||||||
stashedBounds.right > movementBounds.right -> STASH_TYPE_RIGHT
|
stashedBounds.right > unstashedDestBounds.right -> STASH_TYPE_RIGHT
|
||||||
stashedBounds.top < movementBounds.top -> STASH_TYPE_TOP
|
stashedBounds.top < unstashedDestBounds.top -> STASH_TYPE_TOP
|
||||||
stashedBounds.bottom > movementBounds.bottom -> STASH_TYPE_BOTTOM
|
stashedBounds.bottom > unstashedDestBounds.bottom -> STASH_TYPE_BOTTOM
|
||||||
else -> STASH_TYPE_NONE
|
else -> STASH_TYPE_NONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,57 +373,69 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) {
|
|||||||
val areasOverlappingPipX = keepClearAreas.filter { it.intersectsX(bounds) }
|
val areasOverlappingPipX = keepClearAreas.filter { it.intersectsX(bounds) }
|
||||||
val areasOverlappingPipY = keepClearAreas.filter { it.intersectsY(bounds) }
|
val areasOverlappingPipY = keepClearAreas.filter { it.intersectsY(bounds) }
|
||||||
|
|
||||||
|
if (areasOverlappingPipX.isNotEmpty()) {
|
||||||
if (screenBounds.bottom - bounds.bottom <= bounds.top - screenBounds.top) {
|
if (screenBounds.bottom - bounds.bottom <= bounds.top - screenBounds.top) {
|
||||||
val fullStashTop = screenBounds.bottom - stashOffset
|
val fullStashTop = screenBounds.bottom - stashOffset
|
||||||
|
|
||||||
val maxBottom = areasOverlappingPipX.maxByOrNull { it.bottom }!!.bottom
|
val maxBottom = areasOverlappingPipX.maxByOrNull { it.bottom }!!.bottom
|
||||||
val partialStashTop = maxBottom + pipAreaPadding
|
val partialStashTop = maxBottom + pipAreaPadding
|
||||||
|
|
||||||
|
val newTop = min(fullStashTop, partialStashTop)
|
||||||
|
if (newTop > bounds.top) {
|
||||||
val downPosition = Rect(bounds)
|
val downPosition = Rect(bounds)
|
||||||
downPosition.offsetTo(bounds.left, min(fullStashTop, partialStashTop))
|
downPosition.offsetTo(bounds.left, newTop)
|
||||||
stashCandidates += downPosition
|
stashCandidates += downPosition
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (screenBounds.bottom - bounds.bottom >= bounds.top - screenBounds.top) {
|
if (screenBounds.bottom - bounds.bottom >= bounds.top - screenBounds.top) {
|
||||||
val fullStashBottom = screenBounds.top - bounds.height() + stashOffset
|
val fullStashBottom = screenBounds.top - bounds.height() + stashOffset
|
||||||
|
|
||||||
val minTop = areasOverlappingPipX.minByOrNull { it.top }!!.top
|
val minTop = areasOverlappingPipX.minByOrNull { it.top }!!.top
|
||||||
val partialStashBottom = minTop - bounds.height() - pipAreaPadding
|
val partialStashBottom = minTop - bounds.height() - pipAreaPadding
|
||||||
|
|
||||||
|
val newTop = max(fullStashBottom, partialStashBottom)
|
||||||
|
if (newTop < bounds.top) {
|
||||||
val upPosition = Rect(bounds)
|
val upPosition = Rect(bounds)
|
||||||
upPosition.offsetTo(bounds.left, max(fullStashBottom, partialStashBottom))
|
upPosition.offsetTo(bounds.left, newTop)
|
||||||
stashCandidates += upPosition
|
stashCandidates += upPosition
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areasOverlappingPipY.isNotEmpty()) {
|
||||||
if (screenBounds.right - bounds.right <= bounds.left - screenBounds.left) {
|
if (screenBounds.right - bounds.right <= bounds.left - screenBounds.left) {
|
||||||
val fullStashRight = screenBounds.right - stashOffset
|
val fullStashRight = screenBounds.right - stashOffset
|
||||||
|
|
||||||
val maxRight = areasOverlappingPipY.maxByOrNull { it.right }!!.right
|
val maxRight = areasOverlappingPipY.maxByOrNull { it.right }!!.right
|
||||||
val partialStashRight = maxRight + pipAreaPadding
|
val partialStashRight = maxRight + pipAreaPadding
|
||||||
|
|
||||||
|
val newLeft = min(fullStashRight, partialStashRight)
|
||||||
|
if (newLeft > bounds.left) {
|
||||||
val rightPosition = Rect(bounds)
|
val rightPosition = Rect(bounds)
|
||||||
rightPosition.offsetTo(min(fullStashRight, partialStashRight), bounds.top)
|
rightPosition.offsetTo(newLeft, bounds.top)
|
||||||
stashCandidates += rightPosition
|
stashCandidates += rightPosition
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (screenBounds.right - bounds.right >= bounds.left - screenBounds.left) {
|
if (screenBounds.right - bounds.right >= bounds.left - screenBounds.left) {
|
||||||
val fullStashLeft = screenBounds.left - bounds.width() + stashOffset
|
val fullStashLeft = screenBounds.left - bounds.width() + stashOffset
|
||||||
|
|
||||||
val minLeft = areasOverlappingPipY.minByOrNull { it.left }!!.left
|
val minLeft = areasOverlappingPipY.minByOrNull { it.left }!!.left
|
||||||
val partialStashLeft = minLeft - bounds.width() - pipAreaPadding
|
val partialStashLeft = minLeft - bounds.width() - pipAreaPadding
|
||||||
|
|
||||||
|
val newLeft = max(fullStashLeft, partialStashLeft)
|
||||||
|
if (newLeft < bounds.left) {
|
||||||
val leftPosition = Rect(bounds)
|
val leftPosition = Rect(bounds)
|
||||||
leftPosition.offsetTo(max(fullStashLeft, partialStashLeft), bounds.top)
|
leftPosition.offsetTo(newLeft, bounds.top)
|
||||||
stashCandidates += leftPosition
|
stashCandidates += leftPosition
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (stashCandidates.isEmpty()) {
|
|
||||||
return bounds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return stashCandidates.minByOrNull {
|
return stashCandidates.minByOrNull {
|
||||||
val dx = abs(it.left - bounds.left)
|
val dx = abs(it.left - bounds.left)
|
||||||
val dy = abs(it.top - bounds.top)
|
val dy = abs(it.top - bounds.top)
|
||||||
return@minByOrNull dx + dy
|
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 {
|
private fun addDecors(size: Size): Size {
|
||||||
val bounds = Rect(0, 0, size.width, size.height)
|
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
|
* @param bounds the bounds (in screen space) to remove the insets from
|
||||||
*/
|
*/
|
||||||
private fun removePermanentDecors(bounds: Rect): Rect {
|
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
|
* @param bounds the bounds (in base case) to remove the insets from
|
||||||
*/
|
*/
|
||||||
private fun removeTemporaryDecorsTransformed(bounds: Rect): Rect {
|
private fun removeTemporaryDecorsTransformed(bounds: Rect): Rect {
|
||||||
if (pipTemporaryDecorInsets == Insets.NONE) return bounds
|
if (pipTemporaryDecorInsets == Insets.NONE) return bounds
|
||||||
|
|
||||||
var reverseInsets = Insets.subtract(Insets.NONE, pipTemporaryDecorInsets)
|
val reverseInsets = Insets.subtract(Insets.NONE, pipTemporaryDecorInsets)
|
||||||
var boundsInScreenSpace = fromTransformedSpace(bounds)
|
val boundsInScreenSpace = fromTransformedSpace(bounds)
|
||||||
boundsInScreenSpace.inset(reverseInsets)
|
boundsInScreenSpace.inset(reverseInsets)
|
||||||
return toTransformedSpace(boundsInScreenSpace)
|
return toTransformedSpace(boundsInScreenSpace)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Rect.offsetCopy(dx: Int, dy: Int) = Rect(this).apply { offset(dx, dy) }
|
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.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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_NONE
|
||||||
import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_BOTTOM
|
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_RIGHT
|
||||||
|
import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_TOP
|
||||||
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement
|
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -433,6 +434,28 @@ class TvPipKeepClearAlgorithmTest {
|
|||||||
assertEquals(currentTime + algorithm.stashDuration, placement.unstashTime)
|
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
|
@Test
|
||||||
fun test_PipInsets() {
|
fun test_PipInsets() {
|
||||||
val permInsets = Insets.of(-1, -2, -3, -4)
|
val permInsets = Insets.of(-1, -2, -3, -4)
|
||||||
|
|||||||
Reference in New Issue
Block a user