1 /*
2  * Copyright (C) 2024 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  *      http://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:OptIn(ExperimentalCoroutinesApi::class)
18 
19 package com.android.systemui.dreams.ui.viewmodel
20 
21 import android.platform.test.annotations.DisableFlags
22 import android.platform.test.annotations.EnableFlags
23 import androidx.test.ext.junit.runners.AndroidJUnit4
24 import androidx.test.filters.SmallTest
25 import com.android.compose.animation.scene.Swipe
26 import com.android.compose.animation.scene.UserActionResult
27 import com.android.systemui.SysuiTestCase
28 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
29 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
30 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
31 import com.android.systemui.coroutines.collectLastValue
32 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
33 import com.android.systemui.flags.EnableSceneContainer
34 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
35 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
36 import com.android.systemui.kosmos.testScope
37 import com.android.systemui.lifecycle.activateIn
38 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
39 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
40 import com.android.systemui.power.domain.interactor.powerInteractor
41 import com.android.systemui.scene.domain.interactor.sceneInteractor
42 import com.android.systemui.scene.shared.model.Overlays
43 import com.android.systemui.scene.shared.model.Scenes
44 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
45 import com.android.systemui.shade.data.repository.fakeShadeRepository
46 import com.android.systemui.shade.shared.flag.DualShade
47 import com.android.systemui.shade.shared.model.ShadeMode
48 import com.android.systemui.testKosmos
49 import com.google.common.truth.Truth.assertThat
50 import kotlin.test.Test
51 import kotlinx.coroutines.ExperimentalCoroutinesApi
52 import kotlinx.coroutines.test.TestScope
53 import kotlinx.coroutines.test.runCurrent
54 import kotlinx.coroutines.test.runTest
55 import org.junit.Before
56 import org.junit.runner.RunWith
57 
58 @SmallTest
59 @RunWith(AndroidJUnit4::class)
60 @EnableSceneContainer
61 class DreamUserActionsViewModelTest : SysuiTestCase() {
62 
63     private val kosmos = testKosmos()
64     private val testScope = kosmos.testScope
65 
66     private lateinit var underTest: DreamUserActionsViewModel
67 
68     @Before
setUpnull69     fun setUp() {
70         underTest = kosmos.dreamUserActionsViewModel
71         underTest.activateIn(testScope)
72     }
73 
74     @Test
75     @DisableFlags(DualShade.FLAG_NAME)
actions_communalNotAvailable_singleShadenull76     fun actions_communalNotAvailable_singleShade() =
77         testScope.runTest {
78             kosmos.setCommunalAvailable(false)
79 
80             val actions by collectLastValue(underTest.actions)
81 
82             setUpState(
83                 isShadeTouchable = true,
84                 isDeviceUnlocked = false,
85                 shadeMode = ShadeMode.Single,
86             )
87             assertThat(actions).isNotEmpty()
88             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
89             assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
90             assertThat(actions?.get(Swipe.Start)).isNull()
91             assertThat(actions?.get(Swipe.End)).isNull()
92 
93             setUpState(
94                 isShadeTouchable = false,
95                 isDeviceUnlocked = false,
96                 shadeMode = ShadeMode.Single,
97             )
98             assertThat(actions).isEmpty()
99 
100             setUpState(
101                 isShadeTouchable = true,
102                 isDeviceUnlocked = true,
103                 shadeMode = ShadeMode.Single,
104             )
105             assertThat(actions).isNotEmpty()
106             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
107             assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
108             assertThat(actions?.get(Swipe.Start)).isNull()
109             assertThat(actions?.get(Swipe.End)).isNull()
110         }
111 
112     @Test
113     @DisableFlags(DualShade.FLAG_NAME)
actions_communalNotAvailable_splitShadenull114     fun actions_communalNotAvailable_splitShade() =
115         testScope.runTest {
116             kosmos.setCommunalAvailable(false)
117 
118             val actions by collectLastValue(underTest.actions)
119 
120             setUpState(
121                 isShadeTouchable = true,
122                 isDeviceUnlocked = false,
123                 shadeMode = ShadeMode.Split,
124             )
125             assertThat(actions).isNotEmpty()
126             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
127             assertThat(actions?.get(Swipe.Down))
128                 .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
129             assertThat(actions?.get(Swipe.Start)).isNull()
130             assertThat(actions?.get(Swipe.End)).isNull()
131 
132             setUpState(
133                 isShadeTouchable = false,
134                 isDeviceUnlocked = false,
135                 shadeMode = ShadeMode.Split,
136             )
137             assertThat(actions).isEmpty()
138 
139             setUpState(
140                 isShadeTouchable = true,
141                 isDeviceUnlocked = true,
142                 shadeMode = ShadeMode.Split,
143             )
144             assertThat(actions).isNotEmpty()
145             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
146             assertThat(actions?.get(Swipe.Down))
147                 .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
148             assertThat(actions?.get(Swipe.Start)).isNull()
149             assertThat(actions?.get(Swipe.End)).isNull()
150         }
151 
152     @Test
153     @EnableFlags(DualShade.FLAG_NAME)
actions_communalNotAvailable_dualShadenull154     fun actions_communalNotAvailable_dualShade() =
155         testScope.runTest {
156             kosmos.setCommunalAvailable(false)
157 
158             val actions by collectLastValue(underTest.actions)
159 
160             setUpState(
161                 isShadeTouchable = true,
162                 isDeviceUnlocked = false,
163                 shadeMode = ShadeMode.Dual,
164             )
165             assertThat(actions).isNotEmpty()
166             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
167             assertThat(actions?.get(Swipe.Down))
168                 .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
169             assertThat(actions?.get(Swipe.Start)).isNull()
170             assertThat(actions?.get(Swipe.End)).isNull()
171 
172             setUpState(
173                 isShadeTouchable = false,
174                 isDeviceUnlocked = false,
175                 shadeMode = ShadeMode.Dual,
176             )
177             assertThat(actions).isEmpty()
178 
179             setUpState(isShadeTouchable = true, isDeviceUnlocked = true, shadeMode = ShadeMode.Dual)
180             assertThat(actions).isNotEmpty()
181             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
182             assertThat(actions?.get(Swipe.Down))
183                 .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
184             assertThat(actions?.get(Swipe.Start)).isNull()
185             assertThat(actions?.get(Swipe.End)).isNull()
186         }
187 
188     @Test
189     @DisableFlags(DualShade.FLAG_NAME)
actions_communalAvailable_singleShadenull190     fun actions_communalAvailable_singleShade() =
191         testScope.runTest {
192             kosmos.setCommunalAvailable(true)
193 
194             val actions by collectLastValue(underTest.actions)
195 
196             setUpState(
197                 isShadeTouchable = true,
198                 isDeviceUnlocked = false,
199                 shadeMode = ShadeMode.Single,
200             )
201             assertThat(actions).isNotEmpty()
202             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
203             assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
204             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
205             assertThat(actions?.get(Swipe.End)).isNull()
206 
207             setUpState(
208                 isShadeTouchable = false,
209                 isDeviceUnlocked = false,
210                 shadeMode = ShadeMode.Single,
211             )
212             assertThat(actions).isEmpty()
213 
214             setUpState(
215                 isShadeTouchable = true,
216                 isDeviceUnlocked = true,
217                 shadeMode = ShadeMode.Single,
218             )
219             assertThat(actions).isNotEmpty()
220             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
221             assertThat(actions?.get(Swipe.Down)).isEqualTo(UserActionResult(Scenes.Shade))
222             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
223             assertThat(actions?.get(Swipe.End)).isNull()
224         }
225 
226     @Test
227     @DisableFlags(DualShade.FLAG_NAME)
actions_communalAvailable_splitShadenull228     fun actions_communalAvailable_splitShade() =
229         testScope.runTest {
230             kosmos.setCommunalAvailable(true)
231 
232             val actions by collectLastValue(underTest.actions)
233 
234             setUpState(
235                 isShadeTouchable = true,
236                 isDeviceUnlocked = false,
237                 shadeMode = ShadeMode.Split,
238             )
239             assertThat(actions).isNotEmpty()
240             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
241             assertThat(actions?.get(Swipe.Down))
242                 .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
243             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
244             assertThat(actions?.get(Swipe.End)).isNull()
245 
246             setUpState(
247                 isShadeTouchable = false,
248                 isDeviceUnlocked = false,
249                 shadeMode = ShadeMode.Split,
250             )
251             assertThat(actions).isEmpty()
252 
253             setUpState(
254                 isShadeTouchable = true,
255                 isDeviceUnlocked = true,
256                 shadeMode = ShadeMode.Split,
257             )
258             assertThat(actions).isNotEmpty()
259             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
260             assertThat(actions?.get(Swipe.Down))
261                 .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade))
262             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
263             assertThat(actions?.get(Swipe.End)).isNull()
264         }
265 
266     @Test
267     @EnableFlags(DualShade.FLAG_NAME)
actions_communalAvailable_dualShadenull268     fun actions_communalAvailable_dualShade() =
269         testScope.runTest {
270             kosmos.setCommunalAvailable(true)
271 
272             val actions by collectLastValue(underTest.actions)
273 
274             setUpState(
275                 isShadeTouchable = true,
276                 isDeviceUnlocked = false,
277                 shadeMode = ShadeMode.Dual,
278             )
279             assertThat(actions).isNotEmpty()
280             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer))
281             assertThat(actions?.get(Swipe.Down))
282                 .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
283             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
284             assertThat(actions?.get(Swipe.End)).isNull()
285 
286             setUpState(
287                 isShadeTouchable = false,
288                 isDeviceUnlocked = false,
289                 shadeMode = ShadeMode.Dual,
290             )
291             assertThat(actions).isEmpty()
292 
293             setUpState(isShadeTouchable = true, isDeviceUnlocked = true, shadeMode = ShadeMode.Dual)
294             assertThat(actions).isNotEmpty()
295             assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone))
296             assertThat(actions?.get(Swipe.Down))
297                 .isEqualTo(UserActionResult.ShowOverlay(Overlays.NotificationsShade))
298             assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal))
299             assertThat(actions?.get(Swipe.End)).isNull()
300         }
301 
TestScopenull302     private fun TestScope.setUpState(
303         isShadeTouchable: Boolean,
304         isDeviceUnlocked: Boolean,
305         shadeMode: ShadeMode,
306     ) {
307         if (isShadeTouchable) {
308             kosmos.powerInteractor.setAwakeForTest()
309         } else {
310             kosmos.powerInteractor.setAsleepForTest()
311         }
312 
313         if (isDeviceUnlocked) {
314             unlockDevice()
315         } else {
316             lockDevice()
317         }
318 
319         if (shadeMode == ShadeMode.Dual) {
320             assertThat(DualShade.isEnabled).isTrue()
321         } else {
322             assertThat(DualShade.isEnabled).isFalse()
323             kosmos.fakeShadeRepository.setShadeLayoutWide(shadeMode == ShadeMode.Split)
324         }
325         runCurrent()
326     }
327 
lockDevicenull328     private fun TestScope.lockDevice() {
329         val deviceUnlockStatus by
330             collectLastValue(kosmos.deviceUnlockedInteractor.deviceUnlockStatus)
331 
332         kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
333         assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
334         kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
335         runCurrent()
336     }
337 
unlockDevicenull338     private fun TestScope.unlockDevice() {
339         val deviceUnlockStatus by
340             collectLastValue(kosmos.deviceUnlockedInteractor.deviceUnlockStatus)
341 
342         kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
343             SuccessFingerprintAuthenticationStatus(0, true)
344         )
345         assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
346         kosmos.sceneInteractor.changeScene(Scenes.Gone, "reason")
347         runCurrent()
348     }
349 }
350