blob: fe1cd978ef91b7a417f7bcfa96f3350be9abc3ae [file] [log] [blame]
package com.android.systemui.statusbar
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import com.android.systemui.ExpandHelper
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.media.MediaHierarchyManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.FakeConfigurationController
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
private fun <T> anyObject(): T {
return Mockito.anyObject<T>()
}
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
lateinit var transitionController: LockscreenShadeTransitionController
lateinit var row: ExpandableNotificationRow
@Mock lateinit var statusbarStateController: SysuiStatusBarStateController
@Mock lateinit var logger: LSShadeTransitionLogger
@Mock lateinit var dumpManager: DumpManager
@Mock lateinit var keyguardBypassController: KeyguardBypassController
@Mock lateinit var lockScreenUserManager: NotificationLockscreenUserManager
@Mock lateinit var falsingCollector: FalsingCollector
@Mock lateinit var ambientState: AmbientState
@Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
@Mock lateinit var scrimController: ScrimController
@Mock lateinit var falsingManager: FalsingManager
@Mock lateinit var notificationPanelController: NotificationPanelViewController
@Mock lateinit var nsslController: NotificationStackScrollLayoutController
@Mock lateinit var depthController: NotificationShadeDepthController
@Mock lateinit var stackscroller: NotificationStackScrollLayout
@Mock lateinit var expandHelperCallback: ExpandHelper.Callback
@Mock lateinit var mCentralSurfaces: CentralSurfaces
@Mock lateinit var qS: QS
@Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
@Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
@JvmField @Rule val mockito = MockitoJUnit.rule()
private val configurationController = FakeConfigurationController()
@Before
fun setup() {
val helper = NotificationTestHelper(
mContext,
mDependency,
TestableLooper.get(this))
row = helper.createRow()
context.getOrCreateTestableResources()
.addOverride(R.bool.config_use_split_notification_shade, false)
context.getOrCreateTestableResources()
.addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
transitionController =
LockscreenShadeTransitionController(
statusBarStateController = statusbarStateController,
logger = logger,
keyguardBypassController = keyguardBypassController,
lockScreenUserManager = lockScreenUserManager,
falsingCollector = falsingCollector,
ambientState = ambientState,
mediaHierarchyManager = mediaHierarchyManager,
depthController = depthController,
wakefulnessLifecycle = wakefulnessLifecycle,
context = context,
configurationController = configurationController,
falsingManager = falsingManager,
dumpManager = dumpManager,
splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
singleShadeOverScrollerFactory = { singleShadeOverScroller },
scrimTransitionController =
LockscreenShadeScrimTransitionController(
scrimController, context, configurationController, dumpManager),
keyguardTransitionControllerFactory = { notificationPanelController ->
LockscreenShadeKeyguardTransitionController(
mediaHierarchyManager,
notificationPanelController,
context,
configurationController,
dumpManager)
})
whenever(nsslController.view).thenReturn(stackscroller)
whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
transitionController.notificationPanelController = notificationPanelController
transitionController.centralSurfaces = mCentralSurfaces
transitionController.qS = qS
transitionController.setStackScroller(nsslController)
whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(nsslController.isInLockedDownShade).thenReturn(false)
whenever(qS.isFullyCollapsed).thenReturn(true)
whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
true)
whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false)
whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
clearInvocations(mCentralSurfaces)
}
@After
fun tearDown() {
transitionController.dragDownAnimator?.cancel()
}
@Test
fun testCantDragDownWhenQSExpanded() {
assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
whenever(qS.isFullyCollapsed).thenReturn(false)
assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
}
@Test
fun testCanDragDownInLockedDownShade() {
whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
assertFalse("Can drag down in shade locked", transitionController.canDragDown())
whenever(nsslController.isInLockedDownShade).thenReturn(true)
assertTrue("Can't drag down in locked down shade", transitionController.canDragDown())
}
@Test
fun testGoingToLockedShade() {
transitionController.goToLockedShade(null)
verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
}
@Test
fun testWakingToShadeLockedWhenDozing() {
whenever(statusbarStateController.isDozing).thenReturn(true)
transitionController.goToLockedShade(null)
verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
assertTrue("Not waking to shade locked", transitionController.isWakingToShadeLocked)
}
@Test
fun testNotWakingToShadeLockedWhenNotDozing() {
whenever(statusbarStateController.isDozing).thenReturn(false)
transitionController.goToLockedShade(null)
verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
assertFalse("Waking to shade locked when not dozing",
transitionController.isWakingToShadeLocked)
}
@Test
fun testGoToLockedShadeOnlyOnKeyguard() {
whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
transitionController.goToLockedShade(null)
whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE)
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
}
@Test
fun testDontGoWhenShadeDisabled() {
whenever(mCentralSurfaces.isShadeDisabled).thenReturn(true)
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
}
@Test
fun testUserExpandsViewOnGoingToFullShade() {
assertFalse("Row shouldn't be user expanded yet", row.isUserExpanded)
transitionController.goToLockedShade(row)
assertTrue("Row wasn't user expanded on drag down", row.isUserExpanded)
}
@Test
fun testTriggeringBouncerWhenPrivateNotificationsArentAllowed() {
whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
false)
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
}
@Test
fun testTriggeringBouncerNoNotificationsOnLockscreen() {
whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
}
@Test
fun testGoToLockedShadeCreatesQSAnimation() {
transitionController.goToLockedShade(null)
verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
verify(notificationPanelController).animateToFullShade(anyLong())
assertNotNull(transitionController.dragDownAnimator)
}
@Test
fun testGoToLockedShadeDoesntCreateQSAnimation() {
transitionController.goToLockedShade(null, needsQSAnimation = false)
verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
verify(notificationPanelController).animateToFullShade(anyLong())
assertNull(transitionController.dragDownAnimator)
}
@Test
fun testDragDownAmountDoesntCallOutInLockedDownShade() {
whenever(nsslController.isInLockedDownShade).thenReturn(true)
transitionController.dragDownAmount = 10f
verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(),
anyBoolean(), anyLong())
verify(qS, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
}
@Test
fun testDragDownAmountCallsOut() {
transitionController.dragDownAmount = 10f
verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(),
anyBoolean(), anyLong())
verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
verify(depthController).transitionToFullShadeProgress = anyFloat()
}
@Test
fun testDragDownAmount_depthDistanceIsZero_setsProgressToZero() {
context.getOrCreateTestableResources()
.addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 0)
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 10f
verify(depthController).transitionToFullShadeProgress = 0f
}
@Test
fun testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistance() {
context.getOrCreateTestableResources()
.addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 10f
verify(depthController).transitionToFullShadeProgress = 0.1f
}
@Test
fun setDragAmount_setsKeyguardTransitionProgress() {
transitionController.dragDownAmount = 10f
verify(notificationPanelController).setKeyguardTransitionProgress(anyFloat(), anyInt())
}
@Test
fun setDragAmount_setsKeyguardAlphaBasedOnDistance() {
val alphaDistance = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance)
transitionController.dragDownAmount = 10f
val expectedAlpha = 1 - 10f / alphaDistance
verify(notificationPanelController)
.setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
}
@Test
fun setDragAmount_notInSplitShade_setsKeyguardTranslationToZero() {
val mediaTranslationY = 123
disableSplitShade()
whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
.thenReturn(mediaTranslationY)
transitionController.dragDownAmount = 10f
verify(notificationPanelController).setKeyguardTransitionProgress(anyFloat(), eq(0))
}
@Test
fun setDragAmount_inSplitShade_setsKeyguardTranslationBasedOnMediaTranslation() {
val mediaTranslationY = 123
enableSplitShade()
whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
.thenReturn(mediaTranslationY)
transitionController.dragDownAmount = 10f
verify(notificationPanelController)
.setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
}
@Test
fun setDragAmount_inSplitShade_mediaNotShowing_setsKeyguardTranslationBasedOnDistance() {
enableSplitShade()
whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(false)
whenever(mediaHierarchyManager.getGuidedTransformationTranslationY()).thenReturn(123)
transitionController.dragDownAmount = 10f
val distance =
context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_keyguard_transition_distance)
val offset =
context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_keyguard_transition_vertical_offset)
val expectedTranslation = 10f / distance * offset
verify(notificationPanelController)
.setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
}
@Test
fun setDragDownAmount_setsValueOnMediaHierarchyManager() {
transitionController.dragDownAmount = 10f
verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
}
@Test
fun setDragAmount_setsScrimProgressBasedOnScrimDistance() {
val distance = 10
context.orCreateTestableResources
.addOverride(R.dimen.lockscreen_shade_scrim_transition_distance, distance)
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 5f
verify(scrimController).transitionToFullShadeProgress(
progress = eq(0.5f),
lockScreenNotificationsProgress = anyFloat()
)
}
@Test
fun setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelay() {
val distance = 100
val delay = 10
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 20f
verify(scrimController).transitionToFullShadeProgress(
progress = anyFloat(),
lockScreenNotificationsProgress = eq(0.1f)
)
}
@Test
fun setDragAmount_dragAmountLessThanNotifDelayDistance_setsNotificationsScrimProgressToZero() {
val distance = 100
val delay = 50
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 20f
verify(scrimController).transitionToFullShadeProgress(
progress = anyFloat(),
lockScreenNotificationsProgress = eq(0f)
)
}
@Test
fun setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOne() {
val distance = 100
val delay = 50
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
context.orCreateTestableResources.addOverride(
R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 999999f
verify(scrimController).transitionToFullShadeProgress(
progress = anyFloat(),
lockScreenNotificationsProgress = eq(1f)
)
}
@Test
fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() {
enableSplitShade()
transitionController.dragDownAmount = 10f
verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
}
@Test
fun setDragAmount_notInSplitShade_forwardsToSingleShadeOverScroller() {
disableSplitShade()
transitionController.dragDownAmount = 10f
verify(singleShadeOverScroller).expansionDragDownAmount = 10f
verifyZeroInteractions(splitShadeOverScroller)
}
@Test
fun setDragAmount_inSplitShade_forwardsToSplitShadeOverScroller() {
enableSplitShade()
transitionController.dragDownAmount = 10f
verify(splitShadeOverScroller).expansionDragDownAmount = 10f
verifyZeroInteractions(singleShadeOverScroller)
}
@Test
fun setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistance() {
val alphaDistance = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance)
val dragDownAmount = 10f
enableSplitShade()
transitionController.dragDownAmount = dragDownAmount
val expectedAlpha = 1 - dragDownAmount / alphaDistance
verify(notificationPanelController).setKeyguardStatusBarAlpha(expectedAlpha)
}
@Test
fun setDragDownAmount_notInSplitShade_setsKeyguardStatusBarAlphaToMinusOne() {
disableSplitShade()
transitionController.dragDownAmount = 10f
verify(notificationPanelController).setKeyguardStatusBarAlpha(-1f)
}
private fun enableSplitShade() {
setSplitShadeEnabled(true)
}
private fun disableSplitShade() {
setSplitShadeEnabled(false)
}
private fun setSplitShadeEnabled(enabled: Boolean) {
overrideResource(R.bool.config_use_split_notification_shade, enabled)
configurationController.notifyConfigurationChanged()
}
/**
* Wrapper around [ScrimController.transitionToFullShadeProgress] that has named parameters for
* clarify and easier refactoring of parameter names.
*/
private fun ScrimController.transitionToFullShadeProgress(
progress: Float,
lockScreenNotificationsProgress: Float
) {
scrimController.setTransitionToFullShadeProgress(
progress,
lockScreenNotificationsProgress
)
}
}