<lambda>null1 package com.android.systemui.statusbar
2 
3 import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
4 import android.testing.TestableLooper
5 import android.testing.TestableLooper.RunWithLooper
6 import androidx.test.ext.junit.runners.AndroidJUnit4
7 import androidx.test.filters.SmallTest
8 import com.android.systemui.ExpandHelper
9 import com.android.systemui.SysUITestModule
10 import com.android.systemui.SysuiTestCase
11 import com.android.systemui.TestMocksModule
12 import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
13 import com.android.systemui.classifier.FalsingCollectorFake
14 import com.android.systemui.classifier.FalsingManagerFake
15 import com.android.systemui.dagger.SysUISingleton
16 import com.android.systemui.flags.FakeFeatureFlagsClassicModule
17 import com.android.systemui.flags.Flags
18 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
19 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
20 import com.android.systemui.plugins.qs.QS
21 import com.android.systemui.power.domain.interactor.PowerInteractor
22 import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
23 import com.android.systemui.res.R
24 import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
25 import com.android.systemui.shade.data.repository.FakeShadeRepository
26 import com.android.systemui.shade.domain.interactor.ShadeInteractor
27 import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
28 import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
29 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
30 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
31 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
32 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
33 import com.android.systemui.statusbar.phone.CentralSurfaces
34 import com.android.systemui.statusbar.phone.KeyguardBypassController
35 import com.android.systemui.statusbar.phone.ScrimController
36 import com.android.systemui.statusbar.policy.FakeConfigurationController
37 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
38 import com.android.systemui.user.domain.UserDomainLayerModule
39 import com.android.systemui.util.mockito.any
40 import com.android.systemui.util.mockito.argumentCaptor
41 import com.android.systemui.util.mockito.mock
42 import dagger.BindsInstance
43 import dagger.Component
44 import kotlinx.coroutines.ExperimentalCoroutinesApi
45 import kotlinx.coroutines.test.TestScope
46 import kotlinx.coroutines.test.runCurrent
47 import org.junit.After
48 import org.junit.Assert.assertFalse
49 import org.junit.Assert.assertNotNull
50 import org.junit.Assert.assertNull
51 import org.junit.Assert.assertTrue
52 import org.junit.Before
53 import org.junit.Rule
54 import org.junit.Test
55 import org.junit.runner.RunWith
56 import org.mockito.ArgumentMatchers.anyBoolean
57 import org.mockito.ArgumentMatchers.anyFloat
58 import org.mockito.ArgumentMatchers.anyInt
59 import org.mockito.ArgumentMatchers.anyLong
60 import org.mockito.ArgumentMatchers.eq
61 import org.mockito.ArgumentMatchers.isNull
62 import org.mockito.Mock
63 import org.mockito.Mockito
64 import org.mockito.Mockito.clearInvocations
65 import org.mockito.Mockito.never
66 import org.mockito.Mockito.verify
67 import org.mockito.Mockito.verifyZeroInteractions
68 import org.mockito.junit.MockitoJUnit
69 import org.mockito.Mockito.`when` as whenever
70 
71 private fun <T> anyObject(): T {
72     return Mockito.anyObject<T>()
73 }
74 
75 @SmallTest
76 @RunWithLooper(setAsMainLooper = true)
77 @RunWith(AndroidJUnit4::class)
78 @OptIn(ExperimentalCoroutinesApi::class)
79 class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
80 
81     private lateinit var transitionController: LockscreenShadeTransitionController
82     private lateinit var testComponent: TestComponent
83     private val configurationController
84         get() = testComponent.configurationController
85     private val disableFlagsRepository
86         get() = testComponent.disableFlagsRepository
87     private val testScope
88         get() = testComponent.testScope
89 
<lambda>null90     private val qsSceneAdapter = FakeQSSceneAdapter({ mock() })
91 
92     lateinit var row: ExpandableNotificationRow
93 
94     @Mock lateinit var centralSurfaces: CentralSurfaces
95     @Mock lateinit var depthController: NotificationShadeDepthController
96     @Mock lateinit var expandHelperCallback: ExpandHelper.Callback
97     @Mock lateinit var keyguardBypassController: KeyguardBypassController
98     @Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
99     @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
100     @Mock lateinit var nsslController: NotificationStackScrollLayoutController
101     @Mock lateinit var qS: QS
102     @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
103     @Mock lateinit var scrimController: ScrimController
104     @Mock lateinit var shadeLockscreenInteractor: ShadeLockscreenInteractor
105     @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
106     @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
107     @Mock lateinit var stackscroller: NotificationStackScrollLayout
108     @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
109     @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
110     @Mock lateinit var naturalScrollingSettingObserver: NaturalScrollingSettingObserver
111 
112     @JvmField @Rule val mockito = MockitoJUnit.rule()
113 
114     @Before
setupnull115     fun setup() {
116         val helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
117         row = helper.createRow()
118         context
119             .getOrCreateTestableResources()
120             .addOverride(R.bool.config_use_split_notification_shade, false)
121         context
122             .getOrCreateTestableResources()
123             .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
124 
125         whenever(nsslController.view).thenReturn(stackscroller)
126         whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
127         whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
128         whenever(nsslController.isInLockedDownShade).thenReturn(false)
129         whenever(qS.isFullyCollapsed).thenReturn(true)
130         whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt()))
131             .thenReturn(true)
132         whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
133         whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
134         whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
135         whenever(naturalScrollingSettingObserver.isNaturalScrollingEnabled).thenReturn(true)
136 
137         testComponent =
138             DaggerLockscreenShadeTransitionControllerTest_TestComponent.factory()
139                 .create(
140                     test = this,
141                     featureFlags =
142                         FakeFeatureFlagsClassicModule {
143                             set(Flags.FULL_SCREEN_USER_SWITCHER, false)
144                         },
145                     mocks =
146                         TestMocksModule(
147                             notificationShadeDepthController = depthController,
148                             keyguardBypassController = keyguardBypassController,
149                             mediaHierarchyManager = mediaHierarchyManager,
150                             notificationLockscreenUserManager = lockScreenUserManager,
151                             notificationStackScrollLayoutController = nsslController,
152                             scrimController = scrimController,
153                             statusBarStateController = statusbarStateController,
154                         )
155                 )
156 
157         transitionController =
158             LockscreenShadeTransitionController(
159                 statusBarStateController = statusbarStateController,
160                 logger = mock(),
161                 keyguardBypassController = keyguardBypassController,
162                 lockScreenUserManager = lockScreenUserManager,
163                 falsingCollector = FalsingCollectorFake(),
164                 ambientState = mock(),
165                 mediaHierarchyManager = mediaHierarchyManager,
166                 scrimTransitionController =
167                     LockscreenShadeScrimTransitionController(
168                         scrimController = scrimController,
169                         context = context,
170                         configurationController = configurationController,
171                         dumpManager = mock(),
172                         splitShadeStateController = ResourcesSplitShadeStateController()
173                     ),
174                 keyguardTransitionControllerFactory = { notificationPanelController ->
175                     LockscreenShadeKeyguardTransitionController(
176                         mediaHierarchyManager = mediaHierarchyManager,
177                         shadeLockscreenInteractor = notificationPanelController,
178                         context = context,
179                         configurationController = configurationController,
180                         dumpManager = mock(),
181                         splitShadeStateController = ResourcesSplitShadeStateController()
182                     )
183                 },
184                 depthController = depthController,
185                 context = context,
186                 splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
187                 singleShadeOverScrollerFactory = { singleShadeOverScroller },
188                 activityStarter = mock(),
189                 wakefulnessLifecycle = mock(),
190                 configurationController = configurationController,
191                 falsingManager = FalsingManagerFake(),
192                 dumpManager = mock(),
193                 qsTransitionControllerFactory = { qsTransitionController },
194                 shadeRepository = testComponent.shadeRepository,
195                 shadeInteractor = testComponent.shadeInteractor,
196                 splitShadeStateController = ResourcesSplitShadeStateController(),
197                 shadeLockscreenInteractorLazy = {shadeLockscreenInteractor},
198                 naturalScrollingSettingObserver = naturalScrollingSettingObserver,
199                 lazyQSSceneAdapter = { qsSceneAdapter }
200             )
201 
202         transitionController.addCallback(transitionControllerCallback)
203         transitionController.centralSurfaces = centralSurfaces
204         transitionController.qS = qS
205         transitionController.setStackScroller(nsslController)
206         clearInvocations(centralSurfaces)
207 
208         testScope.runCurrent()
209     }
210 
211     @After
tearDownnull212     fun tearDown() {
213         transitionController.dragDownAnimator?.cancel()
214     }
215 
216     @Test
testCantDragDownWhenQSExpandednull217     fun testCantDragDownWhenQSExpanded() {
218         assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
219         whenever(qS.isFullyCollapsed).thenReturn(false)
220         assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
221     }
222 
223     @Test
testCanDragDownInLockedDownShadenull224     fun testCanDragDownInLockedDownShade() {
225         whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
226         assertFalse("Can drag down in shade locked", transitionController.canDragDown())
227         whenever(nsslController.isInLockedDownShade).thenReturn(true)
228         assertTrue("Can't drag down in locked down shade", transitionController.canDragDown())
229     }
230 
231     @Test
testGoingToLockedShadenull232     fun testGoingToLockedShade() {
233         transitionController.goToLockedShade(null)
234         verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
235     }
236 
237     @Test
testWakingToShadeLockedWhenDozingnull238     fun testWakingToShadeLockedWhenDozing() {
239         whenever(statusbarStateController.isDozing).thenReturn(true)
240         transitionController.goToLockedShade(null)
241         verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
242         assertTrue("Not waking to shade locked", transitionController.isWakingToShadeLocked)
243     }
244 
245     @Test
testNotWakingToShadeLockedWhenNotDozingnull246     fun testNotWakingToShadeLockedWhenNotDozing() {
247         whenever(statusbarStateController.isDozing).thenReturn(false)
248         transitionController.goToLockedShade(null)
249         verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
250         assertFalse(
251             "Waking to shade locked when not dozing",
252             transitionController.isWakingToShadeLocked
253         )
254     }
255 
256     @Test
testGoToLockedShadeOnlyOnKeyguardnull257     fun testGoToLockedShadeOnlyOnKeyguard() {
258         whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
259         transitionController.goToLockedShade(null)
260         whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE)
261         transitionController.goToLockedShade(null)
262         verify(statusbarStateController, never()).setState(anyInt())
263     }
264 
265     @Test
testDontGoWhenShadeDisablednull266     fun testDontGoWhenShadeDisabled() {
267         disableFlagsRepository.disableFlags.value =
268             DisableFlagsModel(
269                 disable2 = DISABLE2_NOTIFICATION_SHADE,
270             )
271         testScope.runCurrent()
272         transitionController.goToLockedShade(null)
273         verify(statusbarStateController, never()).setState(anyInt())
274     }
275 
276     @Test
testUserExpandsViewOnGoingToFullShadenull277     fun testUserExpandsViewOnGoingToFullShade() {
278         assertFalse("Row shouldn't be user expanded yet", row.isUserExpanded)
279         transitionController.goToLockedShade(row)
280         assertTrue("Row wasn't user expanded on drag down", row.isUserExpanded)
281     }
282 
283     @Test
testTriggeringBouncerNoNotificationsOnLockscreennull284     fun testTriggeringBouncerNoNotificationsOnLockscreen() {
285         whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
286         transitionController.goToLockedShade(null)
287         verify(statusbarStateController, never()).setState(anyInt())
288         verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
289         verify(centralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
290     }
291 
292     @Test
testGoToLockedShadeCreatesQSAnimationnull293     fun testGoToLockedShadeCreatesQSAnimation() {
294         transitionController.goToLockedShade(null)
295         verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
296         verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
297         assertNotNull(transitionController.dragDownAnimator)
298     }
299 
300     @Test
testGoToLockedShadeDoesntCreateQSAnimationnull301     fun testGoToLockedShadeDoesntCreateQSAnimation() {
302         transitionController.goToLockedShade(null, needsQSAnimation = false)
303         verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
304         verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
305         assertNull(transitionController.dragDownAnimator)
306     }
307 
308     @Test
testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShadenull309     fun testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShade() {
310         enableSplitShade()
311         transitionController.goToLockedShade(null, needsQSAnimation = true)
312         verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
313         assertNotNull(transitionController.dragDownAnimator)
314     }
315 
316     @Test
testGoToLockedShadeCancelDoesntLeaveShadeOpenOnKeyguardHidenull317     fun testGoToLockedShadeCancelDoesntLeaveShadeOpenOnKeyguardHide() {
318         whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
319         whenever(lockScreenUserManager.isLockscreenPublicMode(any())).thenReturn(true)
320         transitionController.goToLockedShade(null)
321         val captor = argumentCaptor<Runnable>()
322         verify(centralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(isNull(), captor.capture())
323         captor.value.run()
324         verify(statusbarStateController).setLeaveOpenOnKeyguardHide(false)
325     }
326 
327     @Test
testDragDownAmountDoesntCallOutInLockedDownShadenull328     fun testDragDownAmountDoesntCallOutInLockedDownShade() {
329         whenever(nsslController.isInLockedDownShade).thenReturn(true)
330         transitionController.dragDownAmount = 10f
331         verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
332         verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
333         verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
334         verify(transitionControllerCallback, never())
335             .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
336         verify(qsTransitionController, never()).dragDownAmount = anyFloat()
337     }
338 
339     @Test
testDragDownAmountCallsOutnull340     fun testDragDownAmountCallsOut() {
341         transitionController.dragDownAmount = 10f
342         verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
343         verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
344         verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
345         verify(transitionControllerCallback)
346             .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
347         verify(qsTransitionController).dragDownAmount = 10f
348         verify(depthController).transitionToFullShadeProgress = anyFloat()
349     }
350 
351     @Test
testDragDownAmount_depthDistanceIsZero_setsProgressToZeronull352     fun testDragDownAmount_depthDistanceIsZero_setsProgressToZero() {
353         context
354             .getOrCreateTestableResources()
355             .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 0)
356         configurationController.notifyConfigurationChanged()
357 
358         transitionController.dragDownAmount = 10f
359 
360         verify(depthController).transitionToFullShadeProgress = 0f
361     }
362 
363     @Test
testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistancenull364     fun testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistance() {
365         context
366             .getOrCreateTestableResources()
367             .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
368         configurationController.notifyConfigurationChanged()
369 
370         transitionController.dragDownAmount = 10f
371 
372         verify(depthController).transitionToFullShadeProgress = 0.1f
373     }
374 
375     @Test
setDragAmount_setsKeyguardTransitionProgressnull376     fun setDragAmount_setsKeyguardTransitionProgress() {
377         transitionController.dragDownAmount = 10f
378 
379         verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), anyInt())
380     }
381 
382     @Test
setDragAmount_setsKeyguardAlphaBasedOnDistancenull383     fun setDragAmount_setsKeyguardAlphaBasedOnDistance() {
384         val alphaDistance =
385             context.resources.getDimensionPixelSize(
386                 R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
387             )
388         transitionController.dragDownAmount = 10f
389 
390         val expectedAlpha = 1 - 10f / alphaDistance
391         verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
392     }
393 
394     @Test
setDragAmount_notInSplitShade_setsKeyguardTranslationToZeronull395     fun setDragAmount_notInSplitShade_setsKeyguardTranslationToZero() {
396         val mediaTranslationY = 123
397         disableSplitShade()
398         whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
399         whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
400             .thenReturn(mediaTranslationY)
401 
402         transitionController.dragDownAmount = 10f
403 
404         verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), eq(0))
405     }
406 
407     @Test
setDragAmount_inSplitShade_setsKeyguardTranslationBasedOnMediaTranslationnull408     fun setDragAmount_inSplitShade_setsKeyguardTranslationBasedOnMediaTranslation() {
409         val mediaTranslationY = 123
410         enableSplitShade()
411         whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
412         whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
413             .thenReturn(mediaTranslationY)
414 
415         transitionController.dragDownAmount = 10f
416 
417         verify(shadeLockscreenInteractor)
418                 .setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
419     }
420 
421     @Test
setDragAmount_inSplitShade_mediaNotShowing_setsKeyguardTranslationBasedOnDistancenull422     fun setDragAmount_inSplitShade_mediaNotShowing_setsKeyguardTranslationBasedOnDistance() {
423         enableSplitShade()
424         whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(false)
425         whenever(mediaHierarchyManager.getGuidedTransformationTranslationY()).thenReturn(123)
426 
427         transitionController.dragDownAmount = 10f
428 
429         val distance =
430             context.resources.getDimensionPixelSize(
431                 R.dimen.lockscreen_shade_keyguard_transition_distance
432             )
433         val offset =
434             context.resources.getDimensionPixelSize(
435                 R.dimen.lockscreen_shade_keyguard_transition_vertical_offset
436             )
437         val expectedTranslation = 10f / distance * offset
438         verify(shadeLockscreenInteractor)
439             .setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
440     }
441 
442     @Test
setDragDownAmount_setsValueOnMediaHierarchyManagernull443     fun setDragDownAmount_setsValueOnMediaHierarchyManager() {
444         transitionController.dragDownAmount = 10f
445 
446         verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
447     }
448 
449     @Test
setDragAmount_setsScrimProgressBasedOnScrimDistancenull450     fun setDragAmount_setsScrimProgressBasedOnScrimDistance() {
451         val distance = 10
452         context.orCreateTestableResources.addOverride(
453             R.dimen.lockscreen_shade_scrim_transition_distance,
454             distance
455         )
456         configurationController.notifyConfigurationChanged()
457 
458         transitionController.dragDownAmount = 5f
459 
460         verify(scrimController)
461             .transitionToFullShadeProgress(
462                 progress = eq(0.5f),
463                 lockScreenNotificationsProgress = anyFloat()
464             )
465     }
466 
467     @Test
setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelaynull468     fun setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelay() {
469         val distance = 100
470         val delay = 10
471         context.orCreateTestableResources.addOverride(
472             R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
473             distance
474         )
475         context.orCreateTestableResources.addOverride(
476             R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
477             delay
478         )
479         configurationController.notifyConfigurationChanged()
480 
481         transitionController.dragDownAmount = 20f
482 
483         verify(scrimController)
484             .transitionToFullShadeProgress(
485                 progress = anyFloat(),
486                 lockScreenNotificationsProgress = eq(0.1f)
487             )
488     }
489 
490     @Test
setDragAmount_dragAmountLessThanNotifDelayDistance_setsNotificationsScrimProgressToZeronull491     fun setDragAmount_dragAmountLessThanNotifDelayDistance_setsNotificationsScrimProgressToZero() {
492         val distance = 100
493         val delay = 50
494         context.orCreateTestableResources.addOverride(
495             R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
496             distance
497         )
498         context.orCreateTestableResources.addOverride(
499             R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
500             delay
501         )
502         configurationController.notifyConfigurationChanged()
503 
504         transitionController.dragDownAmount = 20f
505 
506         verify(scrimController)
507             .transitionToFullShadeProgress(
508                 progress = anyFloat(),
509                 lockScreenNotificationsProgress = eq(0f)
510             )
511     }
512 
513     @Test
setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOnenull514     fun setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOne() {
515         val distance = 100
516         val delay = 50
517         context.orCreateTestableResources.addOverride(
518             R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
519             distance
520         )
521         context.orCreateTestableResources.addOverride(
522             R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
523             delay
524         )
525         configurationController.notifyConfigurationChanged()
526 
527         transitionController.dragDownAmount = 999999f
528 
529         verify(scrimController)
530             .transitionToFullShadeProgress(
531                 progress = anyFloat(),
532                 lockScreenNotificationsProgress = eq(1f)
533             )
534     }
535 
536     @Test
setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManagernull537     fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() {
538         enableSplitShade()
539 
540         transitionController.dragDownAmount = 10f
541 
542         verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
543     }
544 
545     @Test
setDragAmount_notInSplitShade_forwardsToSingleShadeOverScrollernull546     fun setDragAmount_notInSplitShade_forwardsToSingleShadeOverScroller() {
547         disableSplitShade()
548 
549         transitionController.dragDownAmount = 10f
550 
551         verify(singleShadeOverScroller).expansionDragDownAmount = 10f
552         verifyZeroInteractions(splitShadeOverScroller)
553     }
554 
555     @Test
setDragAmount_inSplitShade_forwardsToSplitShadeOverScrollernull556     fun setDragAmount_inSplitShade_forwardsToSplitShadeOverScroller() {
557         enableSplitShade()
558 
559         transitionController.dragDownAmount = 10f
560 
561         verify(splitShadeOverScroller).expansionDragDownAmount = 10f
562         verifyZeroInteractions(singleShadeOverScroller)
563     }
564 
565     @Test
setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistancenull566     fun setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistance() {
567         val alphaDistance =
568             context.resources.getDimensionPixelSize(
569                 R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
570             )
571         val dragDownAmount = 10f
572         enableSplitShade()
573 
574         transitionController.dragDownAmount = dragDownAmount
575 
576         val expectedAlpha = 1 - dragDownAmount / alphaDistance
577         verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(expectedAlpha)
578     }
579 
580     @Test
setDragDownAmount_notInSplitShade_setsKeyguardStatusBarAlphaToMinusOnenull581     fun setDragDownAmount_notInSplitShade_setsKeyguardStatusBarAlphaToMinusOne() {
582         disableSplitShade()
583 
584         transitionController.dragDownAmount = 10f
585 
586         verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(-1f)
587     }
588 
589     @Test
nullQs_canDragDownFromAdapternull590     fun nullQs_canDragDownFromAdapter() {
591         transitionController.qS = null
592 
593         qsSceneAdapter.isQsFullyCollapsed = true
594         assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
595         qsSceneAdapter.isQsFullyCollapsed = false
596         assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
597     }
598 
enableSplitShadenull599     private fun enableSplitShade() {
600         setSplitShadeEnabled(true)
601     }
602 
disableSplitShadenull603     private fun disableSplitShade() {
604         setSplitShadeEnabled(false)
605     }
606 
setSplitShadeEnablednull607     private fun setSplitShadeEnabled(enabled: Boolean) {
608         overrideResource(R.bool.config_use_split_notification_shade, enabled)
609         configurationController.notifyConfigurationChanged()
610     }
611 
612     /**
613      * Wrapper around [ScrimController.transitionToFullShadeProgress] that has named parameters for
614      * clarify and easier refactoring of parameter names.
615      */
transitionToFullShadeProgressnull616     private fun ScrimController.transitionToFullShadeProgress(
617         progress: Float,
618         lockScreenNotificationsProgress: Float
619     ) {
620         setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress)
621     }
622 
623     @SysUISingleton
624     @Component(
625         modules =
626             [
627                 SysUITestModule::class,
628                 UserDomainLayerModule::class,
629                 BiometricsDomainLayerModule::class,
630             ]
631     )
632     interface TestComponent {
633 
634         val configurationController: FakeConfigurationController
635         val disableFlagsRepository: FakeDisableFlagsRepository
636         val powerInteractor: PowerInteractor
637         val shadeInteractor: ShadeInteractor
638         val shadeRepository: FakeShadeRepository
639         val testScope: TestScope
640 
641         @Component.Factory
642         interface Factory {
createnull643             fun create(
644                     @BindsInstance test: SysuiTestCase,
645                     featureFlags: FakeFeatureFlagsClassicModule,
646                     mocks: TestMocksModule,
647             ): TestComponent
648         }
649     }
650 }
651