• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.shade
18 
19 import android.testing.AndroidTestingRunner
20 import android.testing.TestableLooper.RunWithLooper
21 import android.view.MotionEvent
22 import android.view.ViewGroup
23 import androidx.test.filters.SmallTest
24 import com.android.keyguard.KeyguardSecurityContainerController
25 import com.android.keyguard.LockIconViewController
26 import com.android.keyguard.dagger.KeyguardBouncerComponent
27 import com.android.systemui.R
28 import com.android.systemui.SysuiTestCase
29 import com.android.systemui.classifier.FalsingCollectorFake
30 import com.android.systemui.dock.DockManager
31 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
32 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
33 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
34 import com.android.systemui.keyguard.shared.model.TransitionStep
35 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
36 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
37 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
38 import com.android.systemui.statusbar.LockscreenShadeTransitionController
39 import com.android.systemui.statusbar.NotificationInsetsController
40 import com.android.systemui.statusbar.NotificationShadeDepthController
41 import com.android.systemui.statusbar.NotificationShadeWindowController
42 import com.android.systemui.statusbar.SysuiStatusBarStateController
43 import com.android.systemui.statusbar.notification.stack.AmbientState
44 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
45 import com.android.systemui.statusbar.phone.CentralSurfaces
46 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
47 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
48 import com.android.systemui.statusbar.window.StatusBarWindowStateController
49 import com.android.systemui.util.mockito.any
50 import com.google.common.truth.Truth.assertThat
51 import kotlinx.coroutines.flow.emptyFlow
52 import org.junit.Before
53 import org.junit.Test
54 import org.junit.runner.RunWith
55 import org.mockito.ArgumentCaptor
56 import org.mockito.Mock
57 import org.mockito.Mockito
58 import org.mockito.Mockito.anyFloat
59 import org.mockito.Mockito.mock
60 import org.mockito.Mockito.never
61 import org.mockito.Mockito.verify
62 import org.mockito.Mockito.`when` as whenever
63 import org.mockito.MockitoAnnotations
64 
65 @SmallTest
66 @RunWith(AndroidTestingRunner::class)
67 @RunWithLooper(setAsMainLooper = true)
68 class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
69     @Mock private lateinit var view: NotificationShadeWindowView
70     @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
71     @Mock private lateinit var centralSurfaces: CentralSurfaces
72     @Mock private lateinit var dockManager: DockManager
73     @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
74     @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
75     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
76     @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
77     @Mock private lateinit var ambientState: AmbientState
78     @Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
79     @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
80     @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
81     @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
82     @Mock
83     private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
84     @Mock private lateinit var lockIconViewController: LockIconViewController
85     @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
86     @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
87     @Mock private lateinit var notificationInsetsController: NotificationInsetsController
88     @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
89     @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
90     @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
91     @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
92     @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
93     @Mock
94     lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
95 
96     private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
97     private lateinit var interactionEventHandler: InteractionEventHandler
98 
99     private lateinit var underTest: NotificationShadeWindowViewController
100 
101     @Before
setUpnull102     fun setUp() {
103         MockitoAnnotations.initMocks(this)
104         whenever(view.bottom).thenReturn(VIEW_BOTTOM)
105         whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container))
106             .thenReturn(mock(ViewGroup::class.java))
107         whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java)))
108             .thenReturn(keyguardBouncerComponent)
109         whenever(keyguardBouncerComponent.securityContainerController)
110             .thenReturn(keyguardSecurityContainerController)
111         whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
112             .thenReturn(emptyFlow<TransitionStep>())
113         underTest =
114             NotificationShadeWindowViewController(
115                 lockscreenShadeTransitionController,
116                 FalsingCollectorFake(),
117                 sysuiStatusBarStateController,
118                 dockManager,
119                 notificationShadeDepthController,
120                 view,
121                 notificationPanelViewController,
122                 ShadeExpansionStateManager(),
123                 stackScrollLayoutController,
124                 statusBarKeyguardViewManager,
125                 statusBarWindowStateController,
126                 lockIconViewController,
127                 centralSurfaces,
128                 notificationShadeWindowController,
129                 keyguardUnlockAnimationController,
130                 notificationInsetsController,
131                 ambientState,
132                 pulsingGestureListener,
133                 keyguardBouncerViewModel,
134                 keyguardBouncerComponentFactory,
135                 alternateBouncerInteractor,
136                 keyguardTransitionInteractor,
137                 primaryBouncerToGoneTransitionViewModel,
138             )
139         underTest.setupExpandedStatusBar()
140 
141         interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java)
142         verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
143         interactionEventHandler = interactionEventHandlerCaptor.value
144     }
145 
146     // Note: So far, these tests only cover interactions with the status bar view controller. More
147     // tests need to be added to test the rest of handleDispatchTouchEvent.
148 
149     @Test
handleDispatchTouchEvent_nullStatusBarViewController_returnsFalsenull150     fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() {
151         underTest.setStatusBarViewController(null)
152 
153         val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
154 
155         assertThat(returnVal).isFalse()
156     }
157 
158     @Test
handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSbnull159     fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() {
160         underTest.setStatusBarViewController(phoneStatusBarViewController)
161         val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
162         whenever(phoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
163 
164         val returnVal = interactionEventHandler.handleDispatchTouchEvent(ev)
165 
166         verify(phoneStatusBarViewController).sendTouchToView(ev)
167         assertThat(returnVal).isTrue()
168     }
169 
170     @Test
handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSbnull171     fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
172         underTest.setStatusBarViewController(phoneStatusBarViewController)
173         val downEvBelow =
174             MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
175         interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
176 
177         val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
178         whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
179 
180         val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
181 
182         verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
183         assertThat(returnVal).isTrue()
184     }
185 
186     @Test
handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSbnull187     fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() {
188         underTest.setStatusBarViewController(phoneStatusBarViewController)
189         whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
190         whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
191         whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
192             .thenReturn(true)
193         whenever(phoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true)
194 
195         val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
196 
197         verify(phoneStatusBarViewController).sendTouchToView(downEv)
198         assertThat(returnVal).isTrue()
199     }
200 
201     @Test
handleDispatchTouchEvent_panelNotCollapsed_returnsNullnull202     fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() {
203         underTest.setStatusBarViewController(phoneStatusBarViewController)
204         whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
205         whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
206             .thenReturn(true)
207         // Item we're testing
208         whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(false)
209 
210         val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
211 
212         verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
213         assertThat(returnVal).isNull()
214     }
215 
216     @Test
handleDispatchTouchEvent_touchNotInSbBounds_returnsNullnull217     fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() {
218         underTest.setStatusBarViewController(phoneStatusBarViewController)
219         whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
220         whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
221         // Item we're testing
222         whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
223             .thenReturn(false)
224 
225         val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
226 
227         verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
228         assertThat(returnVal).isNull()
229     }
230 
231     @Test
handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTruenull232     fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() {
233         underTest.setStatusBarViewController(phoneStatusBarViewController)
234         whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
235         whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
236             .thenReturn(true)
237         // Item we're testing
238         whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
239 
240         val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
241 
242         verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
243         assertThat(returnVal).isTrue()
244     }
245 
246     @Test
handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSbnull247     fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() {
248         underTest.setStatusBarViewController(phoneStatusBarViewController)
249         whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
250         whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
251         whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
252             .thenReturn(true)
253 
254         // Down event first
255         interactionEventHandler.handleDispatchTouchEvent(downEv)
256 
257         // Then another event
258         val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
259         whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
260 
261         val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
262 
263         verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
264         assertThat(returnVal).isTrue()
265     }
266 
267     @Test
testGetBouncerContainernull268     fun testGetBouncerContainer() {
269         Mockito.clearInvocations(view)
270         underTest.bouncerContainer
271         verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
272     }
273 
274     @Test
testGetKeyguardMessageAreanull275     fun testGetKeyguardMessageArea() {
276         underTest.keyguardMessageArea
277         verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
278     }
279 
280     @Test
handleDispatchTouchEvent_statusBarViewControllerOnTouch_returnsTruenull281     fun handleDispatchTouchEvent_statusBarViewControllerOnTouch_returnsTrue() {
282         underTest.setStatusBarViewController(phoneStatusBarViewController)
283 
284         // GIVEN the statusBarKeyguardViewManager will handle any touches
285         whenever(statusBarKeyguardViewManager.onTouch(any())).thenReturn(true)
286 
287         // WHEN a touch is dispatched
288         val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
289 
290         // THEN handleDispatchTouchEvent returns true
291         assertThat(returnVal).isTrue()
292     }
293 }
294 
295 private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
296 private const val VIEW_BOTTOM = 100
297