<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.testing.AndroidTestingRunner
10 import android.testing.TestableLooper.RunWithLooper
11 import android.view.RemoteAnimationTarget
12 import android.view.SurfaceControl
13 import android.view.SyncRtSurfaceTransactionApplier
14 import android.view.View
15 import android.view.ViewRootImpl
16 import androidx.test.filters.SmallTest
17 import com.android.keyguard.KeyguardViewController
18 import com.android.systemui.SysuiTestCase
19 import com.android.systemui.flags.FeatureFlags
20 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
21 import com.android.systemui.statusbar.NotificationShadeWindowController
22 import com.android.systemui.statusbar.SysuiStatusBarStateController
23 import com.android.systemui.statusbar.phone.BiometricUnlockController
24 import com.android.systemui.statusbar.policy.KeyguardStateController
25 import com.android.systemui.util.mockito.any
26 import com.android.systemui.util.mockito.argThat
27 import com.android.systemui.util.mockito.whenever
28 import junit.framework.Assert.assertEquals
29 import junit.framework.Assert.assertFalse
30 import junit.framework.Assert.assertTrue
31 import org.junit.After
32 import org.junit.Before
33 import org.junit.Test
34 import org.junit.runner.RunWith
35 import org.mockito.Mock
36 import org.mockito.Mockito.atLeastOnce
37 import org.mockito.Mockito.eq
38 import org.mockito.Mockito.mock
39 import org.mockito.Mockito.never
40 import org.mockito.Mockito.times
41 import org.mockito.Mockito.verify
42 import org.mockito.Mockito.verifyNoMoreInteractions
43 import org.mockito.MockitoAnnotations
44 import java.util.function.Predicate
45
46 @RunWith(AndroidTestingRunner::class)
47 @RunWithLooper
48 @SmallTest
49 class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
50 private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
51
52 @Mock
53 private lateinit var keyguardViewMediator: KeyguardViewMediator
54 @Mock
55 private lateinit var keyguardStateController: KeyguardStateController
56 @Mock
57 private lateinit var keyguardViewController: KeyguardViewController
58 @Mock
59 private lateinit var featureFlags: FeatureFlags
60 @Mock
61 private lateinit var biometricUnlockController: BiometricUnlockController
62 @Mock
63 private lateinit var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
64 @Mock
65 private lateinit var statusBarStateController: SysuiStatusBarStateController
66 @Mock
67 private lateinit var notificationShadeWindowController: NotificationShadeWindowController
68 @Mock
69 private lateinit var powerManager: PowerManager
70 @Mock
71 private lateinit var wallpaperManager: WallpaperManager
72
73 @Mock
74 private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub
75
76 private var surfaceControl1 = mock(SurfaceControl::class.java)
77 private var remoteTarget1 = RemoteAnimationTarget(
78 0 /* taskId */, 0, surfaceControl1, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
79 mock(WindowConfiguration::class.java), false, surfaceControl1, Rect(),
80 mock(ActivityManager.RunningTaskInfo::class.java), false)
81
82 private var surfaceControl2 = mock(SurfaceControl::class.java)
83 private var remoteTarget2 = RemoteAnimationTarget(
84 1 /* taskId */, 0, surfaceControl2, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
85 mock(WindowConfiguration::class.java), false, surfaceControl2, Rect(),
86 mock(ActivityManager.RunningTaskInfo::class.java), false)
87 private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
88
89 private var surfaceControlWp = mock(SurfaceControl::class.java)
90 private var wallpaperTarget = RemoteAnimationTarget(
91 2 /* taskId */, 0, surfaceControlWp, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
92 mock(WindowConfiguration::class.java), false, surfaceControlWp, Rect(),
93 mock(ActivityManager.RunningTaskInfo::class.java), false)
94 private lateinit var wallpaperTargets: Array<RemoteAnimationTarget>
95
96 @Before
97 fun setUp() {
98 MockitoAnnotations.initMocks(this)
99 keyguardUnlockAnimationController = KeyguardUnlockAnimationController(
100 context, keyguardStateController, { keyguardViewMediator }, keyguardViewController,
101 featureFlags, { biometricUnlockController }, statusBarStateController,
102 notificationShadeWindowController, powerManager, wallpaperManager
103 )
104 keyguardUnlockAnimationController.setLauncherUnlockController(
105 launcherUnlockAnimationController)
106
107 whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java))
108 whenever(powerManager.isInteractive).thenReturn(true)
109 whenever(wallpaperManager.isLockscreenLiveWallpaperEnabled).thenReturn(false)
110
111 // All of these fields are final, so we can't mock them, but are needed so that the surface
112 // appear amount setter doesn't short circuit.
113 remoteAnimationTargets = arrayOf(remoteTarget1)
114 wallpaperTargets = arrayOf(wallpaperTarget)
115
116 // Set the surface applier to our mock so that we can verify the arguments passed to it.
117 // This applier does not have any side effects within the unlock animation controller, so
118 // this is a reasonable way to test.
119 keyguardUnlockAnimationController.surfaceTransactionApplier = surfaceTransactionApplier
120 }
121
122 @After
123 fun tearDown() {
124 keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
125 }
126
127 /**
128 * If we're wake and unlocking, we are animating from the black/AOD screen to the app/launcher
129 * underneath. The LightRevealScrim will animate circularly from the fingerprint reader,
130 * revealing the app/launcher below. In this case, we want to make sure we are not animating the
131 * surface, or the user will see the wallpaper briefly as the app animates in.
132 */
133 @Test
134 fun noSurfaceAnimation_ifWakeAndUnlocking() {
135 whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
136
137 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
138 remoteAnimationTargets,
139 arrayOf(),
140 0 /* startTime */,
141 false /* requestedShowSurfaceBehindKeyguard */
142 )
143
144 val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
145 verify(surfaceTransactionApplier, times(1)).scheduleApply(
146 captorSb.capture { sp -> sp.surface == surfaceControl1 })
147
148 val params = captorSb.getLastValue()
149
150 // We expect that we've instantly set the surface behind to alpha = 1f, and have no
151 // transforms (translate, scale) on its matrix.
152 assertEquals(1f, params.alpha)
153 assertTrue(params.matrix.isIdentity)
154
155 // Also expect we've immediately asked the keyguard view mediator to finish the remote
156 // animation.
157 verify(keyguardViewMediator, times(1)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
158 false /* cancelled */)
159
160 verifyNoMoreInteractions(surfaceTransactionApplier)
161 }
162
163 /**
164 * If we are not wake and unlocking, we expect the unlock animation to play normally.
165 */
166 @Test
167 fun surfaceAnimation_ifNotWakeAndUnlocking() {
168 whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
169
170 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
171 remoteAnimationTargets,
172 wallpaperTargets,
173 0 /* startTime */,
174 false /* requestedShowSurfaceBehindKeyguard */
175 )
176
177 // Since the animation is running, we should not have finished the remote animation.
178 verify(keyguardViewMediator, times(0)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
179 false /* cancelled */)
180 }
181
182 @Test
183 fun onWakeAndUnlock_notifiesListenerWithTrue() {
184 whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
185 whenever(biometricUnlockController.mode).thenReturn(
186 BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
187
188 val listener = mock(
189 KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java)
190 keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener)
191
192 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
193 remoteAnimationTargets,
194 wallpaperTargets,
195 0 /* startTime */,
196 false /* requestedShowSurfaceBehindKeyguard */
197 )
198
199 verify(listener).onUnlockAnimationStarted(any(), eq(true), any(), any())
200 }
201
202 @Test
203 fun onWakeAndUnlockFromDream_notifiesListenerWithFalse() {
204 whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
205 whenever(biometricUnlockController.mode).thenReturn(
206 BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
207
208 val listener = mock(
209 KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java)
210 keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener)
211
212 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
213 remoteAnimationTargets,
214 wallpaperTargets,
215 0 /* startTime */,
216 false /* requestedShowSurfaceBehindKeyguard */
217 )
218
219 verify(listener).onUnlockAnimationStarted(any(), eq(false), any(), any())
220 }
221
222 /**
223 * If we requested that the surface behind be made visible, and we're not flinging away the
224 * keyguard, it means that we're swiping to unlock and want the surface visible so it can follow
225 * the user's touch event as they swipe to unlock.
226 *
227 * In this case, we should verify that the surface was made visible via the alpha fade in
228 * animator, and verify that we did not start the canned animation to animate the surface in
229 * (since it's supposed to be following the touch events).
230 */
231 @Test
232 fun fadeInSurfaceBehind_ifRequestedShowSurface_butNotFlinging() {
233 whenever(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(false)
234
235 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
236 remoteAnimationTargets,
237 wallpaperTargets,
238 0 /* startTime */,
239 true /* requestedShowSurfaceBehindKeyguard */
240 )
241
242 assertTrue(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
243 assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
244 }
245
246 /**
247 * We requested the surface behind to be made visible, but we're now flinging to dismiss the
248 * keyguard. This means this was a swipe to dismiss gesture but the user flung the keyguard and
249 * lifted their finger while we were requesting the surface be made visible.
250 *
251 * In this case, we should verify that we are playing the canned unlock animation and not
252 * simply fading in the surface.
253 */
254 @Test
255 fun playCannedUnlockAnimation_ifRequestedShowSurface_andFlinging() {
256 whenever(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(true)
257
258 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
259 remoteAnimationTargets,
260 wallpaperTargets,
261 0 /* startTime */,
262 true /* requestedShowSurfaceBehindKeyguard */
263 )
264
265 assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
266 assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
267 }
268
269 /**
270 * We never requested the surface behind to be made visible, which means no swiping to unlock
271 * ever happened and we're just playing the simple canned animation (happens via UDFPS unlock,
272 * long press on the lock icon, etc).
273 *
274 * In this case, we should verify that we are playing the canned unlock animation and not
275 * simply fading in the surface.
276 */
277 @Test
278 fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() {
279 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
280 remoteAnimationTargets,
281 wallpaperTargets,
282 0 /* startTime */,
283 false /* requestedShowSurfaceBehindKeyguard */
284 )
285
286 assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
287 assertFalse(keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.isRunning)
288 }
289
290 @Test
291 fun doNotPlayCannedUnlockAnimation_ifLaunchingApp() {
292 whenever(notificationShadeWindowController.isLaunchingActivity).thenReturn(true)
293
294 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
295 remoteAnimationTargets,
296 wallpaperTargets,
297 0 /* startTime */,
298 true /* requestedShowSurfaceBehindKeyguard */
299 )
300
301 assertFalse(keyguardUnlockAnimationController.canPerformInWindowLauncherAnimations())
302 assertFalse(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
303 }
304
305 @Test
306 fun playCannedUnlockAnimation_nullSmartspaceView_doesNotThrowExecption() {
307 keyguardUnlockAnimationController.lockscreenSmartspace = null
308 keyguardUnlockAnimationController.willUnlockWithInWindowLauncherAnimations = true
309
310 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
311 remoteAnimationTargets,
312 wallpaperTargets,
313 0 /* startTime */,
314 false /* requestedShowSurfaceBehindKeyguard */
315 )
316
317 assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
318 }
319
320 /**
321 * If we are not wake and unlocking, we expect the unlock animation to play normally.
322 */
323 @Test
324 fun surfaceAnimation_multipleTargets() {
325 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
326 arrayOf(remoteTarget1, remoteTarget2),
327 wallpaperTargets,
328 0 /* startTime */,
329 false /* requestedShowSurfaceBehindKeyguard */
330 )
331
332 // Set appear to 50%, we'll just verify that we're not applying the identity matrix which
333 // means an animation is in progress.
334 keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f)
335
336 val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
337 verify(surfaceTransactionApplier, times(2)).scheduleApply(captorSb
338 .capture { sp -> sp.surface == surfaceControl1 || sp.surface == surfaceControl2 })
339 val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
340 verify(surfaceTransactionApplier, times(1).description(
341 "WallpaperSurface was expected to receive scheduleApply once"
342 )).scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp})
343
344 val allParams = captorSb.getAllValues()
345
346 val remainingTargets = mutableListOf(surfaceControl1, surfaceControl2)
347 allParams.forEach { params ->
348 assertTrue(!params.matrix.isIdentity)
349 remainingTargets.remove(params.surface)
350 }
351
352 // Make sure we called applyParams with each of the surface controls once. The order does
353 // not matter, so don't explicitly check for that.
354 assertTrue(remainingTargets.isEmpty())
355
356 // Since the animation is running, we should not have finished the remote animation.
357 verify(keyguardViewMediator, times(0)).exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
358 false /* cancelled */)
359 }
360
361 @Test
362 fun surfaceBehindAlphaOverriddenTo0_ifNotInteractive() {
363 whenever(powerManager.isInteractive).thenReturn(false)
364
365 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
366 remoteAnimationTargets,
367 wallpaperTargets,
368 0 /* startTime */,
369 false /* requestedShowSurfaceBehindKeyguard */
370 )
371
372 keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
373 keyguardUnlockAnimationController.setWallpaperAppearAmount(1f)
374
375 val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
376 verify(surfaceTransactionApplier, times(1)).scheduleApply(
377 captorSb.capture { sp -> sp.surface == surfaceControl1})
378 val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
379 verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has not " +
380 "received scheduleApply")).scheduleApply(
381 captorWp.capture { sp -> sp.surface == surfaceControlWp })
382
383 val params = captorSb.getLastValue()
384
385 // We expect that we've set the surface behind to alpha = 0f since we're not interactive.
386 assertEquals(0f, params.alpha)
387 assertTrue(params.matrix.isIdentity)
388
389 verifyNoMoreInteractions(surfaceTransactionApplier)
390 }
391
392 @Test
393 fun surfaceBehindAlphaNotOverriddenTo0_ifInteractive() {
394 whenever(powerManager.isInteractive).thenReturn(true)
395
396 keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
397 remoteAnimationTargets,
398 wallpaperTargets,
399 0 /* startTime */,
400 false /* requestedShowSurfaceBehindKeyguard */
401 )
402
403 keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
404 keyguardUnlockAnimationController.setWallpaperAppearAmount(1f)
405
406 val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
407 verify(surfaceTransactionApplier, times(1)).scheduleApply(
408 captorSb.capture { sp -> sp.surface == surfaceControl1 })
409 val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
410 verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has not " +
411 "received scheduleApply")).scheduleApply(
412 captorWp.capture { sp -> sp.surface == surfaceControlWp })
413
414 val params = captorSb.getLastValue()
415 assertEquals(1f, params.alpha)
416 assertTrue(params.matrix.isIdentity)
417 assertEquals("Wallpaper surface was expected to have opacity 1",
418 1f, captorWp.getLastValue().alpha)
419
420 verifyNoMoreInteractions(surfaceTransactionApplier)
421 }
422
423 @Test
424 fun unlockToLauncherWithInWindowAnimations_ssViewIsVisible() {
425 val mockLockscreenSmartspaceView = mock(View::class.java)
426 whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.VISIBLE)
427 keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
428
429 keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
430
431 verify(mockLockscreenSmartspaceView).visibility = View.INVISIBLE
432 }
433
434 @Test
435 fun unlockToLauncherWithInWindowAnimations_ssViewIsInvisible() {
436 val mockLockscreenSmartspaceView = mock(View::class.java)
437 whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
438 keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
439
440 keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
441
442 verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE
443 }
444
445 @Test
446 fun unlockToLauncherWithInWindowAnimations_ssViewIsGone() {
447 val mockLockscreenSmartspaceView = mock(View::class.java)
448 whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
449 keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
450
451 keyguardUnlockAnimationController.unlockToLauncherWithInWindowAnimations()
452
453 verify(mockLockscreenSmartspaceView, never()).visibility = View.INVISIBLE
454 }
455
456 @Test
457 fun notifyFinishedKeyguardExitAnimation_ssViewIsInvisibleAndCancelledIsTrue() {
458 val mockLockscreenSmartspaceView = mock(View::class.java)
459 whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
460 keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
461
462 keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
463
464 verify(mockLockscreenSmartspaceView).visibility = View.VISIBLE
465 }
466
467 @Test
468 fun notifyFinishedKeyguardExitAnimation_ssViewIsGoneAndCancelledIsTrue() {
469 val mockLockscreenSmartspaceView = mock(View::class.java)
470 whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
471 keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
472
473 keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(true)
474
475 verify(mockLockscreenSmartspaceView, never()).visibility = View.VISIBLE
476 }
477
478 @Test
479 fun notifyFinishedKeyguardExitAnimation_ssViewIsInvisibleAndCancelledIsFalse() {
480 val mockLockscreenSmartspaceView = mock(View::class.java)
481 whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.INVISIBLE)
482 keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
483
484 keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(false)
485
486 verify(mockLockscreenSmartspaceView).visibility = View.VISIBLE
487 }
488
489 @Test
490 fun notifyFinishedKeyguardExitAnimation_ssViewIsGoneAndCancelledIsFalse() {
491 val mockLockscreenSmartspaceView = mock(View::class.java)
492 whenever(mockLockscreenSmartspaceView.visibility).thenReturn(View.GONE)
493 keyguardUnlockAnimationController.lockscreenSmartspace = mockLockscreenSmartspaceView
494
495 keyguardUnlockAnimationController.notifyFinishedKeyguardExitAnimation(false)
496
497 verify(mockLockscreenSmartspaceView, never()).visibility = View.VISIBLE
498 }
499
500 private class ArgThatCaptor<T> {
501 private var allArgs: MutableList<T> = mutableListOf()
502
503 fun capture(predicate: Predicate<T>): T {
504 return argThat{x: T ->
505 if (predicate.test(x)) {
506 allArgs.add(x)
507 return@argThat true
508 }
509 return@argThat false
510 }
511 }
512
513 fun getLastValue(): T {
514 return allArgs.last()
515 }
516
517 fun getAllValues(): List<T> {
518 return allArgs
519 }
520 }
521 }
522