blob: 82a57438052fafb60eaa86a47685d804116c8ee9 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.shade
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.MotionEvent
import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityContainerController
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationInsetsController
import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.emptyFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock private lateinit var view: NotificationShadeWindowView
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var centralSurfaces: CentralSurfaces
@Mock private lateinit var dockManager: DockManager
@Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
@Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
@Mock private lateinit var ambientState: AmbientState
@Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
@Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
@Mock
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
@Mock private lateinit var lockIconViewController: LockIconViewController
@Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
@Mock private lateinit var pulsingGestureListener: PulsingGestureListener
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
@Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var interactionEventHandler: InteractionEventHandler
private lateinit var underTest: NotificationShadeWindowViewController
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(view.bottom).thenReturn(VIEW_BOTTOM)
whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container))
.thenReturn(mock(ViewGroup::class.java))
whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java)))
.thenReturn(keyguardBouncerComponent)
whenever(keyguardBouncerComponent.securityContainerController)
.thenReturn(keyguardSecurityContainerController)
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
.thenReturn(emptyFlow<TransitionStep>())
underTest =
NotificationShadeWindowViewController(
lockscreenShadeTransitionController,
FalsingCollectorFake(),
sysuiStatusBarStateController,
dockManager,
notificationShadeDepthController,
view,
notificationPanelViewController,
ShadeExpansionStateManager(),
stackScrollLayoutController,
statusBarKeyguardViewManager,
statusBarWindowStateController,
lockIconViewController,
centralSurfaces,
notificationShadeWindowController,
keyguardUnlockAnimationController,
notificationInsetsController,
ambientState,
pulsingGestureListener,
keyguardBouncerViewModel,
keyguardBouncerComponentFactory,
alternateBouncerInteractor,
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
)
underTest.setupExpandedStatusBar()
interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java)
verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
interactionEventHandler = interactionEventHandlerCaptor.value
}
// Note: So far, these tests only cover interactions with the status bar view controller. More
// tests need to be added to test the rest of handleDispatchTouchEvent.
@Test
fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() {
underTest.setStatusBarViewController(null)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
assertThat(returnVal).isFalse()
}
@Test
fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
whenever(phoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(ev)
verify(phoneStatusBarViewController).sendTouchToView(ev)
assertThat(returnVal).isTrue()
}
@Test
fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
val downEvBelow =
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
assertThat(returnVal).isTrue()
}
@Test
fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
.thenReturn(true)
whenever(phoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
verify(phoneStatusBarViewController).sendTouchToView(downEv)
assertThat(returnVal).isTrue()
}
@Test
fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
.thenReturn(true)
// Item we're testing
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(false)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
assertThat(returnVal).isNull()
}
@Test
fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
// Item we're testing
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
.thenReturn(false)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
assertThat(returnVal).isNull()
}
@Test
fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
.thenReturn(true)
// Item we're testing
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
assertThat(returnVal).isTrue()
}
@Test
fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() {
underTest.setStatusBarViewController(phoneStatusBarViewController)
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
.thenReturn(true)
// Down event first
interactionEventHandler.handleDispatchTouchEvent(downEv)
// Then another event
val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
assertThat(returnVal).isTrue()
}
@Test
fun testGetBouncerContainer() {
Mockito.clearInvocations(view)
underTest.bouncerContainer
verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
}
@Test
fun testGetKeyguardMessageArea() {
underTest.keyguardMessageArea
verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
}
}
private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
private const val VIEW_BOTTOM = 100