1 /*
2  * Copyright (C) 2023 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 package com.android.systemui.keyguard.domain.interactor
18 
19 import androidx.test.ext.junit.runners.AndroidJUnit4
20 import androidx.test.filters.SmallTest
21 import com.android.systemui.SysUITestModule
22 import com.android.systemui.SysuiTestCase
23 import com.android.systemui.TestMocksModule
24 import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
25 import com.android.systemui.coroutines.collectValues
26 import com.android.systemui.dagger.SysUISingleton
27 import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
28 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
29 import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository
30 import com.android.systemui.keyguard.shared.model.KeyguardState
31 import com.android.systemui.keyguard.shared.model.TransitionState
32 import com.android.systemui.keyguard.shared.model.TransitionStep
33 import com.android.systemui.keyguard.util.mockTopActivityClassName
34 import com.android.systemui.shared.system.ActivityManagerWrapper
35 import com.android.systemui.user.domain.UserDomainLayerModule
36 import dagger.BindsInstance
37 import dagger.Component
38 import junit.framework.Assert.assertEquals
39 import kotlinx.coroutines.test.TestScope
40 import kotlinx.coroutines.test.runCurrent
41 import kotlinx.coroutines.test.runTest
42 import org.junit.Before
43 import org.junit.Test
44 import org.junit.runner.RunWith
45 import org.mockito.Mock
46 import org.mockito.MockitoAnnotations
47 
48 @SmallTest
49 @RunWith(AndroidJUnit4::class)
50 @kotlinx.coroutines.ExperimentalCoroutinesApi
51 class InWindowLauncherUnlockAnimationInteractorTest : SysuiTestCase() {
52     private lateinit var underTest: InWindowLauncherUnlockAnimationInteractor
53 
54     private lateinit var testComponent: TestComponent
55     private lateinit var testScope: TestScope
56     private lateinit var transitionRepository: FakeKeyguardTransitionRepository
57     @Mock private lateinit var activityManagerWrapper: ActivityManagerWrapper
58 
59     private val launcherClassName = "launcher"
60 
61     @Before
setUpnull62     fun setUp() {
63         MockitoAnnotations.initMocks(this)
64 
65         testComponent =
66             DaggerInWindowLauncherUnlockAnimationInteractorTest_TestComponent.factory()
67                 .create(
68                     test = this,
69                     mocks =
70                         TestMocksModule(
71                             activityManagerWrapper = activityManagerWrapper,
72                         ),
73                 )
74         underTest = testComponent.underTest
75         testScope = testComponent.testScope
76         transitionRepository = testComponent.transitionRepository
77 
78         activityManagerWrapper.mockTopActivityClassName(launcherClassName)
79     }
80 
81     @Test
testTransitioningToGoneWithInWindowAnimation_trueIfTopActivityIsLauncher_andTransitioningToGonenull82     fun testTransitioningToGoneWithInWindowAnimation_trueIfTopActivityIsLauncher_andTransitioningToGone() =
83         testScope.runTest {
84             val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation)
85             runCurrent()
86 
87             assertEquals(
88                 listOf(
89                     false, // False by default.
90                 ),
91                 values
92             )
93 
94             // Put launcher on top
95             testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
96                 launcherClassName
97             )
98             activityManagerWrapper.mockTopActivityClassName(launcherClassName)
99             runCurrent()
100 
101             // Should still be false since we're not transitioning to GONE.
102             assertEquals(
103                 listOf(
104                     false, // False by default.
105                 ),
106                 values
107             )
108 
109             transitionRepository.sendTransitionStep(
110                 TransitionStep(
111                     transitionState = TransitionState.STARTED,
112                     from = KeyguardState.LOCKSCREEN,
113                     to = KeyguardState.GONE,
114                 )
115             )
116             runCurrent()
117 
118             assertEquals(
119                 listOf(
120                     false,
121                     true, // -> GONE + launcher is behind
122                 ),
123                 values
124             )
125 
126             activityManagerWrapper.mockTopActivityClassName("not_launcher")
127             transitionRepository.sendTransitionStep(
128                 TransitionStep(
129                     transitionState = TransitionState.RUNNING,
130                     from = KeyguardState.LOCKSCREEN,
131                     to = KeyguardState.GONE,
132                 )
133             )
134             runCurrent()
135 
136             assertEquals(
137                 listOf(
138                     false,
139                     true, // Top activity should be sampled, if it changes midway it should not
140                     // matter.
141                 ),
142                 values
143             )
144 
145             transitionRepository.sendTransitionStep(
146                 TransitionStep(
147                     transitionState = TransitionState.FINISHED,
148                     from = KeyguardState.LOCKSCREEN,
149                     to = KeyguardState.GONE,
150                 )
151             )
152             runCurrent()
153 
154             assertEquals(
155                 listOf(
156                     false,
157                     true,
158                     false, // False once we're not transitioning anymore.
159                 ),
160                 values
161             )
162         }
163 
164     @Test
testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherPartwayThroughnull165     fun testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherPartwayThrough() =
166         testScope.runTest {
167             val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation)
168             runCurrent()
169 
170             assertEquals(
171                 listOf(
172                     false, // False by default.
173                 ),
174                 values
175             )
176 
177             // Put not launcher on top
178             testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
179                 launcherClassName
180             )
181             activityManagerWrapper.mockTopActivityClassName("not_launcher")
182             runCurrent()
183 
184             assertEquals(
185                 listOf(
186                     false,
187                 ),
188                 values
189             )
190 
191             transitionRepository.sendTransitionStep(
192                 TransitionStep(
193                     transitionState = TransitionState.STARTED,
194                     from = KeyguardState.LOCKSCREEN,
195                     to = KeyguardState.GONE,
196                 )
197             )
198             runCurrent()
199 
200             assertEquals(
201                 listOf(
202                     false,
203                 ),
204                 values
205             )
206 
207             activityManagerWrapper.mockTopActivityClassName(launcherClassName)
208             transitionRepository.sendTransitionStep(
209                 TransitionStep(
210                     transitionState = TransitionState.RUNNING,
211                     from = KeyguardState.LOCKSCREEN,
212                     to = KeyguardState.GONE,
213                 )
214             )
215             runCurrent()
216 
217             assertEquals(
218                 listOf(
219                     false,
220                 ),
221                 values
222             )
223 
224             transitionRepository.sendTransitionStep(
225                 TransitionStep(
226                     transitionState = TransitionState.FINISHED,
227                     from = KeyguardState.LOCKSCREEN,
228                     to = KeyguardState.GONE,
229                 )
230             )
231             runCurrent()
232 
233             assertEquals(
234                 listOf(
235                     false,
236                 ),
237                 values
238             )
239         }
240 
241     @Test
testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherWhileNotTransitioningToGonenull242     fun testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherWhileNotTransitioningToGone() =
243         testScope.runTest {
244             val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation)
245             runCurrent()
246 
247             assertEquals(
248                 listOf(
249                     false, // False by default.
250                 ),
251                 values
252             )
253 
254             // Put launcher on top
255             testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
256                 launcherClassName
257             )
258             activityManagerWrapper.mockTopActivityClassName(launcherClassName)
259             runCurrent()
260 
261             assertEquals(
262                 listOf(
263                     false,
264                 ),
265                 values
266             )
267 
268             transitionRepository.sendTransitionStep(
269                 TransitionStep(
270                     transitionState = TransitionState.STARTED,
271                     from = KeyguardState.AOD,
272                     to = KeyguardState.LOCKSCREEN,
273                 )
274             )
275             runCurrent()
276 
277             assertEquals(
278                 listOf(
279                     false,
280                 ),
281                 values
282             )
283         }
284 
285     @Test
testShouldStartInWindowAnimation_trueOnceSurfaceAvailable_falseWhenTransitionEndsnull286     fun testShouldStartInWindowAnimation_trueOnceSurfaceAvailable_falseWhenTransitionEnds() =
287         testScope.runTest {
288             val values by collectValues(underTest.shouldStartInWindowAnimation)
289             runCurrent()
290 
291             assertEquals(
292                 listOf(
293                     false, // False by default.
294                 ),
295                 values
296             )
297 
298             // Put Launcher on top and begin transitioning to GONE.
299             testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
300                 launcherClassName
301             )
302             activityManagerWrapper.mockTopActivityClassName(launcherClassName)
303             transitionRepository.sendTransitionStep(
304                 TransitionStep(
305                     transitionState = TransitionState.STARTED,
306                     from = KeyguardState.LOCKSCREEN,
307                     to = KeyguardState.GONE,
308                 )
309             )
310             runCurrent()
311 
312             assertEquals(
313                 listOf(
314                     false,
315                 ),
316                 values
317             )
318 
319             testComponent.surfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true)
320             runCurrent()
321 
322             assertEquals(
323                 listOf(
324                     false,
325                     true, // The surface is now available, so we should start the animation.
326                 ),
327                 values
328             )
329 
330             transitionRepository.sendTransitionStep(
331                 TransitionStep(
332                     transitionState = TransitionState.FINISHED,
333                     from = KeyguardState.LOCKSCREEN,
334                     to = KeyguardState.GONE,
335                 )
336             )
337             runCurrent()
338 
339             assertEquals(
340                 listOf(
341                     false,
342                     true,
343                     false,
344                 ),
345                 values
346             )
347         }
348 
349     @Test
testShouldStartInWindowAnimation_neverTrueIfSurfaceNotAvailablenull350     fun testShouldStartInWindowAnimation_neverTrueIfSurfaceNotAvailable() =
351         testScope.runTest {
352             val values by collectValues(underTest.shouldStartInWindowAnimation)
353             runCurrent()
354 
355             assertEquals(
356                 listOf(
357                     false, // False by default.
358                 ),
359                 values
360             )
361 
362             // Put Launcher on top and begin transitioning to GONE.
363             testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
364                 launcherClassName
365             )
366             activityManagerWrapper.mockTopActivityClassName(launcherClassName)
367             transitionRepository.sendTransitionStep(
368                 TransitionStep(
369                     transitionState = TransitionState.STARTED,
370                     from = KeyguardState.LOCKSCREEN,
371                     to = KeyguardState.GONE,
372                 )
373             )
374             transitionRepository.sendTransitionStep(
375                 TransitionStep(
376                     transitionState = TransitionState.FINISHED,
377                     from = KeyguardState.LOCKSCREEN,
378                     to = KeyguardState.GONE,
379                 )
380             )
381             runCurrent()
382 
383             assertEquals(
384                 listOf(
385                     false,
386                 ),
387                 values
388             )
389         }
390 
391     @Test
testShouldStartInWindowAnimation_falseIfSurfaceAvailable_afterTransitionInterruptednull392     fun testShouldStartInWindowAnimation_falseIfSurfaceAvailable_afterTransitionInterrupted() =
393         testScope.runTest {
394             val values by collectValues(underTest.shouldStartInWindowAnimation)
395             runCurrent()
396 
397             assertEquals(
398                 listOf(
399                     false, // False by default.
400                 ),
401                 values
402             )
403 
404             // Put Launcher on top and begin transitioning to GONE.
405             testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
406                 launcherClassName
407             )
408             activityManagerWrapper.mockTopActivityClassName(launcherClassName)
409             transitionRepository.sendTransitionStep(
410                 TransitionStep(
411                     transitionState = TransitionState.STARTED,
412                     from = KeyguardState.LOCKSCREEN,
413                     to = KeyguardState.GONE,
414                 )
415             )
416             transitionRepository.sendTransitionStep(
417                 TransitionStep(
418                     transitionState = TransitionState.CANCELED,
419                     from = KeyguardState.LOCKSCREEN,
420                     to = KeyguardState.GONE,
421                 )
422             )
423             transitionRepository.sendTransitionStep(
424                 TransitionStep(
425                     transitionState = TransitionState.STARTED,
426                     from = KeyguardState.GONE,
427                     to = KeyguardState.AOD,
428                 )
429             )
430             testComponent.surfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true)
431             runCurrent()
432 
433             assertEquals(
434                 listOf(
435                     false,
436                 ),
437                 values
438             )
439         }
440 
441     @SysUISingleton
442     @Component(
443         modules =
444             [
445                 SysUITestModule::class,
446                 BiometricsDomainLayerModule::class,
447                 UserDomainLayerModule::class,
448             ]
449     )
450     interface TestComponent {
451         val underTest: InWindowLauncherUnlockAnimationInteractor
452         val testScope: TestScope
453         val transitionRepository: FakeKeyguardTransitionRepository
454         val surfaceBehindRepository: FakeKeyguardSurfaceBehindRepository
455         val inWindowLauncherUnlockAnimationRepository: InWindowLauncherUnlockAnimationRepository
456 
457         @Component.Factory
458         interface Factory {
createnull459             fun create(
460                 @BindsInstance test: SysuiTestCase,
461                 mocks: TestMocksModule,
462             ): TestComponent
463         }
464     }
465 }
466