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.ui.viewmodel
18 
19 import androidx.test.ext.junit.runners.AndroidJUnit4
20 import androidx.test.filters.SmallTest
21 import com.android.settingslib.Utils
22 import com.android.systemui.SysuiTestCase
23 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
24 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
25 import com.android.systemui.coroutines.collectLastValue
26 import com.android.systemui.flags.FakeFeatureFlags
27 import com.android.systemui.flags.Flags
28 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
29 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
30 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
31 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
32 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
33 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
34 import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
35 import com.android.systemui.keyguard.shared.model.KeyguardState
36 import com.android.systemui.keyguard.shared.model.StatusBarState
37 import com.android.systemui.keyguard.shared.model.TransitionState
38 import com.android.systemui.keyguard.shared.model.TransitionStep
39 import com.android.systemui.shade.data.repository.FakeShadeRepository
40 import com.android.systemui.statusbar.phone.SystemUIDialogManager
41 import com.android.systemui.util.mockito.argumentCaptor
42 import com.android.systemui.util.mockito.mock
43 import com.android.wm.shell.animation.Interpolators
44 import com.google.common.collect.Range
45 import com.google.common.truth.Truth.assertThat
46 import kotlinx.coroutines.ExperimentalCoroutinesApi
47 import kotlinx.coroutines.test.TestScope
48 import kotlinx.coroutines.test.runCurrent
49 import kotlinx.coroutines.test.runTest
50 import org.junit.Before
51 import org.junit.Test
52 import org.junit.runner.RunWith
53 import org.mockito.Mock
54 import org.mockito.Mockito
55 import org.mockito.MockitoAnnotations
56 
57 /** Tests UDFPS lockscreen view model transitions. */
58 @ExperimentalCoroutinesApi
59 @SmallTest
60 @RunWith(AndroidJUnit4::class)
61 class UdfpsLockscreenViewModelTest : SysuiTestCase() {
62     private val lockscreenColorResId = android.R.attr.textColorPrimary
63     private val alternateBouncerResId = com.android.internal.R.attr.materialColorOnPrimaryFixed
64     private val lockscreenColor = Utils.getColorAttrDefaultColor(context, lockscreenColorResId)
65     private val alternateBouncerColor =
66         Utils.getColorAttrDefaultColor(context, alternateBouncerResId)
67 
68     @Mock private lateinit var dialogManager: SystemUIDialogManager
69 
70     private lateinit var underTest: UdfpsLockscreenViewModel
71     private lateinit var testScope: TestScope
72     private lateinit var transitionRepository: FakeKeyguardTransitionRepository
73     private lateinit var configRepository: FakeConfigurationRepository
74     private lateinit var keyguardRepository: FakeKeyguardRepository
75     private lateinit var keyguardInteractor: KeyguardInteractor
76     private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
77     private lateinit var shadeRepository: FakeShadeRepository
78     private lateinit var featureFlags: FakeFeatureFlags
79 
80     @Before
81     fun setUp() {
82         MockitoAnnotations.initMocks(this)
83         testScope = TestScope()
84         transitionRepository = FakeKeyguardTransitionRepository()
85         shadeRepository = FakeShadeRepository()
86         featureFlags =
87             FakeFeatureFlags().apply {
88                 set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
89                 set(Flags.FACE_AUTH_REFACTOR, false)
90             }
91         KeyguardInteractorFactory.create(
92                 featureFlags = featureFlags,
93             )
94             .also {
95                 keyguardInteractor = it.keyguardInteractor
96                 keyguardRepository = it.repository
97                 configRepository = it.configurationRepository
98                 bouncerRepository = it.bouncerRepository
99             }
100 
101         val transitionInteractor =
102             KeyguardTransitionInteractorFactory.create(
103                     scope = testScope.backgroundScope,
104                     repository = transitionRepository,
105                     keyguardInteractor = keyguardInteractor,
106                 )
107                 .keyguardTransitionInteractor
108 
109         underTest =
110             UdfpsLockscreenViewModel(
111                 context,
112                 lockscreenColorResId,
113                 alternateBouncerResId,
114                 transitionInteractor,
115                 UdfpsKeyguardInteractor(
116                     configRepository,
117                     BurnInInteractor(
118                         context,
119                         burnInHelperWrapper = mock(),
120                         testScope.backgroundScope,
121                         configRepository,
122                         keyguardInteractor,
123                     ),
124                     keyguardInteractor,
125                     shadeRepository,
126                     dialogManager,
127                 ),
128                 keyguardInteractor,
129             )
130     }
131 
132     @Test
133     fun goneToAodTransition() =
134         testScope.runTest {
135             val transition by collectLastValue(underTest.transition)
136             val visible by collectLastValue(underTest.visible)
137 
138             // TransitionState.STARTED: gone -> AOD
139             transitionRepository.sendTransitionStep(
140                 TransitionStep(
141                     from = KeyguardState.GONE,
142                     to = KeyguardState.AOD,
143                     value = 0f,
144                     transitionState = TransitionState.STARTED,
145                     ownerName = "goneToAodTransition",
146                 )
147             )
148             runCurrent()
149             assertThat(transition?.alpha).isEqualTo(0f)
150             assertThat(visible).isFalse()
151 
152             // TransitionState.RUNNING: gone -> AOD
153             transitionRepository.sendTransitionStep(
154                 TransitionStep(
155                     from = KeyguardState.GONE,
156                     to = KeyguardState.AOD,
157                     value = .6f,
158                     transitionState = TransitionState.RUNNING,
159                     ownerName = "goneToAodTransition",
160                 )
161             )
162             runCurrent()
163             assertThat(transition?.alpha).isEqualTo(0f)
164             assertThat(visible).isFalse()
165 
166             // TransitionState.FINISHED: gone -> AOD
167             transitionRepository.sendTransitionStep(
168                 TransitionStep(
169                     from = KeyguardState.GONE,
170                     to = KeyguardState.AOD,
171                     value = 1f,
172                     transitionState = TransitionState.FINISHED,
173                     ownerName = "goneToAodTransition",
174                 )
175             )
176             runCurrent()
177             assertThat(transition?.alpha).isEqualTo(0f)
178             assertThat(visible).isFalse()
179         }
180 
181     @Test
182     fun lockscreenToAod() =
183         testScope.runTest {
184             val transition by collectLastValue(underTest.transition)
185             val visible by collectLastValue(underTest.visible)
186             keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
187 
188             // TransitionState.STARTED: lockscreen -> AOD
189             transitionRepository.sendTransitionStep(
190                 TransitionStep(
191                     from = KeyguardState.LOCKSCREEN,
192                     to = KeyguardState.AOD,
193                     value = 0f,
194                     transitionState = TransitionState.STARTED,
195                     ownerName = "lockscreenToAod",
196                 )
197             )
198             runCurrent()
199             assertThat(transition?.alpha).isEqualTo(1f)
200             assertThat(transition?.scale).isEqualTo(1f)
201             assertThat(transition?.color).isEqualTo(lockscreenColor)
202             assertThat(visible).isTrue()
203 
204             // TransitionState.RUNNING: lockscreen -> AOD
205             transitionRepository.sendTransitionStep(
206                 TransitionStep(
207                     from = KeyguardState.LOCKSCREEN,
208                     to = KeyguardState.AOD,
209                     value = .6f,
210                     transitionState = TransitionState.RUNNING,
211                     ownerName = "lockscreenToAod",
212                 )
213             )
214             runCurrent()
215             assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
216             assertThat(transition?.scale).isEqualTo(1f)
217             assertThat(transition?.color).isEqualTo(lockscreenColor)
218             assertThat(visible).isTrue()
219 
220             // TransitionState.FINISHED: lockscreen -> AOD
221             transitionRepository.sendTransitionStep(
222                 TransitionStep(
223                     from = KeyguardState.LOCKSCREEN,
224                     to = KeyguardState.AOD,
225                     value = 1f,
226                     transitionState = TransitionState.FINISHED,
227                     ownerName = "lockscreenToAod",
228                 )
229             )
230             runCurrent()
231             assertThat(transition?.alpha).isEqualTo(0f)
232             assertThat(transition?.scale).isEqualTo(1f)
233             assertThat(transition?.color).isEqualTo(lockscreenColor)
234             assertThat(visible).isFalse()
235         }
236 
237     @Test
238     fun lockscreenShadeLockedToAod() =
239         testScope.runTest {
240             val transition by collectLastValue(underTest.transition)
241             val visible by collectLastValue(underTest.visible)
242             keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
243 
244             // TransitionState.STARTED: lockscreen -> AOD
245             transitionRepository.sendTransitionStep(
246                 TransitionStep(
247                     from = KeyguardState.LOCKSCREEN,
248                     to = KeyguardState.AOD,
249                     value = 0f,
250                     transitionState = TransitionState.STARTED,
251                     ownerName = "lockscreenToAod",
252                 )
253             )
254             runCurrent()
255             assertThat(transition?.alpha).isEqualTo(0f)
256             assertThat(visible).isFalse()
257 
258             // TransitionState.RUNNING: lockscreen -> AOD
259             transitionRepository.sendTransitionStep(
260                 TransitionStep(
261                     from = KeyguardState.LOCKSCREEN,
262                     to = KeyguardState.AOD,
263                     value = .6f,
264                     transitionState = TransitionState.RUNNING,
265                     ownerName = "lockscreenToAod",
266                 )
267             )
268             runCurrent()
269             assertThat(transition?.alpha).isEqualTo(0f)
270             assertThat(visible).isFalse()
271 
272             // TransitionState.FINISHED: lockscreen -> AOD
273             transitionRepository.sendTransitionStep(
274                 TransitionStep(
275                     from = KeyguardState.LOCKSCREEN,
276                     to = KeyguardState.AOD,
277                     value = 1f,
278                     transitionState = TransitionState.FINISHED,
279                     ownerName = "lockscreenToAod",
280                 )
281             )
282             runCurrent()
283             assertThat(transition?.alpha).isEqualTo(0f)
284             assertThat(visible).isFalse()
285         }
286 
287     @Test
288     fun aodToLockscreen() =
289         testScope.runTest {
290             val transition by collectLastValue(underTest.transition)
291             val visible by collectLastValue(underTest.visible)
292 
293             // TransitionState.STARTED: AOD -> lockscreen
294             transitionRepository.sendTransitionStep(
295                 TransitionStep(
296                     from = KeyguardState.AOD,
297                     to = KeyguardState.LOCKSCREEN,
298                     value = 0f,
299                     transitionState = TransitionState.STARTED,
300                     ownerName = "aodToLockscreen",
301                 )
302             )
303             runCurrent()
304             assertThat(transition?.alpha).isEqualTo(0f)
305             assertThat(transition?.scale).isEqualTo(1f)
306             assertThat(transition?.color).isEqualTo(lockscreenColor)
307             assertThat(visible).isFalse()
308 
309             // TransitionState.RUNNING: AOD -> lockscreen
310             transitionRepository.sendTransitionStep(
311                 TransitionStep(
312                     from = KeyguardState.AOD,
313                     to = KeyguardState.LOCKSCREEN,
314                     value = .6f,
315                     transitionState = TransitionState.RUNNING,
316                     ownerName = "aodToLockscreen",
317                 )
318             )
319             runCurrent()
320             assertThat(transition?.alpha).isIn(Range.closed(.59f, .61f))
321             assertThat(transition?.scale).isEqualTo(1f)
322             assertThat(transition?.color).isEqualTo(lockscreenColor)
323             assertThat(visible).isTrue()
324 
325             // TransitionState.FINISHED: AOD -> lockscreen
326             transitionRepository.sendTransitionStep(
327                 TransitionStep(
328                     from = KeyguardState.AOD,
329                     to = KeyguardState.LOCKSCREEN,
330                     value = 1f,
331                     transitionState = TransitionState.FINISHED,
332                     ownerName = "aodToLockscreen",
333                 )
334             )
335             runCurrent()
336             assertThat(transition?.alpha).isEqualTo(1f)
337             assertThat(transition?.scale).isEqualTo(1f)
338             assertThat(transition?.color).isEqualTo(lockscreenColor)
339             assertThat(visible).isTrue()
340         }
341 
342     @Test
343     fun lockscreenToAlternateBouncer() =
344         testScope.runTest {
345             val transition by collectLastValue(underTest.transition)
346             val visible by collectLastValue(underTest.visible)
347             keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
348 
349             // TransitionState.STARTED: lockscreen -> alternate bouncer
350             transitionRepository.sendTransitionStep(
351                 TransitionStep(
352                     from = KeyguardState.LOCKSCREEN,
353                     to = KeyguardState.ALTERNATE_BOUNCER,
354                     value = 0f,
355                     transitionState = TransitionState.STARTED,
356                     ownerName = "lockscreenToAlternateBouncer",
357                 )
358             )
359             runCurrent()
360             assertThat(transition?.alpha).isEqualTo(1f)
361             assertThat(transition?.scale).isEqualTo(1f)
362             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
363             assertThat(visible).isTrue()
364 
365             // TransitionState.RUNNING: lockscreen -> alternate bouncer
366             transitionRepository.sendTransitionStep(
367                 TransitionStep(
368                     from = KeyguardState.LOCKSCREEN,
369                     to = KeyguardState.ALTERNATE_BOUNCER,
370                     value = .6f,
371                     transitionState = TransitionState.RUNNING,
372                     ownerName = "lockscreenToAlternateBouncer",
373                 )
374             )
375             runCurrent()
376             assertThat(transition?.alpha).isEqualTo(1f)
377             assertThat(transition?.scale).isEqualTo(1f)
378             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
379             assertThat(visible).isTrue()
380 
381             // TransitionState.FINISHED: lockscreen -> alternate bouncer
382             transitionRepository.sendTransitionStep(
383                 TransitionStep(
384                     from = KeyguardState.LOCKSCREEN,
385                     to = KeyguardState.ALTERNATE_BOUNCER,
386                     value = 1f,
387                     transitionState = TransitionState.FINISHED,
388                     ownerName = "lockscreenToAlternateBouncer",
389                 )
390             )
391             runCurrent()
392             assertThat(transition?.alpha).isEqualTo(1f)
393             assertThat(transition?.scale).isEqualTo(1f)
394             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
395             assertThat(visible).isTrue()
396         }
397 
398     fun alternateBouncerToPrimaryBouncer() =
399         testScope.runTest {
400             val transition by collectLastValue(underTest.transition)
401             val visible by collectLastValue(underTest.visible)
402 
403             // TransitionState.STARTED: alternate bouncer -> primary bouncer
404             transitionRepository.sendTransitionStep(
405                 TransitionStep(
406                     from = KeyguardState.ALTERNATE_BOUNCER,
407                     to = KeyguardState.PRIMARY_BOUNCER,
408                     value = 0f,
409                     transitionState = TransitionState.STARTED,
410                     ownerName = "alternateBouncerToPrimaryBouncer",
411                 )
412             )
413             runCurrent()
414             assertThat(transition?.alpha).isEqualTo(1f)
415             assertThat(transition?.scale).isEqualTo(1f)
416             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
417             assertThat(visible).isTrue()
418 
419             // TransitionState.RUNNING: alternate bouncer -> primary bouncer
420             transitionRepository.sendTransitionStep(
421                 TransitionStep(
422                     from = KeyguardState.ALTERNATE_BOUNCER,
423                     to = KeyguardState.PRIMARY_BOUNCER,
424                     value = .6f,
425                     transitionState = TransitionState.RUNNING,
426                     ownerName = "alternateBouncerToPrimaryBouncer",
427                 )
428             )
429             runCurrent()
430             assertThat(transition?.alpha).isIn(Range.closed(.59f, .61f))
431             assertThat(transition?.scale).isEqualTo(1f)
432             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
433             assertThat(visible).isTrue()
434 
435             // TransitionState.FINISHED: alternate bouncer -> primary bouncer
436             transitionRepository.sendTransitionStep(
437                 TransitionStep(
438                     from = KeyguardState.ALTERNATE_BOUNCER,
439                     to = KeyguardState.PRIMARY_BOUNCER,
440                     value = 1f,
441                     transitionState = TransitionState.FINISHED,
442                     ownerName = "alternateBouncerToPrimaryBouncer",
443                 )
444             )
445             runCurrent()
446             assertThat(transition?.alpha).isEqualTo(0f)
447             assertThat(transition?.scale).isEqualTo(1f)
448             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
449             assertThat(visible).isFalse()
450         }
451 
452     fun alternateBouncerToAod() =
453         testScope.runTest {
454             val transition by collectLastValue(underTest.transition)
455             val visible by collectLastValue(underTest.visible)
456 
457             // TransitionState.STARTED: alternate bouncer -> aod
458             transitionRepository.sendTransitionStep(
459                 TransitionStep(
460                     from = KeyguardState.ALTERNATE_BOUNCER,
461                     to = KeyguardState.AOD,
462                     value = 0f,
463                     transitionState = TransitionState.STARTED,
464                     ownerName = "alternateBouncerToAod",
465                 )
466             )
467             runCurrent()
468             assertThat(transition?.alpha).isEqualTo(1f)
469             assertThat(transition?.scale).isEqualTo(1f)
470             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
471             assertThat(visible).isTrue()
472 
473             // TransitionState.RUNNING: alternate bouncer -> aod
474             transitionRepository.sendTransitionStep(
475                 TransitionStep(
476                     from = KeyguardState.ALTERNATE_BOUNCER,
477                     to = KeyguardState.AOD,
478                     value = .6f,
479                     transitionState = TransitionState.RUNNING,
480                     ownerName = "alternateBouncerToAod",
481                 )
482             )
483             runCurrent()
484             assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
485             assertThat(transition?.scale).isEqualTo(1f)
486             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
487             assertThat(visible).isTrue()
488 
489             // TransitionState.FINISHED: alternate bouncer -> aod
490             transitionRepository.sendTransitionStep(
491                 TransitionStep(
492                     from = KeyguardState.ALTERNATE_BOUNCER,
493                     to = KeyguardState.AOD,
494                     value = 1f,
495                     transitionState = TransitionState.FINISHED,
496                     ownerName = "alternateBouncerToAod",
497                 )
498             )
499             runCurrent()
500             assertThat(transition?.alpha).isEqualTo(0f)
501             assertThat(transition?.scale).isEqualTo(1f)
502             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
503             assertThat(visible).isFalse()
504         }
505 
506     @Test
507     fun lockscreenToOccluded() =
508         testScope.runTest {
509             val transition by collectLastValue(underTest.transition)
510             val visible by collectLastValue(underTest.visible)
511             keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
512 
513             // TransitionState.STARTED: lockscreen -> occluded
514             transitionRepository.sendTransitionStep(
515                 TransitionStep(
516                     from = KeyguardState.LOCKSCREEN,
517                     to = KeyguardState.OCCLUDED,
518                     value = 0f,
519                     transitionState = TransitionState.STARTED,
520                     ownerName = "lockscreenToOccluded",
521                 )
522             )
523             runCurrent()
524             assertThat(transition?.alpha).isEqualTo(1f)
525             assertThat(transition?.scale).isEqualTo(1f)
526             assertThat(transition?.color).isEqualTo(lockscreenColor)
527             assertThat(visible).isTrue()
528 
529             // TransitionState.RUNNING: lockscreen -> occluded
530             transitionRepository.sendTransitionStep(
531                 TransitionStep(
532                     from = KeyguardState.LOCKSCREEN,
533                     to = KeyguardState.OCCLUDED,
534                     value = .6f,
535                     transitionState = TransitionState.RUNNING,
536                     ownerName = "lockscreenToOccluded",
537                 )
538             )
539             runCurrent()
540             assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
541             assertThat(transition?.scale).isEqualTo(1f)
542             assertThat(transition?.color).isEqualTo(lockscreenColor)
543             assertThat(visible).isTrue()
544 
545             // TransitionState.FINISHED: lockscreen -> occluded
546             transitionRepository.sendTransitionStep(
547                 TransitionStep(
548                     from = KeyguardState.LOCKSCREEN,
549                     to = KeyguardState.OCCLUDED,
550                     value = 1f,
551                     transitionState = TransitionState.FINISHED,
552                     ownerName = "lockscreenToOccluded",
553                 )
554             )
555             runCurrent()
556             assertThat(transition?.alpha).isEqualTo(0f)
557             assertThat(transition?.scale).isEqualTo(1f)
558             assertThat(transition?.color).isEqualTo(lockscreenColor)
559             assertThat(visible).isFalse()
560         }
561 
562     @Test
563     fun occludedToLockscreen() =
564         testScope.runTest {
565             val transition by collectLastValue(underTest.transition)
566             val visible by collectLastValue(underTest.visible)
567 
568             // TransitionState.STARTED: occluded -> lockscreen
569             transitionRepository.sendTransitionStep(
570                 TransitionStep(
571                     from = KeyguardState.OCCLUDED,
572                     to = KeyguardState.LOCKSCREEN,
573                     value = 0f,
574                     transitionState = TransitionState.STARTED,
575                     ownerName = "occludedToLockscreen",
576                 )
577             )
578             runCurrent()
579             assertThat(transition?.alpha).isEqualTo(1f)
580             assertThat(transition?.scale).isEqualTo(1f)
581             assertThat(transition?.color).isEqualTo(lockscreenColor)
582             assertThat(visible).isTrue()
583 
584             // TransitionState.RUNNING: occluded -> lockscreen
585             transitionRepository.sendTransitionStep(
586                 TransitionStep(
587                     from = KeyguardState.OCCLUDED,
588                     to = KeyguardState.LOCKSCREEN,
589                     value = .6f,
590                     transitionState = TransitionState.RUNNING,
591                     ownerName = "occludedToLockscreen",
592                 )
593             )
594             runCurrent()
595             assertThat(transition?.alpha).isEqualTo(1f)
596             assertThat(transition?.scale).isEqualTo(1f)
597             assertThat(transition?.color).isEqualTo(lockscreenColor)
598             assertThat(visible).isTrue()
599 
600             // TransitionState.FINISHED: occluded -> lockscreen
601             transitionRepository.sendTransitionStep(
602                 TransitionStep(
603                     from = KeyguardState.OCCLUDED,
604                     to = KeyguardState.LOCKSCREEN,
605                     value = 1f,
606                     transitionState = TransitionState.FINISHED,
607                     ownerName = "occludedToLockscreen",
608                 )
609             )
610             runCurrent()
611             assertThat(transition?.alpha).isEqualTo(1f)
612             assertThat(transition?.scale).isEqualTo(1f)
613             assertThat(transition?.color).isEqualTo(lockscreenColor)
614             assertThat(visible).isTrue()
615         }
616 
617     @Test
618     fun qsProgressChange() =
619         testScope.runTest {
620             val transition by collectLastValue(underTest.transition)
621             val visible by collectLastValue(underTest.visible)
622             givenTransitionToLockscreenFinished()
623 
624             // qsExpansion = 0f
625             shadeRepository.setQsExpansion(0f)
626             runCurrent()
627             assertThat(transition?.alpha).isEqualTo(1f)
628             assertThat(visible).isEqualTo(true)
629 
630             // qsExpansion = .25
631             shadeRepository.setQsExpansion(.2f)
632             runCurrent()
633             assertThat(transition?.alpha).isEqualTo(.6f)
634             assertThat(visible).isEqualTo(true)
635 
636             // qsExpansion = .5
637             shadeRepository.setQsExpansion(.5f)
638             runCurrent()
639             assertThat(transition?.alpha).isEqualTo(0f)
640             assertThat(visible).isEqualTo(false)
641 
642             // qsExpansion = 1
643             shadeRepository.setQsExpansion(1f)
644             runCurrent()
645             assertThat(transition?.alpha).isEqualTo(0f)
646             assertThat(visible).isEqualTo(false)
647         }
648 
649     @Test
650     fun shadeExpansionChanged() =
651         testScope.runTest {
652             val transition by collectLastValue(underTest.transition)
653             val visible by collectLastValue(underTest.visible)
654             givenTransitionToLockscreenFinished()
655 
656             // shadeExpansion = 0f
657             shadeRepository.setUdfpsTransitionToFullShadeProgress(0f)
658             runCurrent()
659             assertThat(transition?.alpha).isEqualTo(1f)
660             assertThat(visible).isEqualTo(true)
661 
662             // shadeExpansion = .2
663             shadeRepository.setUdfpsTransitionToFullShadeProgress(.2f)
664             runCurrent()
665             assertThat(transition?.alpha).isEqualTo(.8f)
666             assertThat(visible).isEqualTo(true)
667 
668             // shadeExpansion = .5
669             shadeRepository.setUdfpsTransitionToFullShadeProgress(.5f)
670             runCurrent()
671             assertThat(transition?.alpha).isEqualTo(.5f)
672             assertThat(visible).isEqualTo(true)
673 
674             // shadeExpansion = 1
675             shadeRepository.setUdfpsTransitionToFullShadeProgress(1f)
676             runCurrent()
677             assertThat(transition?.alpha).isEqualTo(0f)
678             assertThat(visible).isEqualTo(false)
679         }
680 
681     @Test
682     fun dialogHideAffordancesRequestChanged() =
683         testScope.runTest {
684             val transition by collectLastValue(underTest.transition)
685             givenTransitionToLockscreenFinished()
686             runCurrent()
687             val captor = argumentCaptor<SystemUIDialogManager.Listener>()
688             Mockito.verify(dialogManager).registerListener(captor.capture())
689 
690             captor.value.shouldHideAffordances(true)
691             assertThat(transition?.alpha).isEqualTo(0f)
692 
693             captor.value.shouldHideAffordances(false)
694             assertThat(transition?.alpha).isEqualTo(1f)
695         }
696 
697     @Test
698     fun occludedToAlternateBouncer() =
699         testScope.runTest {
700             val transition by collectLastValue(underTest.transition)
701             val visible by collectLastValue(underTest.visible)
702 
703             // TransitionState.STARTED: occluded -> alternate bouncer
704             transitionRepository.sendTransitionStep(
705                 TransitionStep(
706                     from = KeyguardState.OCCLUDED,
707                     to = KeyguardState.ALTERNATE_BOUNCER,
708                     value = 0f,
709                     transitionState = TransitionState.STARTED,
710                     ownerName = "occludedToAlternateBouncer",
711                 )
712             )
713             runCurrent()
714             assertThat(transition?.alpha).isEqualTo(1f)
715             assertThat(transition?.scale).isEqualTo(0f)
716             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
717             assertThat(visible).isTrue()
718 
719             // TransitionState.RUNNING: occluded -> alternate bouncer
720             transitionRepository.sendTransitionStep(
721                 TransitionStep(
722                     from = KeyguardState.OCCLUDED,
723                     to = KeyguardState.ALTERNATE_BOUNCER,
724                     value = .6f,
725                     transitionState = TransitionState.RUNNING,
726                     ownerName = "occludedToAlternateBouncer",
727                 )
728             )
729             runCurrent()
730             assertThat(transition?.alpha).isEqualTo(1f)
731             assertThat(transition?.scale)
732                 .isEqualTo(Interpolators.FAST_OUT_SLOW_IN.getInterpolation(.6f))
733             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
734             assertThat(visible).isTrue()
735 
736             // TransitionState.FINISHED: occluded -> alternate bouncer
737             transitionRepository.sendTransitionStep(
738                 TransitionStep(
739                     from = KeyguardState.OCCLUDED,
740                     to = KeyguardState.ALTERNATE_BOUNCER,
741                     value = 1f,
742                     transitionState = TransitionState.FINISHED,
743                     ownerName = "occludedToAlternateBouncer",
744                 )
745             )
746             runCurrent()
747             assertThat(transition?.alpha).isEqualTo(1f)
748             assertThat(transition?.scale).isEqualTo(1f)
749             assertThat(transition?.color).isEqualTo(alternateBouncerColor)
750             assertThat(visible).isTrue()
751         }
752 
753     private suspend fun givenTransitionToLockscreenFinished() {
754         transitionRepository.sendTransitionStep(
755             TransitionStep(
756                 from = KeyguardState.AOD,
757                 to = KeyguardState.LOCKSCREEN,
758                 value = 1f,
759                 transitionState = TransitionState.FINISHED,
760                 ownerName = "givenTransitionToLockscreenFinished",
761             )
762         )
763     }
764 }
765