Merge "Update statusbar insets when switching displays" into sc-v2-dev

This commit is contained in:
Nick Chameyev
2021-08-26 12:40:04 +00:00
committed by Android (Google) Code Review
4 changed files with 155 additions and 65 deletions

View File

@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Rect
import android.os.LocaleList
import android.view.View.LAYOUT_DIRECTION_RTL
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -29,6 +30,7 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController {
private val lastConfig = Configuration()
private var density: Int = 0
private var smallestScreenWidth: Int = 0
private var maxBounds: Rect? = null
private var fontScale: Float = 0.toFloat()
private val inCarMode: Boolean
private var uiMode: Int = 0
@@ -85,6 +87,14 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController {
}
}
val maxBounds = newConfig.windowConfiguration.maxBounds
if (maxBounds != this.maxBounds) {
this.maxBounds = maxBounds
listeners.filterForEach({ this.listeners.contains(it) }) {
it.onMaxBoundsChanged()
}
}
val localeList = newConfig.locales
if (localeList != this.localeList) {
this.localeList = localeList

View File

@@ -19,11 +19,10 @@ package com.android.systemui.statusbar.phone
import android.content.Context
import android.content.res.Resources
import android.graphics.Rect
import android.util.Log
import android.util.LruCache
import android.util.Pair
import android.view.DisplayCutout
import android.view.View.LAYOUT_DIRECTION_RTL
import android.view.WindowManager
import android.view.WindowMetrics
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
@@ -38,6 +37,7 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import com.android.systemui.util.leak.RotationUtils.Rotation
import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
import java.io.FileDescriptor
import java.io.PrintWriter
import java.lang.Math.max
@@ -61,13 +61,14 @@ import javax.inject.Inject
class StatusBarContentInsetsProvider @Inject constructor(
val context: Context,
val configurationController: ConfigurationController,
val windowManager: WindowManager,
val dumpManager: DumpManager
) : CallbackController<StatusBarContentInsetsChangedListener>,
ConfigurationController.ConfigurationListener,
Dumpable {
// Indexed by @Rotation
private val insetsByCorner = arrayOfNulls<Rect>(4)
// Limit cache size as potentially we may connect large number of displays
// (e.g. network displays)
private val insetsCache = LruCache<CacheKey, Rect>(MAX_CACHE_SIZE)
private val listeners = mutableSetOf<StatusBarContentInsetsChangedListener>()
init {
@@ -91,12 +92,12 @@ class StatusBarContentInsetsProvider @Inject constructor(
clearCachedInsets()
}
private fun clearCachedInsets() {
insetsByCorner[0] = null
insetsByCorner[1] = null
insetsByCorner[2] = null
insetsByCorner[3] = null
override fun onMaxBoundsChanged() {
notifyInsetsChanged()
}
private fun clearCachedInsets() {
insetsCache.evictAll()
notifyInsetsChanged()
}
@@ -111,10 +112,10 @@ class StatusBarContentInsetsProvider @Inject constructor(
* dot in the coordinates relative to the given rotation.
*/
fun getBoundingRectForPrivacyChipForRotation(@Rotation rotation: Int): Rect {
var insets = insetsByCorner[rotation]
val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
var insets = insetsCache[getCacheKey(rotation = rotation)]
val rotatedResources = getResourcesForRotation(rotation, context)
if (insets == null) {
insets = getAndSetInsetsForRotation(rotation, rotatedResources)
insets = getStatusBarContentInsetsForRotation(rotation, rotatedResources)
}
val dotWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter)
@@ -129,24 +130,16 @@ class StatusBarContentInsetsProvider @Inject constructor(
* Calculates the necessary left and right locations for the status bar contents invariant of
* the current device rotation, in the target rotation's coordinates
*/
fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Rect {
var insets = insetsByCorner[rotation]
if (insets == null) {
val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
insets = getAndSetInsetsForRotation(rotation, rotatedResources)
}
return insets
}
private fun getAndSetInsetsForRotation(
@Rotation rot: Int,
rotatedResources: Resources
@JvmOverloads
fun getStatusBarContentInsetsForRotation(
@Rotation rotation: Int,
rotatedResources: Resources = getResourcesForRotation(rotation, context)
): Rect {
val insets = getCalculatedInsetsForRotation(rot, rotatedResources)
insetsByCorner[rot] = insets
return insets
val key = getCacheKey(rotation = rotation)
return insetsCache[key] ?: getCalculatedInsetsForRotation(rotation, rotatedResources)
.also {
insetsCache.put(key, it)
}
}
private fun getCalculatedInsetsForRotation(
@@ -176,17 +169,29 @@ class StatusBarContentInsetsProvider @Inject constructor(
currentRotation,
targetRotation,
dc,
windowManager.maximumWindowMetrics,
context.resources.configuration.windowConfiguration.maxBounds,
rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height),
minLeft,
minRight)
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
insetsByCorner.forEachIndexed { index, rect ->
pw.println("${RotationUtils.toString(index)} -> $rect")
insetsCache.snapshot().forEach { (key, rect) ->
pw.println("$key -> $rect")
}
pw.println(insetsCache)
}
private fun getCacheKey(@Rotation rotation: Int): CacheKey =
CacheKey(
uniqueDisplayId = context.display.uniqueId,
rotation = rotation
)
private data class CacheKey(
val uniqueDisplayId: String,
@Rotation val rotation: Int
)
}
interface StatusBarContentInsetsChangedListener {
@@ -194,10 +199,9 @@ interface StatusBarContentInsetsChangedListener {
}
private const val TAG = "StatusBarInsetsProvider"
private const val MAX_CACHE_SIZE = 16
private fun getRotationZeroDisplayBounds(wm: WindowMetrics, @Rotation exactRotation: Int): Rect {
val bounds = wm.bounds
private fun getRotationZeroDisplayBounds(bounds: Rect, @Rotation exactRotation: Int): Rect {
if (exactRotation == ROTATION_NONE || exactRotation == ROTATION_UPSIDE_DOWN) {
return bounds
}
@@ -243,7 +247,7 @@ fun calculateInsetsForRotationWithRotatedResources(
@Rotation currentRotation: Int,
@Rotation targetRotation: Int,
displayCutout: DisplayCutout?,
windowMetrics: WindowMetrics,
maxBounds: Rect,
statusBarHeight: Int,
minLeft: Int,
minRight: Int
@@ -254,16 +258,15 @@ fun calculateInsetsForRotationWithRotatedResources(
val right = if (isRtl) paddingStart else paddingEnd
*/
val rotZeroBounds = getRotationZeroDisplayBounds(windowMetrics, currentRotation)
val currentBounds = windowMetrics.bounds
val rotZeroBounds = getRotationZeroDisplayBounds(maxBounds, currentRotation)
val sbLeftRight = getStatusBarLeftRight(
displayCutout,
statusBarHeight,
rotZeroBounds.right,
rotZeroBounds.bottom,
currentBounds.width(),
currentBounds.height(),
maxBounds.width(),
maxBounds.height(),
minLeft,
minRight,
targetRotation,

View File

@@ -37,6 +37,7 @@ public interface ConfigurationController extends CallbackController<Configuratio
default void onConfigChanged(Configuration newConfig) {}
default void onDensityOrFontScaleChanged() {}
default void onSmallestScreenWidthChanged() {}
default void onMaxBoundsChanged() {}
default void onOverlayChanged() {}
default void onUiModeChanged() {}
default void onThemeChanged() {}

View File

@@ -16,32 +16,54 @@
package com.android.systemui.statusbar.phone
import android.content.Context
import android.content.res.Configuration
import android.graphics.Rect
import android.test.suitebuilder.annotation.SmallTest
import android.view.Display
import android.view.DisplayCutout
import android.view.WindowMetrics
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.leak.RotationUtils
import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import com.android.systemui.util.leak.RotationUtils.Rotation
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@SmallTest
class StatusBarContentInsetsProviderTest : SysuiTestCase() {
@Mock private lateinit var dc: DisplayCutout
@Mock private lateinit var windowMetrics: WindowMetrics
@Mock private lateinit var contextMock: Context
@Mock private lateinit var display: Display
private lateinit var configurationController: ConfigurationController
private val configuration = Configuration()
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
`when`(contextMock.display).thenReturn(display)
context.ensureTestableResources()
`when`(contextMock.resources).thenReturn(context.resources)
`when`(contextMock.resources.configuration).thenReturn(configuration)
`when`(contextMock.createConfigurationContext(any())).thenAnswer {
context.createConfigurationContext(it.arguments[0] as Configuration)
}
configurationController = ConfigurationControllerImpl(contextMock)
}
@Test
@@ -55,15 +77,13 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val chipWidth = 30
val dotWidth = 10
`when`(windowMetrics.bounds).thenReturn(screenBounds)
var isRtl = false
var targetRotation = ROTATION_NONE
var bounds = calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null,
windowMetrics,
screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -92,7 +112,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
dc,
windowMetrics,
screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -127,7 +147,6 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
`when`(windowMetrics.bounds).thenReturn(screenBounds)
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
// THEN rotations which share a short side should use the greater value between rounded
@@ -142,7 +161,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
dc,
windowMetrics,
screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -159,7 +178,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
dc,
windowMetrics,
screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -178,7 +197,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
dc,
windowMetrics,
screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -196,7 +215,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
dc,
windowMetrics,
screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -219,7 +238,6 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
`when`(windowMetrics.bounds).thenReturn(screenBounds)
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
// THEN only the landscape/seascape rotations should avoid the cutout area because of the
@@ -234,7 +252,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
dc,
windowMetrics,
screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -251,7 +269,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
dc,
windowMetrics,
screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -268,7 +286,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
dc,
windowMetrics,
screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -285,7 +303,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
dc,
windowMetrics,
screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -303,8 +321,6 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val sbHeightPortrait = 100
val sbHeightLandscape = 60
`when`(windowMetrics.bounds).thenReturn(screenBounds)
// THEN content insets should only use rounded corner padding
var targetRotation = ROTATION_NONE
var expectedBounds = Rect(minLeftPadding,
@@ -316,7 +332,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
null, /* no cutout */
windowMetrics,
screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -332,7 +348,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
null, /* no cutout */
windowMetrics,
screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -348,7 +364,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
null, /* no cutout */
windowMetrics,
screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -364,7 +380,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
null, /* no cutout */
windowMetrics,
screenBounds,
sbHeightLandscape,
minLeftPadding,
minRightPadding)
@@ -382,7 +398,6 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val sbHeightLandscape = 60
val currentRotation = ROTATION_NONE
`when`(windowMetrics.bounds).thenReturn(screenBounds)
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
// THEN left should be set to the display cutout width, and right should use the minRight
@@ -396,7 +411,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
currentRotation,
targetRotation,
dc,
windowMetrics,
screenBounds,
sbHeightPortrait,
minLeftPadding,
minRightPadding)
@@ -404,6 +419,67 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@Test
fun testDisplayChanged_returnsUpdatedInsets() {
// GIVEN: get insets on the first display and switch to the second display
val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
mock(DumpManager::class.java))
givenDisplay(
screenBounds = Rect(0, 0, 1080, 2160),
displayUniqueId = "1"
)
val firstDisplayInsets = provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
givenDisplay(
screenBounds = Rect(0, 0, 800, 600),
displayUniqueId = "2"
)
configurationController.onConfigurationChanged(configuration)
// WHEN: get insets on the second display
val secondDisplayInsets = provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
// THEN: insets are updated
assertThat(firstDisplayInsets).isNotEqualTo(secondDisplayInsets)
}
@Test
fun testDisplayChangedAndReturnedBack_returnsTheSameInsets() {
// GIVEN: get insets on the first display, switch to the second display,
// get insets and switch back
val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
mock(DumpManager::class.java))
givenDisplay(
screenBounds = Rect(0, 0, 1080, 2160),
displayUniqueId = "1"
)
val firstDisplayInsetsFirstCall = provider
.getStatusBarContentInsetsForRotation(ROTATION_NONE)
givenDisplay(
screenBounds = Rect(0, 0, 800, 600),
displayUniqueId = "2"
)
configurationController.onConfigurationChanged(configuration)
provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
givenDisplay(
screenBounds = Rect(0, 0, 1080, 2160),
displayUniqueId = "1"
)
configurationController.onConfigurationChanged(configuration)
// WHEN: get insets on the first display again
val firstDisplayInsetsSecondCall = provider
.getStatusBarContentInsetsForRotation(ROTATION_NONE)
// THEN: insets for the first and second calls for the first display are the same
assertThat(firstDisplayInsetsFirstCall).isEqualTo(firstDisplayInsetsSecondCall)
}
private fun givenDisplay(screenBounds: Rect, displayUniqueId: String) {
`when`(display.uniqueId).thenReturn(displayUniqueId)
configuration.windowConfiguration.maxBounds = screenBounds
}
private fun assertRects(
expected: Rect,
actual: Rect,