• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 @file:Suppress("DEPRECATION")
18 
19 package com.google.accompanist.systemuicontroller
20 
21 import android.os.Build
22 import android.view.View
23 import android.view.Window
24 import androidx.activity.ComponentActivity
25 import androidx.compose.ui.graphics.Color
26 import androidx.core.view.ViewCompat
27 import androidx.core.view.WindowCompat
28 import androidx.core.view.WindowInsetsCompat
29 import androidx.core.view.WindowInsetsControllerCompat
30 import androidx.test.ext.junit.rules.ActivityScenarioRule
31 import androidx.test.ext.junit.runners.AndroidJUnit4
32 import androidx.test.filters.FlakyTest
33 import androidx.test.filters.SdkSuppress
34 import com.google.accompanist.internal.test.IgnoreOnRobolectric
35 import com.google.accompanist.internal.test.waitUntil
36 import com.google.accompanist.internal.test.withActivity
37 import com.google.common.truth.Truth.assertThat
38 import org.junit.Before
39 import org.junit.Rule
40 import org.junit.Test
41 import org.junit.experimental.categories.Category
42 import org.junit.runner.RunWith
43 
44 @RunWith(AndroidJUnit4::class)
45 class ActivitySystemUiControllerTest {
46     @get:Rule
47     val rule = ActivityScenarioRule(ComponentActivity::class.java)
48 
49     private lateinit var window: Window
50     private lateinit var contentView: View
51 
52     @Before
setupnull53     fun setup() {
54         window = rule.scenario.withActivity { it.window }
55         contentView = rule.scenario.withActivity { it.findViewById(android.R.id.content)!! }
56 
57         if (Build.VERSION.SDK_INT >= 29) {
58             // On API 29+, the system can modify the bar colors to maintain contrast.
59             // We disable that here to make it simple to assert expected values
60             rule.scenario.onActivity {
61                 window.apply {
62                     isNavigationBarContrastEnforced = false
63                     isStatusBarContrastEnforced = false
64                 }
65             }
66         }
67     }
68 
69     @Test
statusBarColornull70     fun statusBarColor() {
71         rule.scenario.onActivity {
72             // Now create an AndroidSystemUiController() and set the status bar color
73             val controller = AndroidSystemUiController(contentView, window)
74             controller.setStatusBarColor(Color.Blue, darkIcons = false)
75         }
76 
77         // Assert that the color was set
78         assertThat(Color(window.statusBarColor)).isEqualTo(Color.Blue)
79     }
80 
81     @Test
navigationBarColornull82     fun navigationBarColor() {
83         rule.scenario.onActivity {
84             // Now create an AndroidSystemUiController() and set the status bar color
85             val controller = AndroidSystemUiController(contentView, window)
86             controller.setNavigationBarColor(Color.Green, darkIcons = false)
87         }
88 
89         assertThat(Color(window.navigationBarColor)).isEqualTo(Color.Green)
90     }
91 
92     @Test
systemBarColornull93     fun systemBarColor() {
94         // Now create an AndroidSystemUiController() and set the system bar colors
95         rule.scenario.onActivity {
96             val controller = AndroidSystemUiController(contentView, window)
97             controller.setSystemBarsColor(Color.Red, darkIcons = false)
98         }
99 
100         // Assert that the colors were set
101         assertThat(Color(window.statusBarColor)).isEqualTo(Color.Red)
102         assertThat(Color(window.navigationBarColor)).isEqualTo(Color.Red)
103     }
104 
105     @Test
106     @Category(IgnoreOnRobolectric::class) // Robolectric implements the new behavior from 23+
107     @SdkSuppress(maxSdkVersion = 22)
statusBarIcons_scrimnull108     fun statusBarIcons_scrim() {
109         // Now create an AndroidSystemUiController() and set the navigation bar with dark icons
110         rule.scenario.onActivity {
111             val controller = AndroidSystemUiController(contentView, window)
112             controller.setStatusBarColor(Color.White, darkIcons = true) {
113                 // Here we can provide custom logic to 'darken' the color to maintain contrast.
114                 // We return red just to assert below.
115                 Color.Red
116             }
117         }
118 
119         // Assert that the colors were set to our 'darkened' color
120         assertThat(Color(window.statusBarColor)).isEqualTo(Color.Red)
121 
122         // Assert that the system couldn't apply the native light icons
123         rule.scenario.onActivity {
124             val windowInsetsController = WindowCompat.getInsetsController(window, contentView)
125             assertThat(windowInsetsController.isAppearanceLightStatusBars).isFalse()
126         }
127     }
128 
129     @Test
130     @SdkSuppress(minSdkVersion = 23)
statusBarIcons_nativenull131     fun statusBarIcons_native() {
132         // Now create an AndroidSystemUiController() and set the status bar with dark icons
133         rule.scenario.onActivity {
134             val controller = AndroidSystemUiController(contentView, window)
135             controller.setStatusBarColor(Color.White, darkIcons = true) {
136                 // Here we can provide custom logic to 'darken' the color to maintain contrast.
137                 // We return red just to assert below.
138                 Color.Red
139             }
140         }
141 
142         // Assert that the colors were darkened color is not used
143         assertThat(Color(window.statusBarColor)).isEqualTo(Color.White)
144 
145         // Assert that the system applied the native light icons
146         rule.scenario.onActivity {
147             val windowInsetsController = WindowCompat.getInsetsController(window, contentView)
148             assertThat(windowInsetsController.isAppearanceLightStatusBars).isTrue()
149         }
150     }
151 
152     @Test
153     @Category(IgnoreOnRobolectric::class) // Robolectric implements the new behavior from 25+
154     @SdkSuppress(maxSdkVersion = 25)
navigationBarIcons_scrimnull155     fun navigationBarIcons_scrim() {
156         // Now create an AndroidSystemUiController() and set the navigation bar with dark icons
157         rule.scenario.onActivity {
158             val controller = AndroidSystemUiController(contentView, window)
159             controller.setNavigationBarColor(Color.White, darkIcons = true) {
160                 // Here we can provide custom logic to 'darken' the color to maintain contrast.
161                 // We return red just to assert below.
162                 Color.Red
163             }
164         }
165 
166         // Assert that the colors were set to our 'darkened' color
167         assertThat(Color(window.navigationBarColor)).isEqualTo(Color.Red)
168 
169         // Assert that the system couldn't apply the native light icons
170         rule.scenario.onActivity {
171             val windowInsetsController = WindowCompat.getInsetsController(window, contentView)
172             assertThat(windowInsetsController.isAppearanceLightNavigationBars).isFalse()
173         }
174     }
175 
176     @Test
177     @SdkSuppress(minSdkVersion = 26)
navigationBar_nativenull178     fun navigationBar_native() {
179         // Now create an AndroidSystemUiController() and set the navigation bar with dark icons
180         rule.scenario.onActivity {
181             val controller = AndroidSystemUiController(contentView, window)
182             controller.setNavigationBarColor(Color.White, darkIcons = true) {
183                 // Here we can provide custom logic to 'darken' the color to maintain contrast.
184                 // We return red just to assert below.
185                 Color.Red
186             }
187         }
188 
189         // Assert that the colors were darkened color is not used
190         assertThat(Color(window.navigationBarColor)).isEqualTo(Color.White)
191 
192         // Assert that the system applied the native light icons
193         rule.scenario.onActivity {
194             val windowInsetsController = WindowCompat.getInsetsController(window, contentView)
195             assertThat(windowInsetsController.isAppearanceLightNavigationBars).isTrue()
196         }
197     }
198 
199     @Test
200     @SdkSuppress(minSdkVersion = 29)
navigationBar_contrastEnforcednull201     fun navigationBar_contrastEnforced() {
202         rule.scenario.onActivity {
203             // Now create an AndroidSystemUiController()
204             val controller = AndroidSystemUiController(contentView, window)
205 
206             // Assert that the contrast is not enforced initially
207             assertThat(controller.isNavigationBarContrastEnforced).isFalse()
208 
209             // and set the navigation bar with dark icons and enforce contrast
210             controller.setNavigationBarColor(
211                 Color.Transparent,
212                 darkIcons = true,
213                 navigationBarContrastEnforced = true
214             ) {
215                 // Here we can provide custom logic to 'darken' the color to maintain contrast.
216                 // We return red just to assert below.
217                 Color.Red
218             }
219 
220             // Assert that the colors were darkened color is not used
221             assertThat(Color(window.navigationBarColor)).isEqualTo(Color.Transparent)
222 
223             // Assert that the system applied the contrast enforced property
224             assertThat(window.isNavigationBarContrastEnforced).isTrue()
225 
226             // Assert that the controller reflects that the contrast is enforced
227             assertThat(controller.isNavigationBarContrastEnforced).isTrue()
228         }
229     }
230 
231     @Suppress("DEPRECATION")
232     @Test
233     @SdkSuppress(minSdkVersion = 30) // TODO: https://issuetracker.google.com/issues/189366125
systemBarsBehavior_showBarsByTouchnull234     fun systemBarsBehavior_showBarsByTouch() {
235         val controller = rule.scenario.withActivity {
236             AndroidSystemUiController(contentView, window)
237         }
238 
239         rule.scenario.onActivity {
240             controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_TOUCH
241         }
242 
243         assertThat(WindowCompat.getInsetsController(window, contentView).systemBarsBehavior)
244             .isEqualTo(WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_TOUCH)
245     }
246 
247     @Test
248     @SdkSuppress(minSdkVersion = 30) // TODO: https://issuetracker.google.com/issues/189366125
systemBarsBehavior_showBarsBySwipenull249     fun systemBarsBehavior_showBarsBySwipe() {
250         val controller = rule.scenario.withActivity {
251             AndroidSystemUiController(contentView, window)
252         }
253 
254         rule.scenario.onActivity {
255             controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_DEFAULT
256         }
257 
258         assertThat(WindowCompat.getInsetsController(window, contentView).systemBarsBehavior)
259             .isEqualTo(WindowInsetsControllerCompat.BEHAVIOR_DEFAULT)
260     }
261 
262     @Test
263     @SdkSuppress(minSdkVersion = 30) // TODO: https://issuetracker.google.com/issues/189366125
systemBarsBehavior_showTransientBarsBySwipenull264     fun systemBarsBehavior_showTransientBarsBySwipe() {
265         val controller = rule.scenario.withActivity {
266             AndroidSystemUiController(contentView, window)
267         }
268 
269         rule.scenario.onActivity {
270             controller.systemBarsBehavior =
271                 WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
272         }
273 
274         assertThat(WindowCompat.getInsetsController(window, contentView).systemBarsBehavior)
275             .isEqualTo(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
276     }
277 
278     @Test
279     @FlakyTest(detail = "https://github.com/google/accompanist/issues/491")
280     @SdkSuppress(minSdkVersion = 23) // rootWindowInsets which work
281     @Category(IgnoreOnRobolectric::class)
statusBarsVisibilitynull282     fun statusBarsVisibility() {
283         // Now create an AndroidSystemUiController() and set the system bar colors
284         val controller = rule.scenario.withActivity {
285             AndroidSystemUiController(contentView, window)
286         }
287 
288         // First show the bars
289         rule.scenario.onActivity {
290             controller.isStatusBarVisible = true
291         }
292         waitUntil { isRootWindowTypeVisible(WindowInsetsCompat.Type.statusBars()) }
293 
294         // Now hide the bars
295         rule.scenario.onActivity {
296             controller.isStatusBarVisible = false
297         }
298         waitUntil { !isRootWindowTypeVisible(WindowInsetsCompat.Type.statusBars()) }
299     }
300 
301     @Test
302     @FlakyTest(detail = "https://github.com/google/accompanist/issues/491")
303     @SdkSuppress(minSdkVersion = 23) // rootWindowInsets which work
304     @Category(IgnoreOnRobolectric::class)
navigationBarsVisibilitynull305     fun navigationBarsVisibility() {
306         // Now create an AndroidSystemUiController() and set the system bar colors
307         val controller = rule.scenario.withActivity {
308             AndroidSystemUiController(contentView, window)
309         }
310 
311         // First show the bars
312         rule.scenario.onActivity {
313             controller.isNavigationBarVisible = true
314         }
315         waitUntil { isRootWindowTypeVisible(WindowInsetsCompat.Type.navigationBars()) }
316 
317         // Now hide the bars
318         rule.scenario.onActivity {
319             controller.isNavigationBarVisible = false
320         }
321         waitUntil { !isRootWindowTypeVisible(WindowInsetsCompat.Type.navigationBars()) }
322     }
323 
324     @Test
325     @Category(IgnoreOnRobolectric::class)
326     @FlakyTest(detail = "https://github.com/google/accompanist/issues/491")
327     @SdkSuppress(minSdkVersion = 23) // rootWindowInsets which work
systemBarsVisibilitynull328     fun systemBarsVisibility() {
329         // Now create an AndroidSystemUiController() and set the system bar colors
330         val controller = rule.scenario.withActivity {
331             AndroidSystemUiController(contentView, window)
332         }
333 
334         // First show the bars
335         rule.scenario.onActivity {
336             controller.isSystemBarsVisible = true
337         }
338         waitUntil { isRootWindowTypeVisible(WindowInsetsCompat.Type.navigationBars()) }
339         waitUntil { isRootWindowTypeVisible(WindowInsetsCompat.Type.statusBars()) }
340 
341         // Now hide the bars
342         rule.scenario.onActivity {
343             controller.isSystemBarsVisible = false
344         }
345         waitUntil { !isRootWindowTypeVisible(WindowInsetsCompat.Type.navigationBars()) }
346         waitUntil { !isRootWindowTypeVisible(WindowInsetsCompat.Type.statusBars()) }
347     }
348 
isRootWindowTypeVisiblenull349     private fun isRootWindowTypeVisible(type: Int): Boolean {
350         return rule.scenario.withActivity {
351             ViewCompat.getRootWindowInsets(contentView)!!.isVisible(type)
352         }
353     }
354 }
355