<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.SysuiTestCase
10 import com.android.systemui.classifier.FalsingCollectorFake
11 import com.android.systemui.classifier.FalsingManagerFake
12 import com.android.systemui.flags.Flags
13 import com.android.systemui.flags.fakeFeatureFlagsClassic
14 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
15 import com.android.systemui.kosmos.testScope
16 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
17 import com.android.systemui.plugins.qs.QS
18 import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
19 import com.android.systemui.res.R
20 import com.android.systemui.shade.data.repository.shadeRepository
21 import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
22 import com.android.systemui.shade.domain.interactor.shadeInteractor
23 import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
24 import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
25 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
26 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
27 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
28 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
29 import com.android.systemui.statusbar.phone.CentralSurfaces
30 import com.android.systemui.statusbar.phone.KeyguardBypassController
31 import com.android.systemui.statusbar.phone.ScrimController
32 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
33 import com.android.systemui.statusbar.policy.fakeConfigurationController
34 import com.android.systemui.testKosmos
35 import com.android.systemui.util.mockito.any
36 import com.android.systemui.util.mockito.argumentCaptor
37 import com.android.systemui.util.mockito.mock
38 import kotlinx.coroutines.ExperimentalCoroutinesApi
39 import kotlinx.coroutines.test.runCurrent
40 import kotlinx.coroutines.test.runTest
41 import org.junit.After
42 import org.junit.Assert.assertFalse
43 import org.junit.Assert.assertNotNull
44 import org.junit.Assert.assertNull
45 import org.junit.Assert.assertTrue
46 import org.junit.Before
47 import org.junit.Rule
48 import org.junit.Test
49 import org.junit.runner.RunWith
50 import org.mockito.ArgumentMatchers.anyBoolean
51 import org.mockito.ArgumentMatchers.anyFloat
52 import org.mockito.ArgumentMatchers.anyInt
53 import org.mockito.ArgumentMatchers.anyLong
54 import org.mockito.ArgumentMatchers.eq
55 import org.mockito.ArgumentMatchers.isNull
56 import org.mockito.Mock
57 import org.mockito.Mockito
58 import org.mockito.Mockito.clearInvocations
59 import org.mockito.Mockito.never
60 import org.mockito.Mockito.verify
61 import org.mockito.Mockito.verifyNoMoreInteractions
62 import org.mockito.Mockito.`when` as whenever
63 import org.mockito.junit.MockitoJUnit
64 
65 private fun <T> anyObject(): T {
66     return Mockito.anyObject<T>()
67 }
68 
69 @SmallTest
70 @RunWithLooper(setAsMainLooper = true)
71 @RunWith(AndroidJUnit4::class)
72 @OptIn(ExperimentalCoroutinesApi::class)
73 class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
74     private val kosmos =
<lambda>null75         testKosmos().apply {
76             fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
77         }
78     private lateinit var transitionController: LockscreenShadeTransitionController
79     private val configurationController = kosmos.fakeConfigurationController
80     private val disableFlagsRepository = kosmos.fakeDisableFlagsRepository
81     private val testScope = kosmos.testScope
82 
<lambda>null83     private val qsSceneAdapter = FakeQSSceneAdapter({ mock() })
84 
85     lateinit var row: ExpandableNotificationRow
86 
87     @Mock lateinit var centralSurfaces: CentralSurfaces
88     @Mock lateinit var depthController: NotificationShadeDepthController
89     @Mock lateinit var expandHelperCallback: ExpandHelper.Callback
90     @Mock lateinit var keyguardBypassController: KeyguardBypassController
91     @Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
92     @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
93     @Mock lateinit var nsslController: NotificationStackScrollLayoutController
94     @Mock lateinit var qS: QS
95     @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
96     @Mock lateinit var scrimController: ScrimController
97     @Mock lateinit var shadeLockscreenInteractor: ShadeLockscreenInteractor
98     @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
99     @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
100     @Mock lateinit var stackscroller: NotificationStackScrollLayout
101     @Mock lateinit var statusbarStateController: SysuiStatusBarStateController
102     @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
103     @Mock lateinit var naturalScrollingSettingObserver: NaturalScrollingSettingObserver
104 
105     @JvmField @Rule val mockito = MockitoJUnit.rule()
106 
107     @Before
setupnull108     fun setup() {
109         val helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
110         row = helper.createRow()
111         context
112             .getOrCreateTestableResources()
113             .addOverride(R.bool.config_use_split_notification_shade, false)
114         context
115             .getOrCreateTestableResources()
116             .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
117 
118         whenever(nsslController.view).thenReturn(stackscroller)
119         whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
120         whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
121         whenever(nsslController.isInLockedDownShade).thenReturn(false)
122         whenever(qS.isFullyCollapsed).thenReturn(true)
123         whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt()))
124             .thenReturn(true)
125         whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
126         whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
127         whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
128         whenever(naturalScrollingSettingObserver.isNaturalScrollingEnabled).thenReturn(true)
129 
130         transitionController =
131             LockscreenShadeTransitionController(
132                 statusBarStateController = statusbarStateController,
133                 logger = mock(),
134                 keyguardBypassController = keyguardBypassController,
135                 lockScreenUserManager = lockScreenUserManager,
136                 falsingCollector = FalsingCollectorFake(),
137                 ambientState = mock(),
138                 mediaHierarchyManager = mediaHierarchyManager,
139                 scrimTransitionController =
140                     LockscreenShadeScrimTransitionController(
141                         scrimController = scrimController,
142                         context = context,
143                         configurationController = configurationController,
144                         dumpManager = mock(),
145                         splitShadeStateController = ResourcesSplitShadeStateController(),
146                     ),
147                 keyguardTransitionControllerFactory = { notificationPanelController ->
148                     LockscreenShadeKeyguardTransitionController(
149                         mediaHierarchyManager = mediaHierarchyManager,
150                         shadeLockscreenInteractor = notificationPanelController,
151                         context = context,
152                         configurationController = configurationController,
153                         dumpManager = mock(),
154                         splitShadeStateController = ResourcesSplitShadeStateController(),
155                     )
156                 },
157                 depthController = depthController,
158                 context = context,
159                 splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
160                 singleShadeOverScrollerFactory = { singleShadeOverScroller },
161                 activityStarter = mock(),
162                 wakefulnessLifecycle = mock(),
163                 configurationController = configurationController,
164                 falsingManager = FalsingManagerFake(),
165                 dumpManager = mock(),
166                 qsTransitionControllerFactory = { qsTransitionController },
167                 shadeRepository = kosmos.shadeRepository,
168                 shadeInteractor = kosmos.shadeInteractor,
169                 splitShadeStateController = ResourcesSplitShadeStateController(),
170                 shadeLockscreenInteractorLazy = { shadeLockscreenInteractor },
171                 naturalScrollingSettingObserver = naturalScrollingSettingObserver,
172                 lazyQSSceneAdapter = { qsSceneAdapter },
173             )
174 
175         transitionController.addCallback(transitionControllerCallback)
176         transitionController.centralSurfaces = centralSurfaces
177         transitionController.qS = qS
178         transitionController.setStackScroller(nsslController)
179         clearInvocations(centralSurfaces)
180 
181         testScope.runCurrent()
182     }
183 
184     @After
tearDownnull185     fun tearDown() {
186         transitionController.dragDownAnimator?.cancel()
187     }
188 
189     @Test
testCantDragDownWhenQSExpandednull190     fun testCantDragDownWhenQSExpanded() =
191         testScope.runTest {
192             assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
193             whenever(qS.isFullyCollapsed).thenReturn(false)
194             assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
195         }
196 
197     @Test
testCanDragDownInLockedDownShadenull198     fun testCanDragDownInLockedDownShade() =
199         testScope.runTest {
200             whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
201             assertFalse("Can drag down in shade locked", transitionController.canDragDown())
202             whenever(nsslController.isInLockedDownShade).thenReturn(true)
203             assertTrue("Can't drag down in locked down shade", transitionController.canDragDown())
204         }
205 
206     @Test
testGoingToLockedShadenull207     fun testGoingToLockedShade() =
208         testScope.runTest {
209             transitionController.goToLockedShade(null)
210             verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
211         }
212 
213     @Test
testWakingToShadeLockedWhenDozingnull214     fun testWakingToShadeLockedWhenDozing() =
215         testScope.runTest {
216             whenever(statusbarStateController.isDozing).thenReturn(true)
217             transitionController.goToLockedShade(null)
218             verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
219             assertTrue("Not waking to shade locked", transitionController.isWakingToShadeLocked)
220         }
221 
222     @Test
testNotWakingToShadeLockedWhenNotDozingnull223     fun testNotWakingToShadeLockedWhenNotDozing() =
224         testScope.runTest {
225             whenever(statusbarStateController.isDozing).thenReturn(false)
226             transitionController.goToLockedShade(null)
227             verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
228             assertFalse(
229                 "Waking to shade locked when not dozing",
230                 transitionController.isWakingToShadeLocked,
231             )
232         }
233 
234     @Test
testGoToLockedShadeOnlyOnKeyguardnull235     fun testGoToLockedShadeOnlyOnKeyguard() =
236         testScope.runTest {
237             whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
238             transitionController.goToLockedShade(null)
239             whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE)
240             transitionController.goToLockedShade(null)
241             verify(statusbarStateController, never()).setState(anyInt())
242         }
243 
244     @Test
testDontGoWhenShadeDisablednull245     fun testDontGoWhenShadeDisabled() =
246         testScope.runTest {
247             disableFlagsRepository.disableFlags.value =
248                 DisableFlagsModel(disable2 = DISABLE2_NOTIFICATION_SHADE)
249             testScope.runCurrent()
250             transitionController.goToLockedShade(null)
251             verify(statusbarStateController, never()).setState(anyInt())
252         }
253 
254     @Test
testUserExpandsViewOnGoingToFullShadenull255     fun testUserExpandsViewOnGoingToFullShade() =
256         testScope.runTest {
257             assertFalse("Row shouldn't be user expanded yet", row.isUserExpanded)
258             transitionController.goToLockedShade(row)
259             assertTrue("Row wasn't user expanded on drag down", row.isUserExpanded)
260         }
261 
262     @Test
testTriggeringBouncerNoNotificationsOnLockscreennull263     fun testTriggeringBouncerNoNotificationsOnLockscreen() =
264         testScope.runTest {
265             whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
266             transitionController.goToLockedShade(null)
267             verify(statusbarStateController, never()).setState(anyInt())
268             verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
269             verify(centralSurfaces)
270                 .showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
271         }
272 
273     @Test
testGoToLockedShadeCreatesQSAnimationnull274     fun testGoToLockedShadeCreatesQSAnimation() =
275         testScope.runTest {
276             transitionController.goToLockedShade(null)
277             verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
278             verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
279             assertNotNull(transitionController.dragDownAnimator)
280         }
281 
282     @Test
testGoToLockedShadeDoesntCreateQSAnimationnull283     fun testGoToLockedShadeDoesntCreateQSAnimation() =
284         testScope.runTest {
285             transitionController.goToLockedShade(null, needsQSAnimation = false)
286             verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
287             verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
288             assertNull(transitionController.dragDownAnimator)
289         }
290 
291     @Test
testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShadenull292     fun testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShade() =
293         testScope.runTest {
294             enableSplitShade()
295             transitionController.goToLockedShade(null, needsQSAnimation = true)
296             verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
297             assertNotNull(transitionController.dragDownAnimator)
298         }
299 
300     @Test
testGoToLockedShadeCancelDoesntLeaveShadeOpenOnKeyguardHidenull301     fun testGoToLockedShadeCancelDoesntLeaveShadeOpenOnKeyguardHide() =
302         testScope.runTest {
303             whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
304             whenever(lockScreenUserManager.isLockscreenPublicMode(any())).thenReturn(true)
305             transitionController.goToLockedShade(null)
306             val captor = argumentCaptor<Runnable>()
307             verify(centralSurfaces)
308                 .showBouncerWithDimissAndCancelIfKeyguard(isNull(), captor.capture())
309             captor.value.run()
310             verify(statusbarStateController).setLeaveOpenOnKeyguardHide(false)
311         }
312 
313     @Test
testDragDownAmountDoesntCallOutInLockedDownShadenull314     fun testDragDownAmountDoesntCallOutInLockedDownShade() =
315         testScope.runTest {
316             whenever(nsslController.isInLockedDownShade).thenReturn(true)
317             transitionController.dragDownAmount = 10f
318             verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
319             verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
320             verify(scrimController, never())
321                 .setTransitionToFullShadeProgress(anyFloat(), anyFloat())
322             verify(transitionControllerCallback, never())
323                 .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
324             verify(qsTransitionController, never()).dragDownAmount = anyFloat()
325         }
326 
327     @Test
testDragDownAmountCallsOutnull328     fun testDragDownAmountCallsOut() =
329         testScope.runTest {
330             transitionController.dragDownAmount = 10f
331             verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
332             verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
333             verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
334             verify(transitionControllerCallback)
335                 .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
336             verify(qsTransitionController).dragDownAmount = 10f
337             verify(depthController).transitionToFullShadeProgress = anyFloat()
338         }
339 
340     @Test
testDragDownAmount_depthDistanceIsZero_setsProgressToZeronull341     fun testDragDownAmount_depthDistanceIsZero_setsProgressToZero() =
342         testScope.runTest {
343             context
344                 .getOrCreateTestableResources()
345                 .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 0)
346             configurationController.notifyConfigurationChanged()
347 
348             transitionController.dragDownAmount = 10f
349 
350             verify(depthController).transitionToFullShadeProgress = 0f
351         }
352 
353     @Test
testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistancenull354     fun testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistance() =
355         testScope.runTest {
356             context
357                 .getOrCreateTestableResources()
358                 .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
359             configurationController.notifyConfigurationChanged()
360 
361             transitionController.dragDownAmount = 10f
362 
363             verify(depthController).transitionToFullShadeProgress = 0.1f
364         }
365 
366     @Test
setDragAmount_setsKeyguardTransitionProgressnull367     fun setDragAmount_setsKeyguardTransitionProgress() =
368         testScope.runTest {
369             transitionController.dragDownAmount = 10f
370 
371             verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), anyInt())
372         }
373 
374     @Test
setDragAmount_setsKeyguardAlphaBasedOnDistancenull375     fun setDragAmount_setsKeyguardAlphaBasedOnDistance() =
376         testScope.runTest {
377             val alphaDistance =
378                 context.resources.getDimensionPixelSize(
379                     R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
380                 )
381             transitionController.dragDownAmount = 10f
382 
383             val expectedAlpha = 1 - 10f / alphaDistance
384             verify(shadeLockscreenInteractor)
385                 .setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
386         }
387 
388     @Test
setDragAmount_notInSplitShade_setsKeyguardTranslationToZeronull389     fun setDragAmount_notInSplitShade_setsKeyguardTranslationToZero() =
390         testScope.runTest {
391             val mediaTranslationY = 123
392             disableSplitShade()
393             whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
394             whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
395                 .thenReturn(mediaTranslationY)
396 
397             transitionController.dragDownAmount = 10f
398 
399             verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), eq(0))
400         }
401 
402     @Test
setDragAmount_inSplitShade_setsKeyguardTranslationBasedOnMediaTranslationnull403     fun setDragAmount_inSplitShade_setsKeyguardTranslationBasedOnMediaTranslation() =
404         testScope.runTest {
405             val mediaTranslationY = 123
406             enableSplitShade()
407             whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
408             whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
409                 .thenReturn(mediaTranslationY)
410 
411             transitionController.dragDownAmount = 10f
412 
413             verify(shadeLockscreenInteractor)
414                 .setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
415         }
416 
417     @Test
setDragAmount_inSplitShade_mediaNotShowing_setsKeyguardTranslationBasedOnDistancenull418     fun setDragAmount_inSplitShade_mediaNotShowing_setsKeyguardTranslationBasedOnDistance() =
419         testScope.runTest {
420             enableSplitShade()
421             whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(false)
422             whenever(mediaHierarchyManager.getGuidedTransformationTranslationY()).thenReturn(123)
423 
424             transitionController.dragDownAmount = 10f
425 
426             val distance =
427                 context.resources.getDimensionPixelSize(
428                     R.dimen.lockscreen_shade_keyguard_transition_distance
429                 )
430             val offset =
431                 context.resources.getDimensionPixelSize(
432                     R.dimen.lockscreen_shade_keyguard_transition_vertical_offset
433                 )
434             val expectedTranslation = 10f / distance * offset
435             verify(shadeLockscreenInteractor)
436                 .setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
437         }
438 
439     @Test
setDragDownAmount_setsValueOnMediaHierarchyManagernull440     fun setDragDownAmount_setsValueOnMediaHierarchyManager() =
441         testScope.runTest {
442             transitionController.dragDownAmount = 10f
443 
444             verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
445         }
446 
447     @Test
setDragAmount_setsScrimProgressBasedOnScrimDistancenull448     fun setDragAmount_setsScrimProgressBasedOnScrimDistance() =
449         testScope.runTest {
450             val distance = 10
451             context.orCreateTestableResources.addOverride(
452                 R.dimen.lockscreen_shade_scrim_transition_distance,
453                 distance,
454             )
455             configurationController.notifyConfigurationChanged()
456 
457             transitionController.dragDownAmount = 5f
458 
459             verify(scrimController)
460                 .transitionToFullShadeProgress(
461                     progress = eq(0.5f),
462                     lockScreenNotificationsProgress = anyFloat(),
463                 )
464         }
465 
466     @Test
setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelaynull467     fun setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelay() =
468         testScope.runTest {
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         testScope.runTest {
493             val distance = 100
494             val delay = 50
495             context.orCreateTestableResources.addOverride(
496                 R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
497                 distance,
498             )
499             context.orCreateTestableResources.addOverride(
500                 R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
501                 delay,
502             )
503             configurationController.notifyConfigurationChanged()
504 
505             transitionController.dragDownAmount = 20f
506 
507             verify(scrimController)
508                 .transitionToFullShadeProgress(
509                     progress = anyFloat(),
510                     lockScreenNotificationsProgress = eq(0f),
511                 )
512         }
513 
514     @Test
setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOnenull515     fun setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOne() =
516         testScope.runTest {
517             val distance = 100
518             val delay = 50
519             context.orCreateTestableResources.addOverride(
520                 R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
521                 distance,
522             )
523             context.orCreateTestableResources.addOverride(
524                 R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
525                 delay,
526             )
527             configurationController.notifyConfigurationChanged()
528 
529             transitionController.dragDownAmount = 999999f
530 
531             verify(scrimController)
532                 .transitionToFullShadeProgress(
533                     progress = anyFloat(),
534                     lockScreenNotificationsProgress = eq(1f),
535                 )
536         }
537 
538     @Test
setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManagernull539     fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() =
540         testScope.runTest {
541             enableSplitShade()
542 
543             transitionController.dragDownAmount = 10f
544 
545             verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
546         }
547 
548     @Test
setDragAmount_notInSplitShade_forwardsToSingleShadeOverScrollernull549     fun setDragAmount_notInSplitShade_forwardsToSingleShadeOverScroller() =
550         testScope.runTest {
551             disableSplitShade()
552 
553             transitionController.dragDownAmount = 10f
554 
555             verify(singleShadeOverScroller).expansionDragDownAmount = 10f
556             verifyNoMoreInteractions(splitShadeOverScroller)
557         }
558 
559     @Test
setDragAmount_inSplitShade_forwardsToSplitShadeOverScrollernull560     fun setDragAmount_inSplitShade_forwardsToSplitShadeOverScroller() =
561         testScope.runTest {
562             enableSplitShade()
563 
564             transitionController.dragDownAmount = 10f
565 
566             verify(splitShadeOverScroller).expansionDragDownAmount = 10f
567             verifyNoMoreInteractions(singleShadeOverScroller)
568         }
569 
570     @Test
setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistancenull571     fun setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistance() =
572         testScope.runTest {
573             val alphaDistance =
574                 context.resources.getDimensionPixelSize(
575                     R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
576                 )
577             val dragDownAmount = 10f
578             enableSplitShade()
579 
580             transitionController.dragDownAmount = dragDownAmount
581 
582             val expectedAlpha = 1 - dragDownAmount / alphaDistance
583             verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(expectedAlpha)
584         }
585 
586     @Test
setDragDownAmount_notInSplitShade_setsKeyguardStatusBarAlphaToMinusOnenull587     fun setDragDownAmount_notInSplitShade_setsKeyguardStatusBarAlphaToMinusOne() =
588         testScope.runTest {
589             disableSplitShade()
590 
591             transitionController.dragDownAmount = 10f
592 
593             verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(-1f)
594         }
595 
596     @Test
nullQs_canDragDownFromAdapternull597     fun nullQs_canDragDownFromAdapter() =
598         testScope.runTest {
599             transitionController.qS = null
600 
601             qsSceneAdapter.isQsFullyCollapsed = true
602             assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
603             qsSceneAdapter.isQsFullyCollapsed = false
604             assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
605         }
606 
enableSplitShadenull607     private fun enableSplitShade() {
608         setSplitShadeEnabled(true)
609     }
610 
disableSplitShadenull611     private fun disableSplitShade() {
612         setSplitShadeEnabled(false)
613     }
614 
setSplitShadeEnablednull615     private fun setSplitShadeEnabled(enabled: Boolean) {
616         overrideResource(R.bool.config_use_split_notification_shade, enabled)
617         configurationController.notifyConfigurationChanged()
618     }
619 
620     /**
621      * Wrapper around [ScrimController.transitionToFullShadeProgress] that has named parameters for
622      * clarify and easier refactoring of parameter names.
623      */
transitionToFullShadeProgressnull624     private fun ScrimController.transitionToFullShadeProgress(
625         progress: Float,
626         lockScreenNotificationsProgress: Float,
627     ) {
628         setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress)
629     }
630 }
631