From 2bf6aaf17397948ff888937b1c4c660ab130dc64 Mon Sep 17 00:00:00 2001 From: Chris Göllner Date: Mon, 5 Feb 2024 16:24:43 +0000 Subject: Remove unneeded status bar side insets when cutout is at the bottom When the cutout is in the bottom corner, and is considered to be a "bottom" cutout, instead of a "side" cutout, it still shares a short edge with the status bar. Because of that, we were adding side insets to the status bar, even when not needed. The fix is to exclude bottom cutouts from the intersection calculations. Fixes: 321957421 Test: StatusBarContentInsetsProviderTest Test: Manually Flag: NONE Change-Id: I76c3cde7ad5c19058b9d6a22e08c0497fc53164f --- .../phone/StatusBarContentInsetsProviderTest.kt | 147 ++++++++++++++------- .../phone/StatusBarContentInsetsProvider.kt | 6 +- 2 files changed, 102 insertions(+), 51 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt index 53b262bd29a1..8b6880c3cb67 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt @@ -69,6 +69,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { context.createConfigurationContext(it.arguments[0] as Configuration) } configurationController = ConfigurationControllerImpl(contextMock) + setNoCutout() } @Test @@ -178,7 +179,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { val dotWidth = 10 val statusBarContentHeight = 15 - whenever(dc.boundingRects).thenReturn(listOf(dcBounds)) + setCutoutBounds(top = dcBounds) // THEN rotations which share a short side should use the greater value between rounded // corner padding and the display cutout's size @@ -285,11 +286,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { val dotWidth = 10 val statusBarContentHeight = 15 - val protectionInfo = mock { - whenever(this.cutoutBounds).thenReturn(protectionBounds) - } - whenever(sysUICutout.cameraProtection).thenReturn(protectionInfo) - whenever(dc.boundingRects).thenReturn(listOf(dcBounds)) + setCameraProtectionBounds(protectionBounds) + setCutoutBounds(top = dcBounds) // THEN rotations which share a short side should use the greater value between rounded // corner padding, the display cutout's size, and the camera protections' size. @@ -381,11 +379,36 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { assertRects(expectedBounds, bounds, currentRotation, targetRotation) } + private fun Rect(left: Int, top: Int, right: Int, bottom: Int) = + android.graphics.Rect(left, top, right, bottom) + @Test fun testCalculateInsetsForRotationWithRotatedResources_topRightCutout_noCameraProtection() { - // GIVEN a device in portrait mode with width < height and a display cutout in the top-left val screenBounds = Rect(0, 0, 1000, 2000) - val dcBounds = Rect(900, 0, 1000, 100) + val dcWidth = 100 + val dcHeight = 50 + val dcBoundsPortrait = + Rect( + left = screenBounds.right - dcWidth, + top = 0, + right = screenBounds.right, + bottom = dcHeight + ) + val dcBoundsLandscape = Rect(left = 0, top = 0, right = dcHeight, bottom = dcWidth) + val dcBoundsSeascape = + Rect( + left = screenBounds.right - dcHeight, + top = screenBounds.bottom - dcWidth, + right = screenBounds.right - dcHeight, + bottom = screenBounds.bottom - dcWidth + ) + val dcBoundsUpsideDown = + Rect( + left = 0, + top = screenBounds.bottom - dcHeight, + right = dcWidth, + bottom = screenBounds.bottom - dcHeight + ) val minLeftPadding = 20 val minRightPadding = 20 val sbHeightPortrait = 100 @@ -395,15 +418,15 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { val dotWidth = 10 val statusBarContentHeight = 15 - whenever(dc.boundingRects).thenReturn(listOf(dcBounds)) - - // THEN rotations which share a short side should use the greater value between rounded - // corner padding and the display cutout's size var targetRotation = ROTATION_NONE - var expectedBounds = Rect(minLeftPadding, - 0, - dcBounds.left - dotWidth, - sbHeightPortrait) + setCutoutBounds(top = dcBoundsPortrait) + var expectedBounds = + Rect( + left = minLeftPadding, + top = 0, + right = dcBoundsPortrait.left - dotWidth, + bottom = sbHeightPortrait + ) var bounds = calculateInsetsForRotationWithRotatedResources( currentRotation, @@ -421,10 +444,14 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { assertRects(expectedBounds, bounds, currentRotation, targetRotation) targetRotation = ROTATION_LANDSCAPE - expectedBounds = Rect(dcBounds.height(), - 0, - screenBounds.height() - minRightPadding, - sbHeightLandscape) + setCutoutBounds(top = dcBoundsLandscape) + expectedBounds = + Rect( + left = dcBoundsLandscape.height(), + top = 0, + right = screenBounds.height() - minRightPadding, + bottom = sbHeightLandscape + ) bounds = calculateInsetsForRotationWithRotatedResources( currentRotation, @@ -441,13 +468,15 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { assertRects(expectedBounds, bounds, currentRotation, targetRotation) - // THEN the side that does NOT share a short side with the display cutout ignores the - // display cutout bounds targetRotation = ROTATION_UPSIDE_DOWN - expectedBounds = Rect(minLeftPadding, - 0, - screenBounds.width() - minRightPadding, - sbHeightPortrait) + setCutoutBounds(bottom = dcBoundsUpsideDown) + expectedBounds = + Rect( + left = minLeftPadding, + top = 0, + right = screenBounds.width() - minRightPadding, + bottom = sbHeightPortrait + ) bounds = calculateInsetsForRotationWithRotatedResources( currentRotation, @@ -464,12 +493,15 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { assertRects(expectedBounds, bounds, currentRotation, targetRotation) - // Phone in portrait, seascape (rot_270) bounds targetRotation = ROTATION_SEASCAPE - expectedBounds = Rect(minLeftPadding, - 0, - screenBounds.height() - dcBounds.height() - dotWidth, - sbHeightLandscape) + setCutoutBounds(bottom = dcBoundsSeascape) + expectedBounds = + Rect( + left = minLeftPadding, + top = 0, + right = screenBounds.height() - minRightPadding, + bottom = sbHeightLandscape + ) bounds = calculateInsetsForRotationWithRotatedResources( currentRotation, @@ -502,19 +534,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { val dotWidth = 10 val statusBarContentHeight = 15 - val protectionInfo = mock { - whenever(this.cutoutBounds).thenReturn(protectionBounds) - } - whenever(sysUICutout.cameraProtection).thenReturn(protectionInfo) - whenever(dc.boundingRects).thenReturn(listOf(dcBounds)) + setCameraProtectionBounds(protectionBounds) - // THEN rotations which share a short side should use the greater value between rounded - // corner padding, the display cutout's size, and the camera protections' size. var targetRotation = ROTATION_NONE - var expectedBounds = Rect(minLeftPadding, - 0, - protectionBounds.left - dotWidth, - sbHeightPortrait) + setCutoutBounds(top = dcBounds) + var expectedBounds = + Rect(minLeftPadding, 0, protectionBounds.left - dotWidth, sbHeightPortrait) var bounds = calculateInsetsForRotationWithRotatedResources( currentRotation, @@ -600,7 +625,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { @Test fun calculateInsetsForRotationWithRotatedResources_bottomAlignedMarginDisabled_noTopInset() { - whenever(dc.boundingRects).thenReturn(emptyList()) + setNoCutout() val bounds = calculateInsetsForRotationWithRotatedResources( currentRotation = ROTATION_NONE, @@ -663,11 +688,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { val dotWidth = 10 val statusBarContentHeight = 15 - val protectionInfo = mock { - whenever(this.cutoutBounds).thenReturn(protectionBounds) - } - whenever(sysUICutout.cameraProtection).thenReturn(protectionInfo) - whenever(dc.boundingRects).thenReturn(listOf(dcBounds)) + setCameraProtectionBounds(protectionBounds) + setCutoutBounds(top = dcBounds) // THEN only the landscape/seascape rotations should avoid the cutout area because of the // potential letterboxing @@ -865,7 +887,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { val dotWidth = 10 val statusBarContentHeight = 15 - whenever(dc.boundingRects).thenReturn(listOf(dcBounds)) + setCutoutBounds(top = dcBounds) // THEN left should be set to the display cutout width, and right should use the minRight val targetRotation = ROTATION_NONE @@ -1008,7 +1030,32 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" + " targetRotation=${RotationUtils.toString(targetRotation)}" + " expected=$expected actual=$actual", - expected.equals(actual)) + expected.equals(actual) + ) + } + + private fun setNoCutout() { + setCutoutBounds() + } + + private fun setCutoutBounds( + left: Rect = Rect(), + top: Rect = Rect(), + right: Rect = Rect(), + bottom: Rect = Rect() + ) { + whenever(dc.boundingRects) + .thenReturn(listOf(left, top, right, bottom).filter { !it.isEmpty }) + whenever(dc.boundingRectLeft).thenReturn(left) + whenever(dc.boundingRectTop).thenReturn(top) + whenever(dc.boundingRectRight).thenReturn(right) + whenever(dc.boundingRectBottom).thenReturn(bottom) + } + + private fun setCameraProtectionBounds(protectionBounds: Rect) { + val protectionInfo = + mock { whenever(this.cutoutBounds).thenReturn(protectionBounds) } + whenever(sysUICutout.cameraProtection).thenReturn(protectionInfo) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt index e84b7a077b21..ac203db209e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt @@ -494,7 +494,8 @@ private fun getStatusBarContentBounds( val logicalDisplayWidth = if (targetRotation.isHorizontal()) height else width - val cutoutRects = sysUICutout?.cutout?.boundingRects + // Exclude the bottom rect, as it doesn't intersect with the status bar. + val cutoutRects = sysUICutout?.cutout?.boundingRectsLeftRightTop if (cutoutRects.isNullOrEmpty()) { return Rect(minLeft, insetTop, logicalDisplayWidth - minRight, sbHeight) } @@ -547,6 +548,9 @@ private fun rectUnion(first: Rect, second: Rect) = Rect(first).apply { union(sec private fun Rect.intersects(other: Rect): Boolean = intersects(other.left, other.top, other.right, other.bottom) +private val DisplayCutout.boundingRectsLeftRightTop + get() = listOf(boundingRectLeft, boundingRectRight, boundingRectTop).filter { !it.isEmpty } + /* * Returns the inset top of the status bar. * -- cgit v1.2.3-59-g8ed1b