<lambda>null1 package com.android.systemui.statusbar
2
3 import android.testing.AndroidTestingRunner
4 import android.testing.TestableLooper
5 import android.testing.TestableLooper.RunWithLooper
6 import androidx.test.filters.SmallTest
7 import com.android.systemui.ExpandHelper
8 import com.android.systemui.R
9 import com.android.systemui.SysuiTestCase
10 import com.android.systemui.classifier.FalsingCollector
11 import com.android.systemui.dump.DumpManager
12 import com.android.systemui.keyguard.WakefulnessLifecycle
13 import com.android.systemui.media.controls.ui.MediaHierarchyManager
14 import com.android.systemui.plugins.FalsingManager
15 import com.android.systemui.plugins.qs.QS
16 import com.android.systemui.shade.NotificationPanelViewController
17 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
18 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
19 import com.android.systemui.statusbar.notification.stack.AmbientState
20 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
21 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
22 import com.android.systemui.statusbar.phone.CentralSurfaces
23 import com.android.systemui.statusbar.phone.KeyguardBypassController
24 import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
25 import com.android.systemui.statusbar.phone.ScrimController
26 import com.android.systemui.statusbar.policy.FakeConfigurationController
27 import org.junit.After
28 import org.junit.Assert.assertFalse
29 import org.junit.Assert.assertNotNull
30 import org.junit.Assert.assertNull
31 import org.junit.Assert.assertTrue
32 import org.junit.Before
33 import org.junit.Rule
34 import org.junit.Test
35 import org.junit.runner.RunWith
36 import org.mockito.ArgumentMatchers.anyBoolean
37 import org.mockito.ArgumentMatchers.anyFloat
38 import org.mockito.ArgumentMatchers.anyInt
39 import org.mockito.ArgumentMatchers.anyLong
40 import org.mockito.ArgumentMatchers.eq
41 import org.mockito.Mock
42 import org.mockito.Mockito
43 import org.mockito.Mockito.clearInvocations
44 import org.mockito.Mockito.never
45 import org.mockito.Mockito.verify
46 import org.mockito.Mockito.verifyZeroInteractions
47 import org.mockito.Mockito.`when` as whenever
48 import org.mockito.junit.MockitoJUnit
49
50 private fun <T> anyObject(): T {
51 return Mockito.anyObject<T>()
52 }
53
54 @SmallTest
55 @RunWithLooper(setAsMainLooper = true)
56 @RunWith(AndroidTestingRunner::class)
57 class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
58
59 lateinit var transitionController: LockscreenShadeTransitionController
60 lateinit var row: ExpandableNotificationRow
61 @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
62 @Mock lateinit var logger: LSShadeTransitionLogger
63 @Mock lateinit var dumpManager: DumpManager
64 @Mock lateinit var keyguardBypassController: KeyguardBypassController
65 @Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
66 @Mock lateinit var falsingCollector: FalsingCollector
67 @Mock lateinit var ambientState: AmbientState
68 @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
69 @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
70 @Mock lateinit var scrimController: ScrimController
71 @Mock lateinit var falsingManager: FalsingManager
72 @Mock lateinit var notificationPanelController: NotificationPanelViewController
73 @Mock lateinit var nsslController: NotificationStackScrollLayoutController
74 @Mock lateinit var depthController: NotificationShadeDepthController
75 @Mock lateinit var stackscroller: NotificationStackScrollLayout
76 @Mock lateinit var expandHelperCallback: ExpandHelper.Callback
77 @Mock lateinit var mCentralSurfaces: CentralSurfaces
78 @Mock lateinit var qS: QS
79 @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
80 @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
81 @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
82 @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
83 @JvmField @Rule val mockito = MockitoJUnit.rule()
84
85 private val configurationController = FakeConfigurationController()
86
87 @Before
setupnull88 fun setup() {
89 val helper = NotificationTestHelper(
90 mContext,
91 mDependency,
92 TestableLooper.get(this))
93 row = helper.createRow()
94 context.getOrCreateTestableResources()
95 .addOverride(R.bool.config_use_split_notification_shade, false)
96 context.getOrCreateTestableResources()
97 .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
98 transitionController =
99 LockscreenShadeTransitionController(
100 statusBarStateController = statusbarStateController,
101 logger = logger,
102 keyguardBypassController = keyguardBypassController,
103 lockScreenUserManager = lockScreenUserManager,
104 falsingCollector = falsingCollector,
105 ambientState = ambientState,
106 mediaHierarchyManager = mediaHierarchyManager,
107 depthController = depthController,
108 wakefulnessLifecycle = wakefulnessLifecycle,
109 context = context,
110 configurationController = configurationController,
111 falsingManager = falsingManager,
112 dumpManager = dumpManager,
113 splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
114 singleShadeOverScrollerFactory = { singleShadeOverScroller },
115 scrimTransitionController =
116 LockscreenShadeScrimTransitionController(
117 scrimController, context, configurationController, dumpManager),
118 keyguardTransitionControllerFactory = { notificationPanelController ->
119 LockscreenShadeKeyguardTransitionController(
120 mediaHierarchyManager,
121 notificationPanelController,
122 context,
123 configurationController,
124 dumpManager)
125 },
126 qsTransitionControllerFactory = { qsTransitionController },
127 )
128 transitionController.addCallback(transitionControllerCallback)
129 whenever(nsslController.view).thenReturn(stackscroller)
130 whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
131 transitionController.notificationPanelController = notificationPanelController
132 transitionController.centralSurfaces = mCentralSurfaces
133 transitionController.qS = qS
134 transitionController.setStackScroller(nsslController)
135 whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
136 whenever(nsslController.isInLockedDownShade).thenReturn(false)
137 whenever(qS.isFullyCollapsed).thenReturn(true)
138 whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
139 true)
140 whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
141 whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
142 whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false)
143 whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
144 clearInvocations(mCentralSurfaces)
145 }
146
147 @After
tearDownnull148 fun tearDown() {
149 transitionController.dragDownAnimator?.cancel()
150 }
151
152 @Test
testCantDragDownWhenQSExpandednull153 fun testCantDragDownWhenQSExpanded() {
154 assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
155 whenever(qS.isFullyCollapsed).thenReturn(false)
156 assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
157 }
158
159 @Test
testCanDragDownInLockedDownShadenull160 fun testCanDragDownInLockedDownShade() {
161 whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
162 assertFalse("Can drag down in shade locked", transitionController.canDragDown())
163 whenever(nsslController.isInLockedDownShade).thenReturn(true)
164 assertTrue("Can't drag down in locked down shade", transitionController.canDragDown())
165 }
166
167 @Test
testGoingToLockedShadenull168 fun testGoingToLockedShade() {
169 transitionController.goToLockedShade(null)
170 verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
171 }
172
173 @Test
testWakingToShadeLockedWhenDozingnull174 fun testWakingToShadeLockedWhenDozing() {
175 whenever(statusbarStateController.isDozing).thenReturn(true)
176 transitionController.goToLockedShade(null)
177 verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
178 assertTrue("Not waking to shade locked", transitionController.isWakingToShadeLocked)
179 }
180
181 @Test
testNotWakingToShadeLockedWhenNotDozingnull182 fun testNotWakingToShadeLockedWhenNotDozing() {
183 whenever(statusbarStateController.isDozing).thenReturn(false)
184 transitionController.goToLockedShade(null)
185 verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
186 assertFalse("Waking to shade locked when not dozing",
187 transitionController.isWakingToShadeLocked)
188 }
189
190 @Test
testGoToLockedShadeOnlyOnKeyguardnull191 fun testGoToLockedShadeOnlyOnKeyguard() {
192 whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
193 transitionController.goToLockedShade(null)
194 whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE)
195 transitionController.goToLockedShade(null)
196 verify(statusbarStateController, never()).setState(anyInt())
197 }
198
199 @Test
testDontGoWhenShadeDisablednull200 fun testDontGoWhenShadeDisabled() {
201 whenever(mCentralSurfaces.isShadeDisabled).thenReturn(true)
202 transitionController.goToLockedShade(null)
203 verify(statusbarStateController, never()).setState(anyInt())
204 }
205
206 @Test
testUserExpandsViewOnGoingToFullShadenull207 fun testUserExpandsViewOnGoingToFullShade() {
208 assertFalse("Row shouldn't be user expanded yet", row.isUserExpanded)
209 transitionController.goToLockedShade(row)
210 assertTrue("Row wasn't user expanded on drag down", row.isUserExpanded)
211 }
212
213 @Test
testTriggeringBouncerNoNotificationsOnLockscreennull214 fun testTriggeringBouncerNoNotificationsOnLockscreen() {
215 whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
216 transitionController.goToLockedShade(null)
217 verify(statusbarStateController, never()).setState(anyInt())
218 verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
219 verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
220 }
221
222 @Test
testGoToLockedShadeCreatesQSAnimationnull223 fun testGoToLockedShadeCreatesQSAnimation() {
224 transitionController.goToLockedShade(null)
225 verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
226 verify(notificationPanelController).animateToFullShade(anyLong())
227 assertNotNull(transitionController.dragDownAnimator)
228 }
229
230 @Test
testGoToLockedShadeDoesntCreateQSAnimationnull231 fun testGoToLockedShadeDoesntCreateQSAnimation() {
232 transitionController.goToLockedShade(null, needsQSAnimation = false)
233 verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
234 verify(notificationPanelController).animateToFullShade(anyLong())
235 assertNull(transitionController.dragDownAnimator)
236 }
237
238 @Test
testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShadenull239 fun testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShade() {
240 enableSplitShade()
241 transitionController.goToLockedShade(null, needsQSAnimation = true)
242 verify(notificationPanelController).animateToFullShade(anyLong())
243 assertNotNull(transitionController.dragDownAnimator)
244 }
245
246 @Test
testDragDownAmountDoesntCallOutInLockedDownShadenull247 fun testDragDownAmountDoesntCallOutInLockedDownShade() {
248 whenever(nsslController.isInLockedDownShade).thenReturn(true)
249 transitionController.dragDownAmount = 10f
250 verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
251 verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
252 verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
253 verify(transitionControllerCallback, never()).setTransitionToFullShadeAmount(anyFloat(),
254 anyBoolean(), anyLong())
255 verify(qsTransitionController, never()).dragDownAmount = anyFloat()
256 }
257
258 @Test
testDragDownAmountCallsOutnull259 fun testDragDownAmountCallsOut() {
260 transitionController.dragDownAmount = 10f
261 verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
262 verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
263 verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
264 verify(transitionControllerCallback).setTransitionToFullShadeAmount(anyFloat(),
265 anyBoolean(), anyLong())
266 verify(qsTransitionController).dragDownAmount = 10f
267 verify(depthController).transitionToFullShadeProgress = anyFloat()
268 }
269
270 @Test
testDragDownAmount_depthDistanceIsZero_setsProgressToZeronull271 fun testDragDownAmount_depthDistanceIsZero_setsProgressToZero() {
272 context.getOrCreateTestableResources()
273 .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 0)
274 configurationController.notifyConfigurationChanged()
275
276 transitionController.dragDownAmount = 10f
277
278 verify(depthController).transitionToFullShadeProgress = 0f
279 }
280
281 @Test
testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistancenull282 fun testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistance() {
283 context.getOrCreateTestableResources()
284 .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
285 configurationController.notifyConfigurationChanged()
286
287 transitionController.dragDownAmount = 10f
288
289 verify(depthController).transitionToFullShadeProgress = 0.1f
290 }
291
292 @Test
setDragAmount_setsKeyguardTransitionProgressnull293 fun setDragAmount_setsKeyguardTransitionProgress() {
294 transitionController.dragDownAmount = 10f
295
296 verify(notificationPanelController).setKeyguardTransitionProgress(anyFloat(), anyInt())
297 }
298
299 @Test
setDragAmount_setsKeyguardAlphaBasedOnDistancenull300 fun setDragAmount_setsKeyguardAlphaBasedOnDistance() {
301 val alphaDistance = context.resources.getDimensionPixelSize(
302 R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance)
303 transitionController.dragDownAmount = 10f
304
305 val expectedAlpha = 1 - 10f / alphaDistance
306 verify(notificationPanelController)
307 .setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
308 }
309
310 @Test
setDragAmount_notInSplitShade_setsKeyguardTranslationToZeronull311 fun setDragAmount_notInSplitShade_setsKeyguardTranslationToZero() {
312 val mediaTranslationY = 123
313 disableSplitShade()
314 whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
315 whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
316 .thenReturn(mediaTranslationY)
317
318 transitionController.dragDownAmount = 10f
319
320 verify(notificationPanelController).setKeyguardTransitionProgress(anyFloat(), eq(0))
321 }
322
323 @Test
setDragAmount_inSplitShade_setsKeyguardTranslationBasedOnMediaTranslationnull324 fun setDragAmount_inSplitShade_setsKeyguardTranslationBasedOnMediaTranslation() {
325 val mediaTranslationY = 123
326 enableSplitShade()
327 whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
328 whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
329 .thenReturn(mediaTranslationY)
330
331 transitionController.dragDownAmount = 10f
332
333 verify(notificationPanelController)
334 .setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
335 }
336
337 @Test
setDragAmount_inSplitShade_mediaNotShowing_setsKeyguardTranslationBasedOnDistancenull338 fun setDragAmount_inSplitShade_mediaNotShowing_setsKeyguardTranslationBasedOnDistance() {
339 enableSplitShade()
340 whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(false)
341 whenever(mediaHierarchyManager.getGuidedTransformationTranslationY()).thenReturn(123)
342
343 transitionController.dragDownAmount = 10f
344
345 val distance =
346 context.resources.getDimensionPixelSize(
347 R.dimen.lockscreen_shade_keyguard_transition_distance)
348 val offset =
349 context.resources.getDimensionPixelSize(
350 R.dimen.lockscreen_shade_keyguard_transition_vertical_offset)
351 val expectedTranslation = 10f / distance * offset
352 verify(notificationPanelController)
353 .setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
354 }
355
356 @Test
setDragDownAmount_setsValueOnMediaHierarchyManagernull357 fun setDragDownAmount_setsValueOnMediaHierarchyManager() {
358 transitionController.dragDownAmount = 10f
359
360 verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
361 }
362
363 @Test
setDragAmount_setsScrimProgressBasedOnScrimDistancenull364 fun setDragAmount_setsScrimProgressBasedOnScrimDistance() {
365 val distance = 10
366 context.orCreateTestableResources
367 .addOverride(R.dimen.lockscreen_shade_scrim_transition_distance, distance)
368 configurationController.notifyConfigurationChanged()
369
370 transitionController.dragDownAmount = 5f
371
372 verify(scrimController).transitionToFullShadeProgress(
373 progress = eq(0.5f),
374 lockScreenNotificationsProgress = anyFloat()
375 )
376 }
377
378 @Test
setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelaynull379 fun setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelay() {
380 val distance = 100
381 val delay = 10
382 context.orCreateTestableResources.addOverride(
383 R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
384 context.orCreateTestableResources.addOverride(
385 R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
386 configurationController.notifyConfigurationChanged()
387
388 transitionController.dragDownAmount = 20f
389
390 verify(scrimController).transitionToFullShadeProgress(
391 progress = anyFloat(),
392 lockScreenNotificationsProgress = eq(0.1f)
393 )
394 }
395
396 @Test
setDragAmount_dragAmountLessThanNotifDelayDistance_setsNotificationsScrimProgressToZeronull397 fun setDragAmount_dragAmountLessThanNotifDelayDistance_setsNotificationsScrimProgressToZero() {
398 val distance = 100
399 val delay = 50
400 context.orCreateTestableResources.addOverride(
401 R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
402 context.orCreateTestableResources.addOverride(
403 R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
404 configurationController.notifyConfigurationChanged()
405
406 transitionController.dragDownAmount = 20f
407
408 verify(scrimController).transitionToFullShadeProgress(
409 progress = anyFloat(),
410 lockScreenNotificationsProgress = eq(0f)
411 )
412 }
413
414 @Test
setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOnenull415 fun setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOne() {
416 val distance = 100
417 val delay = 50
418 context.orCreateTestableResources.addOverride(
419 R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
420 context.orCreateTestableResources.addOverride(
421 R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
422 configurationController.notifyConfigurationChanged()
423
424 transitionController.dragDownAmount = 999999f
425
426 verify(scrimController).transitionToFullShadeProgress(
427 progress = anyFloat(),
428 lockScreenNotificationsProgress = eq(1f)
429 )
430 }
431
432 @Test
setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManagernull433 fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() {
434 enableSplitShade()
435
436 transitionController.dragDownAmount = 10f
437
438 verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
439 }
440
441 @Test
setDragAmount_notInSplitShade_forwardsToSingleShadeOverScrollernull442 fun setDragAmount_notInSplitShade_forwardsToSingleShadeOverScroller() {
443 disableSplitShade()
444
445 transitionController.dragDownAmount = 10f
446
447 verify(singleShadeOverScroller).expansionDragDownAmount = 10f
448 verifyZeroInteractions(splitShadeOverScroller)
449 }
450
451 @Test
setDragAmount_inSplitShade_forwardsToSplitShadeOverScrollernull452 fun setDragAmount_inSplitShade_forwardsToSplitShadeOverScroller() {
453 enableSplitShade()
454
455 transitionController.dragDownAmount = 10f
456
457 verify(splitShadeOverScroller).expansionDragDownAmount = 10f
458 verifyZeroInteractions(singleShadeOverScroller)
459 }
460
461 @Test
setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistancenull462 fun setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistance() {
463 val alphaDistance = context.resources.getDimensionPixelSize(
464 R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance)
465 val dragDownAmount = 10f
466 enableSplitShade()
467
468 transitionController.dragDownAmount = dragDownAmount
469
470 val expectedAlpha = 1 - dragDownAmount / alphaDistance
471 verify(notificationPanelController).setKeyguardStatusBarAlpha(expectedAlpha)
472 }
473
474 @Test
setDragDownAmount_notInSplitShade_setsKeyguardStatusBarAlphaToMinusOnenull475 fun setDragDownAmount_notInSplitShade_setsKeyguardStatusBarAlphaToMinusOne() {
476 disableSplitShade()
477
478 transitionController.dragDownAmount = 10f
479
480 verify(notificationPanelController).setKeyguardStatusBarAlpha(-1f)
481 }
482
enableSplitShadenull483 private fun enableSplitShade() {
484 setSplitShadeEnabled(true)
485 }
486
disableSplitShadenull487 private fun disableSplitShade() {
488 setSplitShadeEnabled(false)
489 }
490
setSplitShadeEnablednull491 private fun setSplitShadeEnabled(enabled: Boolean) {
492 overrideResource(R.bool.config_use_split_notification_shade, enabled)
493 configurationController.notifyConfigurationChanged()
494 }
495
496 /**
497 * Wrapper around [ScrimController.transitionToFullShadeProgress] that has named parameters for
498 * clarify and easier refactoring of parameter names.
499 */
transitionToFullShadeProgressnull500 private fun ScrimController.transitionToFullShadeProgress(
501 progress: Float,
502 lockScreenNotificationsProgress: Float
503 ) {
504 scrimController.setTransitionToFullShadeProgress(
505 progress,
506 lockScreenNotificationsProgress
507 )
508 }
509 }
510