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

Commit ec9b150a authored by Caitlin Shkuratov's avatar Caitlin Shkuratov Committed by Android (Google) Code Review
Browse files

Merge changes I4e2c4584,I81919822,Ic3968b89 into tm-qpr-dev

* changes:
  Write tests for all of ConfigurationControllerImpl.
  [Privacy Chip] Minor updates to StatusBarContentInsetsProviderTest
  Update ConfigurationController to not store maxBounds by reference.
parents 6fe41592 71448516
Loading
Loading
Loading
Loading
+7 −2
Original line number Original line Diff line number Diff line
@@ -33,7 +33,7 @@ class ConfigurationControllerImpl @Inject constructor(context: Context) : Config
    private val lastConfig = Configuration()
    private val lastConfig = Configuration()
    private var density: Int = 0
    private var density: Int = 0
    private var smallestScreenWidth: Int = 0
    private var smallestScreenWidth: Int = 0
    private var maxBounds: Rect? = null
    private var maxBounds = Rect()
    private var fontScale: Float = 0.toFloat()
    private var fontScale: Float = 0.toFloat()
    private val inCarMode: Boolean
    private val inCarMode: Boolean
    private var uiMode: Int = 0
    private var uiMode: Int = 0
@@ -47,6 +47,7 @@ class ConfigurationControllerImpl @Inject constructor(context: Context) : Config
        fontScale = currentConfig.fontScale
        fontScale = currentConfig.fontScale
        density = currentConfig.densityDpi
        density = currentConfig.densityDpi
        smallestScreenWidth = currentConfig.smallestScreenWidthDp
        smallestScreenWidth = currentConfig.smallestScreenWidthDp
        maxBounds.set(currentConfig.windowConfiguration.maxBounds)
        inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
        inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
                Configuration.UI_MODE_TYPE_CAR
                Configuration.UI_MODE_TYPE_CAR
        uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
        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
        val maxBounds = newConfig.windowConfiguration.maxBounds
        if (maxBounds != this.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) }) {
            listeners.filterForEach({ this.listeners.contains(it) }) {
                it.onMaxBoundsChanged()
                it.onMaxBoundsChanged()
            }
            }
+316 −3
Original line number Original line Diff line number Diff line
@@ -14,23 +14,37 @@


package com.android.systemui.statusbar.phone
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 android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
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.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verify
import java.util.Locale


@RunWith(AndroidTestingRunner::class)
@RunWith(AndroidTestingRunner::class)
@SmallTest
@SmallTest
class ConfigurationControllerImplTest : SysuiTestCase() {
class ConfigurationControllerImplTest : SysuiTestCase() {


    private val mConfigurationController =
    private lateinit var mConfigurationController: ConfigurationControllerImpl
            com.android.systemui.statusbar.phone.ConfigurationControllerImpl(mContext)

    @Before
    fun setUp() {
        mConfigurationController = ConfigurationControllerImpl(mContext)
    }


    @Test
    @Test
    fun testThemeChange() {
    fun testThemeChange() {
@@ -57,4 +71,303 @@ class ConfigurationControllerImplTest : SysuiTestCase() {
        verify(listener).onThemeChanged()
        verify(listener).onThemeChanged()
        verify(listener2, never()).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
        }
    }
}
}
+74 −27
Original line number Original line Diff line number Diff line
@@ -19,9 +19,9 @@ package com.android.systemui.statusbar.phone
import android.content.Context
import android.content.Context
import android.content.res.Configuration
import android.content.res.Configuration
import android.graphics.Rect
import android.graphics.Rect
import android.test.suitebuilder.annotation.SmallTest
import android.view.Display
import android.view.Display
import android.view.DisplayCutout
import android.view.DisplayCutout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -463,16 +463,10 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
        val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
        val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
            mock(DumpManager::class.java))
            mock(DumpManager::class.java))


        givenDisplay(
        configuration.windowConfiguration.maxBounds = Rect(0, 0, 1080, 2160)
            screenBounds = Rect(0, 0, 1080, 2160),
            displayUniqueId = "1"
        )
        val firstDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
        val firstDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
        givenDisplay(

            screenBounds = Rect(0, 0, 800, 600),
        configuration.windowConfiguration.maxBounds = Rect(0, 0, 800, 600)
            displayUniqueId = "2"
        )
        configurationController.onConfigurationChanged(configuration)


        // WHEN: get insets on the second display
        // WHEN: get insets on the second display
        val secondDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
        val secondDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
@@ -487,23 +481,15 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
        // get insets and switch back
        // get insets and switch back
        val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
        val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
            mock(DumpManager::class.java))
            mock(DumpManager::class.java))
        givenDisplay(

            screenBounds = Rect(0, 0, 1080, 2160),
        configuration.windowConfiguration.maxBounds = Rect(0, 0, 1080, 2160)
            displayUniqueId = "1"
        )
        val firstDisplayInsetsFirstCall = provider
        val firstDisplayInsetsFirstCall = provider
            .getStatusBarContentAreaForRotation(ROTATION_NONE)
            .getStatusBarContentAreaForRotation(ROTATION_NONE)
        givenDisplay(

            screenBounds = Rect(0, 0, 800, 600),
        configuration.windowConfiguration.maxBounds = Rect(0, 0, 800, 600)
            displayUniqueId = "2"
        )
        configurationController.onConfigurationChanged(configuration)
        provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
        provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
        givenDisplay(

            screenBounds = Rect(0, 0, 1080, 2160),
        configuration.windowConfiguration.maxBounds = Rect(0, 0, 1080, 2160)
            displayUniqueId = "1"
        )
        configurationController.onConfigurationChanged(configuration)


        // WHEN: get insets on the first display again
        // WHEN: get insets on the first display again
        val firstDisplayInsetsSecondCall = provider
        val firstDisplayInsetsSecondCall = provider
@@ -513,9 +499,70 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
        assertThat(firstDisplayInsetsFirstCall).isEqualTo(firstDisplayInsetsSecondCall)
        assertThat(firstDisplayInsetsFirstCall).isEqualTo(firstDisplayInsetsSecondCall)
    }
    }


    private fun givenDisplay(screenBounds: Rect, displayUniqueId: String) {
    // Regression test for b/245799099
        `when`(display.uniqueId).thenReturn(displayUniqueId)
    @Test
        configuration.windowConfiguration.maxBounds = screenBounds
    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(
    private fun assertRects(