Merge changes I4e2c4584,I81919822,Ic3968b89 into tm-qpr-dev am: ec9b150a31

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

Change-Id: Ie10c937c4131974c1e6902867d6ba1a6c51436c0
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Caitlin Shkuratov
2022-11-15 22:31:56 +00:00
committed by Automerger Merge Worker
3 changed files with 397 additions and 32 deletions

View File

@@ -33,7 +33,7 @@ class ConfigurationControllerImpl @Inject constructor(context: Context) : Config
private val lastConfig = Configuration()
private var density: Int = 0
private var smallestScreenWidth: Int = 0
private var maxBounds: Rect? = null
private var maxBounds = Rect()
private var fontScale: Float = 0.toFloat()
private val inCarMode: Boolean
private var uiMode: Int = 0
@@ -47,6 +47,7 @@ class ConfigurationControllerImpl @Inject constructor(context: Context) : Config
fontScale = currentConfig.fontScale
density = currentConfig.densityDpi
smallestScreenWidth = currentConfig.smallestScreenWidthDp
maxBounds.set(currentConfig.windowConfiguration.maxBounds)
inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
Configuration.UI_MODE_TYPE_CAR
uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
@@ -92,7 +93,11 @@ class ConfigurationControllerImpl @Inject constructor(context: Context) : Config
val maxBounds = newConfig.windowConfiguration.maxBounds
if (maxBounds != this.maxBounds) {
this.maxBounds = maxBounds
// Update our internal rect to have the same bounds, instead of using
// `this.maxBounds = maxBounds` directly. Setting it directly means that `maxBounds`
// would be a direct reference to windowConfiguration.maxBounds, so the if statement
// above would always fail. See b/245799099 for more information.
this.maxBounds.set(maxBounds)
listeners.filterForEach({ this.listeners.contains(it) }) {
it.onMaxBoundsChanged()
}

View File

@@ -14,23 +14,37 @@
package com.android.systemui.statusbar.phone
import androidx.test.filters.SmallTest
import android.content.res.Configuration
import android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_LTR
import android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_RTL
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.content.res.Configuration.UI_MODE_TYPE_CAR
import android.os.LocaleList
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import java.util.Locale
@RunWith(AndroidTestingRunner::class)
@SmallTest
class ConfigurationControllerImplTest : SysuiTestCase() {
private val mConfigurationController =
com.android.systemui.statusbar.phone.ConfigurationControllerImpl(mContext)
private lateinit var mConfigurationController: ConfigurationControllerImpl
@Before
fun setUp() {
mConfigurationController = ConfigurationControllerImpl(mContext)
}
@Test
fun testThemeChange() {
@@ -57,4 +71,303 @@ class ConfigurationControllerImplTest : SysuiTestCase() {
verify(listener).onThemeChanged()
verify(listener2, never()).onThemeChanged()
}
@Test
fun configChanged_listenerNotified() {
val config = mContext.resources.configuration
config.densityDpi = 12
config.smallestScreenWidthDp = 240
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN the config is updated
config.densityDpi = 20
config.smallestScreenWidthDp = 300
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
assertThat(listener.changedConfig?.densityDpi).isEqualTo(20)
assertThat(listener.changedConfig?.smallestScreenWidthDp).isEqualTo(300)
}
@Test
fun densityChanged_listenerNotified() {
val config = mContext.resources.configuration
config.densityDpi = 12
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN the density is updated
config.densityDpi = 20
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
assertThat(listener.densityOrFontScaleChanged).isTrue()
}
@Test
fun fontChanged_listenerNotified() {
val config = mContext.resources.configuration
config.fontScale = 1.5f
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN the font is updated
config.fontScale = 1.4f
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
assertThat(listener.densityOrFontScaleChanged).isTrue()
}
@Test
fun isCarAndUiModeChanged_densityListenerNotified() {
val config = mContext.resources.configuration
config.uiMode = UI_MODE_TYPE_CAR or UI_MODE_NIGHT_YES
// Re-create the controller since we calculate car mode on creation
mConfigurationController = ConfigurationControllerImpl(mContext)
val listener = createAndAddListener()
// WHEN the ui mode is updated
config.uiMode = UI_MODE_TYPE_CAR or UI_MODE_NIGHT_NO
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
assertThat(listener.densityOrFontScaleChanged).isTrue()
}
@Test
fun isNotCarAndUiModeChanged_densityListenerNotNotified() {
val config = mContext.resources.configuration
config.uiMode = UI_MODE_NIGHT_YES
// Re-create the controller since we calculate car mode on creation
mConfigurationController = ConfigurationControllerImpl(mContext)
val listener = createAndAddListener()
// WHEN the ui mode is updated
config.uiMode = UI_MODE_NIGHT_NO
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is not notified because it's not car mode
assertThat(listener.densityOrFontScaleChanged).isFalse()
}
@Test
fun smallestScreenWidthChanged_listenerNotified() {
val config = mContext.resources.configuration
config.smallestScreenWidthDp = 240
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN the width is updated
config.smallestScreenWidthDp = 300
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
assertThat(listener.smallestScreenWidthChanged).isTrue()
}
@Test
fun maxBoundsChange_newConfigObject_listenerNotified() {
val config = mContext.resources.configuration
config.windowConfiguration.setMaxBounds(0, 0, 200, 200)
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN a new configuration object with new bounds is sent
val newConfig = Configuration()
newConfig.windowConfiguration.setMaxBounds(0, 0, 100, 100)
mConfigurationController.onConfigurationChanged(newConfig)
// THEN the listener is notified
assertThat(listener.maxBoundsChanged).isTrue()
}
// Regression test for b/245799099
@Test
fun maxBoundsChange_sameObject_listenerNotified() {
val config = mContext.resources.configuration
config.windowConfiguration.setMaxBounds(0, 0, 200, 200)
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN the existing config is updated with new bounds
config.windowConfiguration.setMaxBounds(0, 0, 100, 100)
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
assertThat(listener.maxBoundsChanged).isTrue()
}
@Test
fun localeListChanged_listenerNotified() {
val config = mContext.resources.configuration
config.locales = LocaleList(Locale.CANADA, Locale.GERMANY)
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN the locales are updated
config.locales = LocaleList(Locale.FRANCE, Locale.JAPAN, Locale.CHINESE)
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
assertThat(listener.localeListChanged).isTrue()
}
@Test
fun uiModeChanged_listenerNotified() {
val config = mContext.resources.configuration
config.uiMode = UI_MODE_NIGHT_YES
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN the ui mode is updated
config.uiMode = UI_MODE_NIGHT_NO
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
assertThat(listener.uiModeChanged).isTrue()
}
@Test
fun layoutDirectionUpdated_listenerNotified() {
val config = mContext.resources.configuration
config.screenLayout = SCREENLAYOUT_LAYOUTDIR_LTR
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN the layout is updated
config.screenLayout = SCREENLAYOUT_LAYOUTDIR_RTL
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
assertThat(listener.layoutDirectionChanged).isTrue()
}
@Test
fun assetPathsUpdated_listenerNotified() {
val config = mContext.resources.configuration
config.assetsSeq = 45
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN the assets sequence is updated
config.assetsSeq = 46
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified
assertThat(listener.themeChanged).isTrue()
}
@Test
fun multipleUpdates_listenerNotifiedOfAll() {
val config = mContext.resources.configuration
config.densityDpi = 14
config.windowConfiguration.setMaxBounds(0, 0, 2, 2)
config.uiMode = UI_MODE_NIGHT_YES
mConfigurationController.onConfigurationChanged(config)
val listener = createAndAddListener()
// WHEN multiple fields are updated
config.densityDpi = 20
config.windowConfiguration.setMaxBounds(0, 0, 3, 3)
config.uiMode = UI_MODE_NIGHT_NO
mConfigurationController.onConfigurationChanged(config)
// THEN the listener is notified of all of them
assertThat(listener.densityOrFontScaleChanged).isTrue()
assertThat(listener.maxBoundsChanged).isTrue()
assertThat(listener.uiModeChanged).isTrue()
}
@Test
fun equivalentConfigObject_listenerNotNotified() {
val config = mContext.resources.configuration
val listener = createAndAddListener()
// WHEN we update with the new object that has all the same fields
mConfigurationController.onConfigurationChanged(Configuration(config))
listener.assertNoMethodsCalled()
}
private fun createAndAddListener(): TestListener {
val listener = TestListener()
mConfigurationController.addCallback(listener)
// Adding a listener can trigger some callbacks, so we want to reset the values right
// after the listener is added
listener.reset()
return listener
}
private class TestListener : ConfigurationListener {
var changedConfig: Configuration? = null
var densityOrFontScaleChanged = false
var smallestScreenWidthChanged = false
var maxBoundsChanged = false
var uiModeChanged = false
var themeChanged = false
var localeListChanged = false
var layoutDirectionChanged = false
override fun onConfigChanged(newConfig: Configuration?) {
changedConfig = newConfig
}
override fun onDensityOrFontScaleChanged() {
densityOrFontScaleChanged = true
}
override fun onSmallestScreenWidthChanged() {
smallestScreenWidthChanged = true
}
override fun onMaxBoundsChanged() {
maxBoundsChanged = true
}
override fun onUiModeChanged() {
uiModeChanged = true
}
override fun onThemeChanged() {
themeChanged = true
}
override fun onLocaleListChanged() {
localeListChanged = true
}
override fun onLayoutDirectionChanged(isLayoutRtl: Boolean) {
layoutDirectionChanged = true
}
fun assertNoMethodsCalled() {
assertThat(densityOrFontScaleChanged).isFalse()
assertThat(smallestScreenWidthChanged).isFalse()
assertThat(maxBoundsChanged).isFalse()
assertThat(uiModeChanged).isFalse()
assertThat(themeChanged).isFalse()
assertThat(localeListChanged).isFalse()
assertThat(layoutDirectionChanged).isFalse()
}
fun reset() {
changedConfig = null
densityOrFontScaleChanged = false
smallestScreenWidthChanged = false
maxBoundsChanged = false
uiModeChanged = false
themeChanged = false
localeListChanged = false
layoutDirectionChanged = false
}
}
}

View File

@@ -19,9 +19,9 @@ 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 androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -463,16 +463,10 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
mock(DumpManager::class.java))
givenDisplay(
screenBounds = Rect(0, 0, 1080, 2160),
displayUniqueId = "1"
)
configuration.windowConfiguration.maxBounds = Rect(0, 0, 1080, 2160)
val firstDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
givenDisplay(
screenBounds = Rect(0, 0, 800, 600),
displayUniqueId = "2"
)
configurationController.onConfigurationChanged(configuration)
configuration.windowConfiguration.maxBounds = Rect(0, 0, 800, 600)
// WHEN: get insets on the second display
val secondDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
@@ -487,23 +481,15 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
// get insets and switch back
val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
mock(DumpManager::class.java))
givenDisplay(
screenBounds = Rect(0, 0, 1080, 2160),
displayUniqueId = "1"
)
configuration.windowConfiguration.maxBounds = Rect(0, 0, 1080, 2160)
val firstDisplayInsetsFirstCall = provider
.getStatusBarContentAreaForRotation(ROTATION_NONE)
givenDisplay(
screenBounds = Rect(0, 0, 800, 600),
displayUniqueId = "2"
)
configurationController.onConfigurationChanged(configuration)
configuration.windowConfiguration.maxBounds = Rect(0, 0, 800, 600)
provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
givenDisplay(
screenBounds = Rect(0, 0, 1080, 2160),
displayUniqueId = "1"
)
configurationController.onConfigurationChanged(configuration)
configuration.windowConfiguration.maxBounds = Rect(0, 0, 1080, 2160)
// WHEN: get insets on the first display again
val firstDisplayInsetsSecondCall = provider
@@ -513,9 +499,70 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
assertThat(firstDisplayInsetsFirstCall).isEqualTo(firstDisplayInsetsSecondCall)
}
private fun givenDisplay(screenBounds: Rect, displayUniqueId: String) {
`when`(display.uniqueId).thenReturn(displayUniqueId)
configuration.windowConfiguration.maxBounds = screenBounds
// Regression test for b/245799099
@Test
fun onMaxBoundsChanged_listenerNotified() {
// Start out with an existing configuration with bounds
configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100)
configurationController.onConfigurationChanged(configuration)
val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
mock(DumpManager::class.java))
val listener = object : StatusBarContentInsetsChangedListener {
var triggered = false
override fun onStatusBarContentInsetsChanged() {
triggered = true
}
}
provider.addCallback(listener)
// WHEN the config is updated with new bounds
configuration.windowConfiguration.setMaxBounds(0, 0, 456, 789)
configurationController.onConfigurationChanged(configuration)
// THEN the listener is notified
assertThat(listener.triggered).isTrue()
}
@Test
fun onDensityOrFontScaleChanged_listenerNotified() {
configuration.densityDpi = 12
val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
mock(DumpManager::class.java))
val listener = object : StatusBarContentInsetsChangedListener {
var triggered = false
override fun onStatusBarContentInsetsChanged() {
triggered = true
}
}
provider.addCallback(listener)
// WHEN the config is updated
configuration.densityDpi = 20
configurationController.onConfigurationChanged(configuration)
// THEN the listener is notified
assertThat(listener.triggered).isTrue()
}
@Test
fun onThemeChanged_listenerNotified() {
val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
mock(DumpManager::class.java))
val listener = object : StatusBarContentInsetsChangedListener {
var triggered = false
override fun onStatusBarContentInsetsChanged() {
triggered = true
}
}
provider.addCallback(listener)
configurationController.notifyThemeChanged()
// THEN the listener is notified
assertThat(listener.triggered).isTrue()
}
private fun assertRects(