1 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