1 /*
2  * Copyright (C) 2023 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.disableflags.data.repository
16 
17 import android.app.StatusBarManager.DISABLE2_NONE
18 import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
19 import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
20 import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS
21 import android.app.StatusBarManager.DISABLE_CLOCK
22 import android.app.StatusBarManager.DISABLE_NONE
23 import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
24 import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
25 import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
26 import android.content.res.Configuration
27 import androidx.test.ext.junit.runners.AndroidJUnit4
28 import androidx.test.filters.SmallTest
29 import com.android.systemui.SysuiTestCase
30 import com.android.systemui.dump.DumpManager
31 import com.android.systemui.log.LogBufferFactory
32 import com.android.systemui.res.R
33 import com.android.systemui.statusbar.CommandQueue
34 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
35 import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
36 import com.android.systemui.statusbar.policy.ConfigurationController
37 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler
38 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
39 import com.android.systemui.util.mockito.any
40 import com.android.systemui.util.mockito.argumentCaptor
41 import com.android.systemui.util.mockito.mock
42 import com.google.common.truth.Truth.assertThat
43 import kotlinx.coroutines.ExperimentalCoroutinesApi
44 import kotlinx.coroutines.test.TestScope
45 import kotlinx.coroutines.test.UnconfinedTestDispatcher
46 import kotlinx.coroutines.test.runTest
47 import org.junit.Before
48 import org.junit.Test
49 import org.junit.runner.RunWith
50 import org.mockito.Mockito.verify
51 
52 @SmallTest
53 @OptIn(ExperimentalCoroutinesApi::class)
54 @RunWith(AndroidJUnit4::class)
55 class DisableFlagsRepositoryTest : SysuiTestCase() {
56 
57     private lateinit var underTest: DisableFlagsRepository
58 
59     private val testScope = TestScope(UnconfinedTestDispatcher())
60     private val commandQueue: CommandQueue = mock()
61     private val configurationController: ConfigurationController = mock()
62     private val remoteInputQuickSettingsDisabler =
63         RemoteInputQuickSettingsDisabler(
64             context,
65             commandQueue,
66             ResourcesSplitShadeStateController(),
67             configurationController,
68         )
69     private val logBuffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10)
70     private val disableFlagsLogger = DisableFlagsLogger()
71 
72     @Before
setUpnull73     fun setUp() {
74         underTest =
75             DisableFlagsRepositoryImpl(
76                 commandQueue,
77                 DISPLAY_ID,
78                 testScope.backgroundScope,
79                 remoteInputQuickSettingsDisabler,
80                 logBuffer,
81                 disableFlagsLogger,
82             )
83     }
84 
85     @Test
disableFlags_initialValue_nonenull86     fun disableFlags_initialValue_none() {
87         assertThat(underTest.disableFlags.value)
88             .isEqualTo(DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE, animate = false))
89     }
90 
91     @Test
disableFlags_noSubscribers_callbackStillRegisterednull92     fun disableFlags_noSubscribers_callbackStillRegistered() =
93         testScope.runTest { verify(commandQueue).addCallback(any()) }
94 
95     @Test
disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTruenull96     fun disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTrue() =
97         testScope.runTest {
98             getCommandQueueCallback()
99                 .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
100 
101             assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue()
102         }
103 
104     @Test
disableFlags_notifAlertsDisabled_notifAlertsEnabledFalsenull105     fun disableFlags_notifAlertsDisabled_notifAlertsEnabledFalse() =
106         testScope.runTest {
107             getCommandQueueCallback()
108                 .disable(
109                     DISPLAY_ID,
110                     DISABLE_NOTIFICATION_ALERTS,
111                     DISABLE2_NONE,
112                     /* animate= */ false,
113                 )
114 
115             assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse()
116         }
117 
118     @Test
disableFlags_notifAlertsDisabled_differentDisplay_notifAlertsEnabledTruenull119     fun disableFlags_notifAlertsDisabled_differentDisplay_notifAlertsEnabledTrue() =
120         testScope.runTest {
121             val wrongDisplayId = DISPLAY_ID + 10
122 
123             getCommandQueueCallback()
124                 .disable(
125                     wrongDisplayId,
126                     DISABLE_NOTIFICATION_ALERTS,
127                     DISABLE2_NONE,
128                     /* animate= */ false,
129                 )
130 
131             // THEN our repo reports them as still enabled
132             assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue()
133         }
134 
135     @Test
disableFlags_shadeNotDisabled_shadeEnabledTruenull136     fun disableFlags_shadeNotDisabled_shadeEnabledTrue() =
137         testScope.runTest {
138             getCommandQueueCallback()
139                 .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
140 
141             assertThat(underTest.disableFlags.value.isShadeEnabled()).isTrue()
142         }
143 
144     @Test
disableFlags_shadeDisabled_shadeEnabledFalsenull145     fun disableFlags_shadeDisabled_shadeEnabledFalse() =
146         testScope.runTest {
147             getCommandQueueCallback()
148                 .disable(
149                     DISPLAY_ID,
150                     DISABLE_NONE,
151                     DISABLE2_NOTIFICATION_SHADE,
152                     /* animate= */ false,
153                 )
154 
155             assertThat(underTest.disableFlags.value.isShadeEnabled()).isFalse()
156         }
157 
158     @Test
disableFlags_shadeDisabled_differentDisplay_shadeEnabledTruenull159     fun disableFlags_shadeDisabled_differentDisplay_shadeEnabledTrue() =
160         testScope.runTest {
161             val wrongDisplayId = DISPLAY_ID + 10
162 
163             getCommandQueueCallback()
164                 .disable(
165                     wrongDisplayId,
166                     DISABLE_NONE,
167                     DISABLE2_NOTIFICATION_SHADE,
168                     /* animate= */ false,
169                 )
170 
171             // THEN our repo reports them as still enabled
172             assertThat(underTest.disableFlags.value.isShadeEnabled()).isTrue()
173         }
174 
175     @Test
disableFlags_quickSettingsNotDisabled_quickSettingsEnabledTruenull176     fun disableFlags_quickSettingsNotDisabled_quickSettingsEnabledTrue() =
177         testScope.runTest {
178             getCommandQueueCallback()
179                 .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
180 
181             assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isTrue()
182         }
183 
184     @Test
disableFlags_quickSettingsDisabled_quickSettingsEnabledFalsenull185     fun disableFlags_quickSettingsDisabled_quickSettingsEnabledFalse() =
186         testScope.runTest {
187             getCommandQueueCallback()
188                 .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_QUICK_SETTINGS, /* animate= */ false)
189 
190             assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse()
191         }
192 
193     @Test
disableFlags_quickSettingsDisabled_differentDisplay_quickSettingsEnabledTruenull194     fun disableFlags_quickSettingsDisabled_differentDisplay_quickSettingsEnabledTrue() =
195         testScope.runTest {
196             val wrongDisplayId = DISPLAY_ID + 10
197 
198             getCommandQueueCallback()
199                 .disable(
200                     wrongDisplayId,
201                     DISABLE_NONE,
202                     DISABLE2_QUICK_SETTINGS,
203                     /* animate= */ false,
204                 )
205 
206             // THEN our repo reports them as still enabled
207             assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isTrue()
208         }
209 
210     @Test
disableFlags_remoteInputActive_quickSettingsEnabledFalsenull211     fun disableFlags_remoteInputActive_quickSettingsEnabledFalse() =
212         testScope.runTest {
213             // WHEN remote input is set up to be active
214             val configuration = Configuration(mContext.resources.configuration)
215             configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
216             mContext.orCreateTestableResources.addOverride(
217                 R.bool.config_use_split_notification_shade,
218                 /* value= */ false,
219             )
220             remoteInputQuickSettingsDisabler.setRemoteInputActive(true)
221             remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
222 
223             getCommandQueueCallback()
224                 .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
225 
226             // THEN quick settings is disabled (even if the disable flags don't say so)
227             assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse()
228         }
229 
230     @Test
disableFlags_clockDisablednull231     fun disableFlags_clockDisabled() =
232         testScope.runTest {
233             getCommandQueueCallback()
234                 .disable(DISPLAY_ID, DISABLE_CLOCK, DISABLE2_NONE, /* animate= */ false)
235 
236             assertThat(underTest.disableFlags.value.isClockEnabled).isFalse()
237         }
238 
239     @Test
disableFlags_clockEnablednull240     fun disableFlags_clockEnabled() =
241         testScope.runTest {
242             getCommandQueueCallback()
243                 .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
244 
245             assertThat(underTest.disableFlags.value.isClockEnabled).isTrue()
246         }
247 
248     @Test
disableFlags_notificationIconsDisablednull249     fun disableFlags_notificationIconsDisabled() =
250         testScope.runTest {
251             getCommandQueueCallback()
252                 .disable(
253                     DISPLAY_ID,
254                     DISABLE_NOTIFICATION_ICONS,
255                     DISABLE2_NONE,
256                     /* animate= */ false,
257                 )
258 
259             assertThat(underTest.disableFlags.value.areNotificationIconsEnabled).isFalse()
260         }
261 
262     @Test
disableFlags_notificationIconsEnablednull263     fun disableFlags_notificationIconsEnabled() =
264         testScope.runTest {
265             getCommandQueueCallback()
266                 .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
267 
268             assertThat(underTest.disableFlags.value.areNotificationIconsEnabled).isTrue()
269         }
270 
271     @Test
disableFlags_systemInfoDisabled_viaDisable1null272     fun disableFlags_systemInfoDisabled_viaDisable1() =
273         testScope.runTest {
274             getCommandQueueCallback()
275                 .disable(DISPLAY_ID, DISABLE_SYSTEM_INFO, DISABLE2_NONE, /* animate= */ false)
276 
277             assertThat(underTest.disableFlags.value.isSystemInfoEnabled).isFalse()
278         }
279 
280     @Test
disableFlags_systemInfoDisabled_viaDisable2null281     fun disableFlags_systemInfoDisabled_viaDisable2() =
282         testScope.runTest {
283             getCommandQueueCallback()
284                 .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_SYSTEM_ICONS, /* animate= */ false)
285 
286             assertThat(underTest.disableFlags.value.isSystemInfoEnabled).isFalse()
287         }
288 
289     @Test
disableFlags_systemInfoEnablednull290     fun disableFlags_systemInfoEnabled() =
291         testScope.runTest {
292             getCommandQueueCallback()
293                 .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
294 
295             assertThat(underTest.disableFlags.value.isSystemInfoEnabled).isTrue()
296         }
297 
298     @Test
disableFlags_reactsToChangesnull299     fun disableFlags_reactsToChanges() =
300         testScope.runTest {
301             getCommandQueueCallback()
302                 .disable(
303                     DISPLAY_ID,
304                     DISABLE_NOTIFICATION_ALERTS,
305                     DISABLE2_NONE,
306                     /* animate= */ false,
307                 )
308             assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse()
309 
310             getCommandQueueCallback()
311                 .disable(
312                     DISPLAY_ID,
313                     DISABLE_CLOCK, // Unrelated to notifications
314                     DISABLE2_NONE,
315                     /* animate= */ false,
316                 )
317             assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue()
318 
319             getCommandQueueCallback()
320                 .disable(
321                     DISPLAY_ID,
322                     DISABLE_NOTIFICATION_ALERTS,
323                     DISABLE2_QUICK_SETTINGS or DISABLE2_NOTIFICATION_SHADE,
324                     /* animate= */ false,
325                 )
326             assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse()
327             assertThat(underTest.disableFlags.value.isShadeEnabled()).isFalse()
328             assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse()
329         }
330 
331     @Test
disableFlags_animateFalsenull332     fun disableFlags_animateFalse() =
333         testScope.runTest {
334             getCommandQueueCallback()
335                 .disable(
336                     DISPLAY_ID,
337                     DISABLE_NOTIFICATION_ALERTS,
338                     DISABLE2_NONE,
339                     /* animate= */ false,
340                 )
341 
342             assertThat(underTest.disableFlags.value.animate).isFalse()
343         }
344 
345     @Test
disableFlags_animateTruenull346     fun disableFlags_animateTrue() =
347         testScope.runTest {
348             getCommandQueueCallback()
349                 .disable(
350                     DISPLAY_ID,
351                     DISABLE_NOTIFICATION_ALERTS,
352                     DISABLE2_NONE,
353                     /* animate= */ true,
354                 )
355 
356             assertThat(underTest.disableFlags.value.animate).isTrue()
357         }
358 
getCommandQueueCallbacknull359     private fun getCommandQueueCallback(): CommandQueue.Callbacks {
360         val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
361         verify(commandQueue).addCallback(callbackCaptor.capture())
362         return callbackCaptor.value
363     }
364 
365     private companion object {
366         const val DISPLAY_ID = 1
367     }
368 }
369