• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.statusbar.phone
16 
17 import android.content.res.Configuration
18 import android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_LTR
19 import android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_RTL
20 import android.content.res.Configuration.UI_MODE_NIGHT_NO
21 import android.content.res.Configuration.UI_MODE_NIGHT_YES
22 import android.content.res.Configuration.UI_MODE_TYPE_CAR
23 import android.os.LocaleList
24 import android.testing.AndroidTestingRunner
25 import androidx.test.filters.SmallTest
26 import com.android.systemui.SysuiTestCase
27 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
28 import com.google.common.truth.Truth.assertThat
29 import org.junit.Before
30 import org.junit.Ignore
31 import org.junit.Test
32 import org.junit.runner.RunWith
33 import org.mockito.Mockito.doAnswer
34 import org.mockito.Mockito.mock
35 import org.mockito.Mockito.never
36 import org.mockito.Mockito.verify
37 import java.util.Locale
38 
39 @RunWith(AndroidTestingRunner::class)
40 @SmallTest
41 class ConfigurationControllerImplTest : SysuiTestCase() {
42 
43     private lateinit var mConfigurationController: ConfigurationControllerImpl
44 
45     @Before
setUpnull46     fun setUp() {
47         mConfigurationController = ConfigurationControllerImpl(mContext)
48     }
49 
50     @Test
testThemeChangenull51     fun testThemeChange() {
52         val listener = mock(ConfigurationListener::class.java)
53         mConfigurationController.addCallback(listener)
54 
55         mConfigurationController.notifyThemeChanged()
56         verify(listener).onThemeChanged()
57     }
58 
59     @Test
testRemoveListenerDuringCallbacknull60     fun testRemoveListenerDuringCallback() {
61         val listener = mock(ConfigurationListener::class.java)
62         mConfigurationController.addCallback(listener)
63         val listener2 = mock(ConfigurationListener::class.java)
64         mConfigurationController.addCallback(listener2)
65 
66         doAnswer {
67             mConfigurationController.removeCallback(listener2)
68             null
69         }.`when`(listener).onThemeChanged()
70 
71         mConfigurationController.notifyThemeChanged()
72         verify(listener).onThemeChanged()
73         verify(listener2, never()).onThemeChanged()
74     }
75 
76     @Test
configChanged_listenerNotifiednull77     fun configChanged_listenerNotified() {
78         val config = mContext.resources.configuration
79         config.densityDpi = 12
80         config.smallestScreenWidthDp = 240
81         mConfigurationController.onConfigurationChanged(config)
82 
83         val listener = createAndAddListener()
84 
85         // WHEN the config is updated
86         config.densityDpi = 20
87         config.smallestScreenWidthDp = 300
88         mConfigurationController.onConfigurationChanged(config)
89 
90         // THEN the listener is notified
91         assertThat(listener.changedConfig?.densityDpi).isEqualTo(20)
92         assertThat(listener.changedConfig?.smallestScreenWidthDp).isEqualTo(300)
93     }
94 
95     @Test
densityChanged_listenerNotifiednull96     fun densityChanged_listenerNotified() {
97         val config = mContext.resources.configuration
98         config.densityDpi = 12
99         mConfigurationController.onConfigurationChanged(config)
100 
101         val listener = createAndAddListener()
102 
103         // WHEN the density is updated
104         config.densityDpi = 20
105         mConfigurationController.onConfigurationChanged(config)
106 
107         // THEN the listener is notified
108         assertThat(listener.densityOrFontScaleChanged).isTrue()
109     }
110 
111     @Test
fontChanged_listenerNotifiednull112     fun fontChanged_listenerNotified() {
113         val config = mContext.resources.configuration
114         config.fontScale = 1.5f
115         mConfigurationController.onConfigurationChanged(config)
116 
117         val listener = createAndAddListener()
118 
119         // WHEN the font is updated
120         config.fontScale = 1.4f
121         mConfigurationController.onConfigurationChanged(config)
122 
123         // THEN the listener is notified
124         assertThat(listener.densityOrFontScaleChanged).isTrue()
125     }
126 
127     @Test
isCarAndUiModeChanged_densityListenerNotifiednull128     fun isCarAndUiModeChanged_densityListenerNotified() {
129         val config = mContext.resources.configuration
130         config.uiMode = UI_MODE_TYPE_CAR or UI_MODE_NIGHT_YES
131         // Re-create the controller since we calculate car mode on creation
132         mConfigurationController = ConfigurationControllerImpl(mContext)
133 
134         val listener = createAndAddListener()
135 
136         // WHEN the ui mode is updated
137         config.uiMode = UI_MODE_TYPE_CAR or UI_MODE_NIGHT_NO
138         mConfigurationController.onConfigurationChanged(config)
139 
140         // THEN the listener is notified
141         assertThat(listener.densityOrFontScaleChanged).isTrue()
142     }
143 
144     @Test
isNotCarAndUiModeChanged_densityListenerNotNotifiednull145     fun isNotCarAndUiModeChanged_densityListenerNotNotified() {
146         val config = mContext.resources.configuration
147         config.uiMode = UI_MODE_NIGHT_YES
148         // Re-create the controller since we calculate car mode on creation
149         mConfigurationController = ConfigurationControllerImpl(mContext)
150 
151         val listener = createAndAddListener()
152 
153         // WHEN the ui mode is updated
154         config.uiMode = UI_MODE_NIGHT_NO
155         mConfigurationController.onConfigurationChanged(config)
156 
157         // THEN the listener is not notified because it's not car mode
158         assertThat(listener.densityOrFontScaleChanged).isFalse()
159     }
160 
161     @Test
smallestScreenWidthChanged_listenerNotifiednull162     fun smallestScreenWidthChanged_listenerNotified() {
163         val config = mContext.resources.configuration
164         config.smallestScreenWidthDp = 240
165         mConfigurationController.onConfigurationChanged(config)
166 
167         val listener = createAndAddListener()
168 
169         // WHEN the width is updated
170         config.smallestScreenWidthDp = 300
171         mConfigurationController.onConfigurationChanged(config)
172 
173         // THEN the listener is notified
174         assertThat(listener.smallestScreenWidthChanged).isTrue()
175     }
176 
177     @Test
maxBoundsChange_newConfigObject_listenerNotifiednull178     fun maxBoundsChange_newConfigObject_listenerNotified() {
179         val config = mContext.resources.configuration
180         config.windowConfiguration.setMaxBounds(0, 0, 200, 200)
181         mConfigurationController.onConfigurationChanged(config)
182 
183         val listener = createAndAddListener()
184 
185         // WHEN a new configuration object with new bounds is sent
186         val newConfig = Configuration()
187         newConfig.windowConfiguration.setMaxBounds(0, 0, 100, 100)
188         mConfigurationController.onConfigurationChanged(newConfig)
189 
190         // THEN the listener is notified
191         assertThat(listener.maxBoundsChanged).isTrue()
192     }
193 
194     // Regression test for b/245799099
195     @Test
maxBoundsChange_sameObject_listenerNotifiednull196     fun maxBoundsChange_sameObject_listenerNotified() {
197         val config = mContext.resources.configuration
198         config.windowConfiguration.setMaxBounds(0, 0, 200, 200)
199         mConfigurationController.onConfigurationChanged(config)
200 
201         val listener = createAndAddListener()
202 
203         // WHEN the existing config is updated with new bounds
204         config.windowConfiguration.setMaxBounds(0, 0, 100, 100)
205         mConfigurationController.onConfigurationChanged(config)
206 
207         // THEN the listener is notified
208         assertThat(listener.maxBoundsChanged).isTrue()
209     }
210 
211 
212     @Test
localeListChanged_listenerNotifiednull213     fun localeListChanged_listenerNotified() {
214         val config = mContext.resources.configuration
215         config.locales = LocaleList(Locale.CANADA, Locale.GERMANY)
216         mConfigurationController.onConfigurationChanged(config)
217 
218         val listener = createAndAddListener()
219 
220         // WHEN the locales are updated
221         config.locales = LocaleList(Locale.FRANCE, Locale.JAPAN, Locale.CHINESE)
222         mConfigurationController.onConfigurationChanged(config)
223 
224         // THEN the listener is notified
225         assertThat(listener.localeListChanged).isTrue()
226     }
227 
228     @Test
uiModeChanged_listenerNotifiednull229     fun uiModeChanged_listenerNotified() {
230         val config = mContext.resources.configuration
231         config.uiMode = UI_MODE_NIGHT_YES
232         mConfigurationController.onConfigurationChanged(config)
233 
234         val listener = createAndAddListener()
235 
236         // WHEN the ui mode is updated
237         config.uiMode = UI_MODE_NIGHT_NO
238         mConfigurationController.onConfigurationChanged(config)
239 
240         // THEN the listener is notified
241         assertThat(listener.uiModeChanged).isTrue()
242     }
243 
244     @Test
layoutDirectionUpdated_listenerNotifiednull245     fun layoutDirectionUpdated_listenerNotified() {
246         val config = mContext.resources.configuration
247         config.screenLayout = SCREENLAYOUT_LAYOUTDIR_LTR
248         mConfigurationController.onConfigurationChanged(config)
249 
250         val listener = createAndAddListener()
251 
252         // WHEN the layout is updated
253         config.screenLayout = SCREENLAYOUT_LAYOUTDIR_RTL
254         mConfigurationController.onConfigurationChanged(config)
255 
256         // THEN the listener is notified
257         assertThat(listener.layoutDirectionChanged).isTrue()
258     }
259 
260     @Test
assetPathsUpdated_listenerNotifiednull261     fun assetPathsUpdated_listenerNotified() {
262         val config = mContext.resources.configuration
263         config.assetsSeq = 45
264         mConfigurationController.onConfigurationChanged(config)
265 
266         val listener = createAndAddListener()
267 
268         // WHEN the assets sequence is updated
269         config.assetsSeq = 46
270         mConfigurationController.onConfigurationChanged(config)
271 
272         // THEN the listener is notified
273         assertThat(listener.themeChanged).isTrue()
274     }
275 
276     @Test
multipleUpdates_listenerNotifiedOfAllnull277     fun multipleUpdates_listenerNotifiedOfAll() {
278         val config = mContext.resources.configuration
279         config.densityDpi = 14
280         config.windowConfiguration.setMaxBounds(0, 0, 2, 2)
281         config.uiMode = UI_MODE_NIGHT_YES
282         mConfigurationController.onConfigurationChanged(config)
283 
284         val listener = createAndAddListener()
285 
286         // WHEN multiple fields are updated
287         config.densityDpi = 20
288         config.windowConfiguration.setMaxBounds(0, 0, 3, 3)
289         config.uiMode = UI_MODE_NIGHT_NO
290         mConfigurationController.onConfigurationChanged(config)
291 
292         // THEN the listener is notified of all of them
293         assertThat(listener.densityOrFontScaleChanged).isTrue()
294         assertThat(listener.maxBoundsChanged).isTrue()
295         assertThat(listener.uiModeChanged).isTrue()
296     }
297 
298     @Test
299     @Ignore("b/261408895")
equivalentConfigObject_listenerNotNotifiednull300     fun equivalentConfigObject_listenerNotNotified() {
301         val config = mContext.resources.configuration
302         val listener = createAndAddListener()
303 
304         // WHEN we update with the new object that has all the same fields
305         mConfigurationController.onConfigurationChanged(Configuration(config))
306 
307         listener.assertNoMethodsCalled()
308     }
309 
createAndAddListenernull310     private fun createAndAddListener(): TestListener {
311         val listener = TestListener()
312         mConfigurationController.addCallback(listener)
313         // Adding a listener can trigger some callbacks, so we want to reset the values right
314         // after the listener is added
315         listener.reset()
316         return listener
317     }
318 
319     private class TestListener : ConfigurationListener {
320         var changedConfig: Configuration? = null
321         var densityOrFontScaleChanged = false
322         var smallestScreenWidthChanged = false
323         var maxBoundsChanged = false
324         var uiModeChanged = false
325         var themeChanged = false
326         var localeListChanged = false
327         var layoutDirectionChanged = false
328 
onConfigChangednull329         override fun onConfigChanged(newConfig: Configuration?) {
330             changedConfig = newConfig
331         }
onDensityOrFontScaleChangednull332         override fun onDensityOrFontScaleChanged() {
333             densityOrFontScaleChanged = true
334         }
onSmallestScreenWidthChangednull335         override fun onSmallestScreenWidthChanged() {
336             smallestScreenWidthChanged = true
337         }
onMaxBoundsChangednull338         override fun onMaxBoundsChanged() {
339             maxBoundsChanged = true
340         }
onUiModeChangednull341         override fun onUiModeChanged() {
342             uiModeChanged = true
343         }
onThemeChangednull344         override fun onThemeChanged() {
345             themeChanged = true
346         }
onLocaleListChangednull347         override fun onLocaleListChanged() {
348             localeListChanged = true
349         }
onLayoutDirectionChangednull350         override fun onLayoutDirectionChanged(isLayoutRtl: Boolean) {
351             layoutDirectionChanged = true
352         }
353 
assertNoMethodsCallednull354         fun assertNoMethodsCalled() {
355             assertThat(densityOrFontScaleChanged).isFalse()
356             assertThat(smallestScreenWidthChanged).isFalse()
357             assertThat(maxBoundsChanged).isFalse()
358             assertThat(uiModeChanged).isFalse()
359             assertThat(themeChanged).isFalse()
360             assertThat(localeListChanged).isFalse()
361             assertThat(layoutDirectionChanged).isFalse()
362         }
363 
resetnull364         fun reset() {
365             changedConfig = null
366             densityOrFontScaleChanged = false
367             smallestScreenWidthChanged = false
368             maxBoundsChanged = false
369             uiModeChanged = false
370             themeChanged = false
371             localeListChanged = false
372             layoutDirectionChanged = false
373         }
374     }
375 }
376