<lambda>null1 package com.android.systemui.keyguard
2 
3 import android.app.ActivityManager
4 import android.app.WallpaperManager
5 import android.app.WindowConfiguration
6 import android.graphics.Point
7 import android.graphics.Rect
8 import android.os.PowerManager
9 import android.platform.test.annotations.DisableFlags
10 import android.platform.test.annotations.EnableFlags
11 import android.testing.TestableLooper.RunWithLooper
12 import android.view.RemoteAnimationTarget
13 import android.view.SurfaceControl
14 import android.view.SyncRtSurfaceTransactionApplier
15 import android.view.View
16 import android.view.ViewRootImpl
17 import android.view.WindowManager
18 import androidx.test.ext.junit.runners.AndroidJUnit4
19 import androidx.test.filters.SmallTest
20 import com.android.keyguard.KeyguardViewController
21 import com.android.systemui.Flags
22 import com.android.systemui.SysuiTestCase
23 import com.android.systemui.defaultDeviceState
24 import com.android.systemui.deviceStateManager
25 import com.android.systemui.flags.FeatureFlags
26 import com.android.systemui.kosmos.Kosmos
27 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
28 import com.android.systemui.statusbar.NotificationShadeWindowController
29 import com.android.systemui.statusbar.SysuiStatusBarStateController
30 import com.android.systemui.statusbar.phone.BiometricUnlockController
31 import com.android.systemui.statusbar.policy.KeyguardStateController
32 import com.android.systemui.util.mockito.any
33 import com.android.systemui.util.mockito.argThat
34 import java.util.function.Predicate
35 import junit.framework.Assert.assertEquals
36 import junit.framework.Assert.assertFalse
37 import junit.framework.Assert.assertTrue
38 import org.junit.After
39 import org.junit.Before
40 import org.junit.Test
41 import org.junit.runner.RunWith
42 import org.mockito.Mock
43 import org.mockito.Mockito.atLeastOnce
44 import org.mockito.Mockito.eq
45 import org.mockito.Mockito.mock
46 import org.mockito.Mockito.never
47 import org.mockito.Mockito.times
48 import org.mockito.Mockito.verify
49 import org.mockito.Mockito.verifyNoMoreInteractions
50 import org.mockito.MockitoAnnotations
51 import org.mockito.kotlin.clearInvocations
52 import org.mockito.kotlin.whenever
53 
54 @RunWith(AndroidJUnit4::class)
55 @RunWithLooper
56 @SmallTest
57 class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
58     private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
59 
60     @Mock private lateinit var windowManager: WindowManager
61     @Mock private lateinit var keyguardViewMediator: KeyguardViewMediator
62     @Mock private lateinit var keyguardStateController: KeyguardStateController
63     @Mock private lateinit var keyguardViewController: KeyguardViewController
64     @Mock private lateinit var featureFlags: FeatureFlags
65     @Mock private lateinit var biometricUnlockController: BiometricUnlockController
66     @Mock private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
67     @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
68     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
69     @Mock private lateinit var powerManager: PowerManager
70     @Mock private lateinit var wallpaperManager: WallpaperManager
71     private val kosmos = Kosmos()
72     private val deviceStateManager = kosmos.deviceStateManager
73 
74     @Mock
75     private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub
76 
77     private var surfaceControl1 = mock(SurfaceControl::class.java)
78     private var remoteTarget1 =
79         RemoteAnimationTarget(
80             0 /* taskId */,
81             0,
82             surfaceControl1,
83             false,
84             Rect(),
85             Rect(),
86             0,
87             Point(),
88             Rect(),
89             Rect(),
90             mock(WindowConfiguration::class.java),
91             false,
92             surfaceControl1,
93             Rect(),
94             mock(ActivityManager.RunningTaskInfo::class.java),
95             false
96         )
97 
98     private var surfaceControl2 = mock(SurfaceControl::class.java)
99     private var remoteTarget2 =
100         RemoteAnimationTarget(
101             1 /* taskId */,
102             0,
103             surfaceControl2,
104             false,
105             Rect(),
106             Rect(),
107             0,
108             Point(),
109             Rect(),
110             Rect(),
111             mock(WindowConfiguration::class.java),
112             false,
113             surfaceControl2,
114             Rect(),
115             mock(ActivityManager.RunningTaskInfo::class.java),
116             false
117         )
118     private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
119 
120     private var surfaceControlWp = mock(SurfaceControl::class.java)
121     private var wallpaperTarget =
122         RemoteAnimationTarget(
123             2 /* taskId */,
124             0,
125             surfaceControlWp,
126             false,
127             Rect(),
128             Rect(),
129             0,
130             Point(),
131             Rect(),
132             Rect(),
133             mock(WindowConfiguration::class.java),
134             false,
135             surfaceControlWp,
136             Rect(),
137             mock(ActivityManager.RunningTaskInfo::class.java),
138             false
139         )
140     private lateinit var wallpaperTargets: Array<RemoteAnimationTarget>
141 
142     private var surfaceControlLockWp = mock(SurfaceControl::class.java)
143     private var lockWallpaperTarget =
144         RemoteAnimationTarget(
145             3 /* taskId */,
146             0,
147             surfaceControlLockWp,
148             false,
149             Rect(),
150             Rect(),
151             0,
152             Point(),
153             Rect(),
154             Rect(),
155             mock(WindowConfiguration::class.java),
156             false,
157             surfaceControlLockWp,
158             Rect(),
159             mock(ActivityManager.RunningTaskInfo::class.java),
160             false
161         )
162     private lateinit var lockWallpaperTargets: Array<RemoteAnimationTarget>
163     private var shouldPerformSmartspaceTransition = false
164 
165     @Before
166     fun setUp() {
167         MockitoAnnotations.initMocks(this)
168         keyguardUnlockAnimationController =
169             object :
170                 KeyguardUnlockAnimationController(
171                     windowManager,
172                     context.resources,
173                     keyguardStateController,
174                     { keyguardViewMediator },
175                     keyguardViewController,
176                     featureFlags,
177                     { biometricUnlockController },
178                     statusBarStateController,
179                     notificationShadeWindowController,
180                     powerManager,
181                     wallpaperManager,
182                     deviceStateManager
183                 ) {
184                 override fun shouldPerformSmartspaceTransition(): Boolean =
185                     shouldPerformSmartspaceTransition
186             }
187         keyguardUnlockAnimationController.setLauncherUnlockController(
188             "",
189             launcherUnlockAnimationController
190         )
191 
192         whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
193         whenever(powerManager.isInteractive).thenReturn(true)
194         whenever(deviceStateManager.supportedDeviceStates)
195             .thenReturn(listOf(kosmos.defaultDeviceState))
196 
197         // All of these fields are final, so we can't mock them, but are needed so that the surface
198         // appear amount setter doesn't short circuit.
199         remoteAnimationTargets = arrayOf(remoteTarget1)
200         wallpaperTargets = arrayOf(wallpaperTarget)
201         lockWallpaperTargets = arrayOf(lockWallpaperTarget)
202 
203         // Set the surface applier to our mock so that we can verify the arguments passed to it.
204         // This applier does not have any side effects within the unlock animation controller, so
205         // this is a reasonable way to test.
206         keyguardUnlockAnimationController.surfaceTransactionApplier = surfaceTransactionApplier
207     }
208 
209     @After
210     fun tearDown() {
211         keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
212     }
213 
214     /**
215      * If we're wake and unlocking, we are animating from the black/AOD screen to the app/launcher
216      * underneath. The LightRevealScrim will animate circularly from the fingerprint reader,
217      * revealing the app/launcher below. In this case, we want to make sure we are not animating the
218      * surface, or the user will see the wallpaper briefly as the app animates in.
219      */
220     @Test
221     @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
222     fun noSurfaceAnimation_ifWakeAndUnlocking() {
223         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
224 
225         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
226             remoteAnimationTargets,
227             arrayOf(),
228             arrayOf(),
229             0 /* startTime */,
230             false /* requestedShowSurfaceBehindKeyguard */
231         )
232 
233         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
234         verify(surfaceTransactionApplier, times(1))
235             .scheduleApply(captorSb.capture { sp -> sp.surface == surfaceControl1 })
236 
237         val params = captorSb.getLastValue()
238 
239         // We expect that we've instantly set the surface behind to alpha = 1f, and have no
240         // transforms (translate, scale) on its matrix.
241         assertEquals(1f, params.alpha)
242         assertTrue(params.matrix.isIdentity)
243 
244         // Also expect we've immediately asked the keyguard view mediator to finish the remote
245         // animation.
246         verify(keyguardViewMediator, times(1))
247             .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */)
248 
249         verifyNoMoreInteractions(surfaceTransactionApplier)
250     }
251 
252     /** If we are not wake and unlocking, we expect the unlock animation to play normally. */
253     @Test
254     fun surfaceAnimation_ifNotWakeAndUnlocking() {
255         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
256 
257         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
258             remoteAnimationTargets,
259             wallpaperTargets,
260             arrayOf(),
261             0 /* startTime */,
262             false /* requestedShowSurfaceBehindKeyguard */
263         )
264 
265         // Since the animation is running, we should not have finished the remote animation.
266         verify(keyguardViewMediator, times(0))
267             .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */)
268     }
269 
270     @Test
271     fun onWakeAndUnlock_notifiesListenerWithTrue() {
272         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
273         whenever(biometricUnlockController.mode)
274             .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
275 
276         val listener =
277             mock(KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java)
278         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener)
279 
280         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
281             remoteAnimationTargets,
282             wallpaperTargets,
283             arrayOf(),
284             0 /* startTime */,
285             false /* requestedShowSurfaceBehindKeyguard */
286         )
287 
288         verify(listener).onUnlockAnimationStarted(any(), eq(true), any(), any())
289     }
290 
291     @Test
292     fun onWakeAndUnlockFromDream_notifiesListenerWithFalse() {
293         whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
294         whenever(biometricUnlockController.mode)
295             .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
296 
297         val listener =
298             mock(KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java)
299         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener)
300 
301         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
302             remoteAnimationTargets,
303             wallpaperTargets,
304             arrayOf(),
305             0 /* startTime */,
306             false /* requestedShowSurfaceBehindKeyguard */
307         )
308 
309         verify(listener).onUnlockAnimationStarted(any(), eq(false), any(), any())
310     }
311 
312     /**
313      * If we requested that the surface behind be made visible, and we're not flinging away the
314      * keyguard, it means that we're swiping to unlock and want the surface visible so it can follow
315      * the user's touch event as they swipe to unlock.
316      *
317      * In this case, we should verify that the surface was made visible via the alpha fade in
318      * animator, and verify that we did not start the canned animation to animate the surface in
319      * (since it's supposed to be following the touch events).
320      */
321     @Test
322     fun fadeInSurfaceBehind_ifRequestedShowSurface_butNotFlinging() {
323         whenever(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(false)
324 
325         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
326             remoteAnimationTargets,
327             wallpaperTargets,
328             arrayOf(),
329             0 /* startTime */,
330             true /* requestedShowSurfaceBehindKeyguard */
331         )
332 
333         assertTrue(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
334         assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
335     }
336 
337     /**
338      * We requested the surface behind to be made visible, but we're now flinging to dismiss the
339      * keyguard. This means this was a swipe to dismiss gesture but the user flung the keyguard and
340      * lifted their finger while we were requesting the surface be made visible.
341      *
342      * In this case, we should verify that we are playing the canned unlock animation and not simply
343      * fading in the surface.
344      */
345     @Test
346     fun playCannedUnlockAnimation_ifRequestedShowSurface_andFlinging() {
347         whenever(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(true)
348 
349         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
350             remoteAnimationTargets,
351             wallpaperTargets,
352             arrayOf(),
353             0 /* startTime */,
354             true /* requestedShowSurfaceBehindKeyguard */
355         )
356 
357         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
358         assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
359     }
360 
361     /**
362      * We never requested the surface behind to be made visible, which means no swiping to unlock
363      * ever happened and we're just playing the simple canned animation (happens via UDFPS unlock,
364      * long press on the lock icon, etc).
365      *
366      * In this case, we should verify that we are playing the canned unlock animation and not simply
367      * fading in the surface.
368      */
369     @Test
370     fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() {
371         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
372             remoteAnimationTargets,
373             wallpaperTargets,
374             arrayOf(),
375             0 /* startTime */,
376             false /* requestedShowSurfaceBehindKeyguard */
377         )
378 
379         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
380         assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
381     }
382 
383     @Test
384     fun doNotPlayCannedUnlockAnimation_ifLaunchingApp() {
385         whenever(notificationShadeWindowController.isLaunchingActivity).thenReturn(true)
386 
387         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
388             remoteAnimationTargets,
389             wallpaperTargets,
390             arrayOf(),
391             0 /* startTime */,
392             true /* requestedShowSurfaceBehindKeyguard */
393         )
394 
395         assertFalse(keyguardUnlockAnimationController.canPerformInWindowLauncherAnimations())
396         assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
397     }
398 
399     @Test
400     fun playCannedUnlockAnimation_nullSmartspaceView_doesNotThrowExecption() {
401         keyguardUnlockAnimationController.lockscreenSmartspace = null
402         keyguardUnlockAnimationController.willUnlockWithInWindowLauncherAnimations = true
403 
404         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
405             remoteAnimationTargets,
406             wallpaperTargets,
407             arrayOf(),
408             0 /* startTime */,
409             false /* requestedShowSurfaceBehindKeyguard */
410         )
411 
412         assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
413     }
414 
415     /**
416      * The canned animation should launch a cross fade when there are different wallpapers on lock
417      * and home screen.
418      */
419     @Test
420     @EnableFlags(Flags.FLAG_FASTER_UNLOCK_TRANSITION)
421     fun manualUnlock_multipleWallpapers() {
422         var lastFadeInAlpha = -1f
423         var lastFadeOutAlpha = -1f
424 
425         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
426             arrayOf(remoteTarget1, remoteTarget2),
427             wallpaperTargets,
428             lockWallpaperTargets,
429             0 /* startTime */,
430             false /* requestedShowSurfaceBehindKeyguard */
431         )
432 
433         for (i in 0..10) {
434             clearInvocations(surfaceTransactionApplier)
435             val amount = i / 10f
436 
437             keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(amount)
438 
439             val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
440             verify(surfaceTransactionApplier, times(2))
441                 .scheduleApply(
442                     captorSb.capture { sp ->
443                         sp.surface == surfaceControlWp || sp.surface == surfaceControlLockWp
444                     }
445                 )
446 
447             val fadeInAlpha = captorSb.getLastValue { it.surface == surfaceControlWp }.alpha
448             val fadeOutAlpha = captorSb.getLastValue { it.surface == surfaceControlLockWp }.alpha
449 
450             if (amount == 0f) {
451                 assertTrue(fadeInAlpha == 0f)
452                 assertTrue(fadeOutAlpha == 1f)
453             } else if (amount == 1f) {
454                 assertTrue(fadeInAlpha == 1f)
455                 assertTrue(fadeOutAlpha == 0f)
456             } else {
457                 assertTrue(fadeInAlpha >= lastFadeInAlpha)
458                 assertTrue(fadeOutAlpha <= lastFadeOutAlpha)
459             }
460             lastFadeInAlpha = fadeInAlpha
461             lastFadeOutAlpha = fadeOutAlpha
462         }
463     }
464 
465     /** If we are not wake and unlocking, we expect the unlock animation to play normally. */
466     @Test
467     @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
468     fun surfaceAnimation_multipleTargets() {
469         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
470             arrayOf(remoteTarget1, remoteTarget2),
471             wallpaperTargets,
472             arrayOf(),
473             0 /* startTime */,
474             false /* requestedShowSurfaceBehindKeyguard */
475         )
476 
477         // Cancel the animator so we can verify only the setSurfaceBehind call below.
478         keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.end()
479         clearInvocations(surfaceTransactionApplier)
480 
481         // Set appear to 50%, we'll just verify that we're not applying the identity matrix which
482         // means an animation is in progress.
483         keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f)
484 
485         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
486         verify(surfaceTransactionApplier, times(2))
487             .scheduleApply(
488                 captorSb.capture { sp ->
489                     sp.surface == surfaceControl1 || sp.surface == surfaceControl2
490                 }
491             )
492         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
493         verify(
494                 surfaceTransactionApplier,
495                 times(1).description("WallpaperSurface was expected to receive scheduleApply once")
496             )
497             .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
498 
499         val allParams = captorSb.getAllValues()
500 
501         val remainingTargets = mutableListOf(surfaceControl1, surfaceControl2)
502         allParams.forEach { params ->
503             assertTrue(!params.matrix.isIdentity)
504             remainingTargets.remove(params.surface)
505         }
506 
507         // Make sure we called applyParams with each of the surface controls once. The order does
508         // not matter, so don't explicitly check for that.
509         assertTrue(remainingTargets.isEmpty())
510 
511         // Since the animation is running, we should not have finished the remote animation.
512         verify(keyguardViewMediator, times(0))
513             .exitKeyguardAndFinishSurfaceBehindRemoteAnimation(false /* cancelled */)
514     }
515 
516     @Test
517     @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
518     fun surfaceBehindAlphaOverriddenTo0_ifNotInteractive() {
519         whenever(powerManager.isInteractive).thenReturn(false)
520 
521         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
522             remoteAnimationTargets,
523             wallpaperTargets,
524             arrayOf(),
525             0 /* startTime */,
526             false /* requestedShowSurfaceBehindKeyguard */
527         )
528 
529         // Cancel the animator so we can verify only the setSurfaceBehind call below.
530         keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.end()
531         clearInvocations(surfaceTransactionApplier)
532 
533         keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
534         keyguardUnlockAnimationController.setWallpaperAppearAmount(1f, wallpaperTargets)
535 
536         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
537         verify(surfaceTransactionApplier, times(1))
538             .scheduleApply(captorSb.capture { sp -> sp.surface == surfaceControl1 })
539         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
540         verify(
541                 surfaceTransactionApplier,
542                 atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply")
543             )
544             .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
545 
546         val params = captorSb.getLastValue()
547 
548         // We expect that we've set the surface behind to alpha = 0f since we're not interactive.
549         assertEquals(0f, params.alpha)
550         assertTrue(params.matrix.isIdentity)
551 
552         verifyNoMoreInteractions(surfaceTransactionApplier)
553     }
554 
555     @Test
556     @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
557     fun surfaceBehindAlphaNotOverriddenTo0_ifInteractive() {
558         whenever(powerManager.isInteractive).thenReturn(true)
559 
560         keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
561             remoteAnimationTargets,
562             wallpaperTargets,
563             arrayOf(),
564             0 /* startTime */,
565             false /* requestedShowSurfaceBehindKeyguard */
566         )
567 
568         // Stop the animator - we just want to test whether the override is not applied.
569         keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.end()
570         clearInvocations(surfaceTransactionApplier)
571 
572         keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
573         keyguardUnlockAnimationController.setWallpaperAppearAmount(1f, wallpaperTargets)
574 
575         val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
576         verify(surfaceTransactionApplier, times(1))
577             .scheduleApply(captorSb.capture { sp -> sp.surface == surfaceControl1 })
578         val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
579         verify(
580                 surfaceTransactionApplier,
581                 atLeastOnce().description("Wallpaper surface has  not " + "received scheduleApply")
582             )
583             .scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp })
584 
585         val params = captorSb.getLastValue()
586         assertEquals(1f, params.alpha)
587         assertTrue(params.matrix.isIdentity)
588         assertEquals(
589             "Wallpaper surface was expected to have opacity 1",
590             1f,
591             captorWp.getLastValue().alpha
592         )
593 
594         verifyNoMoreInteractions(surfaceTransactionApplier)
595     }
596 
597     @Test
598     fun unlockToLauncherWithInWindowAnimations_ssViewInVisible_whenPerformSSTransition() {
599         shouldPerformSmartspaceTransition = true
600 
601         val mockLockscreenSmartspaceView = mock(View::class.java)
602         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.VISIBLE)
603         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
604 
605         keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
606 
607         verify(mockLockscreenSmartspaceView).visibility = View.INVISIBLE
608     }
609 
610     @Test
611     fun unlockToLauncherWithInWindowAnimations_ssViewVisible_whenNotPerformSSTransition() {
612         shouldPerformSmartspaceTransition = false
613 
614         val mockLockscreenSmartspaceView = mock(View::class.java)
615         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.VISIBLE)
616         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
617 
618         keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
619 
620         verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE
621     }
622 
623     @Test
624     fun unlockToLauncherWithInWindowAnimations_ssViewIsInvisible() {
625         val mockLockscreenSmartspaceView = mock(View::class.java)
626         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
627         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
628 
629         keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
630 
631         verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE
632     }
633 
634     @Test
635     fun unlockToLauncherWithInWindowAnimations_ssViewIsGone() {
636         val mockLockscreenSmartspaceView = mock(View::class.java)
637         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
638         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
639 
640         keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
641 
642         verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE
643     }
644 
645     @Test
646     fun notifyFinishedKeyguardExitAnimation_ssViewIsInvisibleAndCancelledIsTrue() {
647         val mockLockscreenSmartspaceView = mock(View::class.java)
648         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
649         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
650 
651         keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
652 
653         verify(mockLockscreenSmartspaceView).visibility = View.VISIBLE
654     }
655 
656     @Test
657     fun notifyFinishedKeyguardExitAnimation_ssViewIsGoneAndCancelledIsTrue() {
658         val mockLockscreenSmartspaceView = mock(View::class.java)
659         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
660         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
661 
662         keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
663 
664         verify(mockLockscreenSmartspaceView, never()).visibility = View.VISIBLE
665     }
666 
667     @Test
668     fun notifyFinishedKeyguardExitAnimation_ssViewIsInvisibleAndCancelledIsFalse() {
669         val mockLockscreenSmartspaceView = mock(View::class.java)
670         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
671         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
672 
673         keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(false)
674 
675         verify(mockLockscreenSmartspaceView).visibility = View.VISIBLE
676     }
677 
678     @Test
679     fun notifyFinishedKeyguardExitAnimation_ssViewIsGoneAndCancelledIsFalse() {
680         val mockLockscreenSmartspaceView = mock(View::class.java)
681         whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
682         keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
683 
684         keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(false)
685 
686         verify(mockLockscreenSmartspaceView, never()).visibility = View.VISIBLE
687     }
688 
689     private class ArgThatCaptor<T> {
690         private var allArgs: MutableList<T> = mutableListOf()
691 
692         fun capture(predicate: Predicate<T>): T {
693             return argThat { x: T ->
694                 if (predicate.test(x)) {
695                     allArgs.add(x)
696                     return@argThat true
697                 }
698                 return@argThat false
699             }
700         }
701 
702         fun getLastValue(predicate: Predicate<T>? = null): T {
703             return if (predicate != null) allArgs.last(predicate::test) else allArgs.last()
704         }
705 
706         fun getAllValues(): List<T> {
707             return allArgs
708         }
709     }
710 }
711