1 /*
2  * Copyright (C) 2017 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 package com.android.systemui.shade
17 
18 import android.os.SystemClock
19 import android.testing.AndroidTestingRunner
20 import android.testing.TestableLooper.RunWithLooper
21 import android.view.MotionEvent
22 import android.widget.FrameLayout
23 import androidx.test.filters.SmallTest
24 import com.android.keyguard.KeyguardMessageAreaController
25 import com.android.keyguard.KeyguardSecurityContainerController
26 import com.android.keyguard.LockIconViewController
27 import com.android.keyguard.dagger.KeyguardBouncerComponent
28 import com.android.systemui.R
29 import com.android.systemui.SysuiTestCase
30 import com.android.systemui.back.domain.interactor.BackActionInteractor
31 import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
32 import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
33 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
34 import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
35 import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
36 import com.android.systemui.classifier.FalsingCollectorFake
37 import com.android.systemui.dock.DockManager
38 import com.android.systemui.dump.logcatLogBuffer
39 import com.android.systemui.flags.FakeFeatureFlags
40 import com.android.systemui.flags.Flags
41 import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
42 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
43 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
44 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
45 import com.android.systemui.log.BouncerLogger
46 import com.android.systemui.power.domain.interactor.PowerInteractor
47 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
48 import com.android.systemui.statusbar.DragDownHelper
49 import com.android.systemui.statusbar.LockscreenShadeTransitionController
50 import com.android.systemui.statusbar.NotificationInsetsController
51 import com.android.systemui.statusbar.NotificationShadeDepthController
52 import com.android.systemui.statusbar.NotificationShadeWindowController
53 import com.android.systemui.statusbar.SysuiStatusBarStateController
54 import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
55 import com.android.systemui.statusbar.notification.stack.AmbientState
56 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
57 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
58 import com.android.systemui.statusbar.phone.CentralSurfaces
59 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
60 import com.android.systemui.statusbar.window.StatusBarWindowStateController
61 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
62 import com.android.systemui.user.data.repository.FakeUserRepository
63 import com.android.systemui.util.mockito.any
64 import com.android.systemui.util.mockito.mock
65 import com.android.systemui.util.mockito.whenever
66 import com.android.systemui.util.time.FakeSystemClock
67 import com.google.common.truth.Truth.assertThat
68 import java.util.Optional
69 import kotlinx.coroutines.flow.emptyFlow
70 import kotlinx.coroutines.test.TestScope
71 import kotlinx.coroutines.test.runTest
72 import org.junit.Before
73 import org.junit.Test
74 import org.junit.runner.RunWith
75 import org.mockito.ArgumentCaptor
76 import org.mockito.Captor
77 import org.mockito.Mock
78 import org.mockito.Mockito
79 import org.mockito.Mockito.spy
80 import org.mockito.Mockito.verify
81 import org.mockito.MockitoAnnotations
82 
83 @RunWith(AndroidTestingRunner::class)
84 @RunWithLooper(setAsMainLooper = true)
85 @SmallTest
86 class NotificationShadeWindowViewTest : SysuiTestCase() {
87 
88     @Mock private lateinit var dragDownHelper: DragDownHelper
89     @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
90     @Mock private lateinit var shadeController: ShadeController
91     @Mock private lateinit var centralSurfaces: CentralSurfaces
92     @Mock private lateinit var backActionInteractor: BackActionInteractor
93     @Mock private lateinit var powerInteractor: PowerInteractor
94     @Mock private lateinit var dockManager: DockManager
95     @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
96     @Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout
97     @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
98     @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
99     @Mock
100     private lateinit var notificationStackScrollLayoutController:
101         NotificationStackScrollLayoutController
102     @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
103     @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
104     @Mock
105     private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
106     @Mock private lateinit var lockIconViewController: LockIconViewController
107     @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
108     @Mock private lateinit var ambientState: AmbientState
109     @Mock private lateinit var shadeLogger: ShadeLogger
110     @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
111     @Mock
112     private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener
113     @Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
114     @Mock private lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
115     @Mock private lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
116     @Mock
117     private lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
118     @Mock
119     private lateinit var unfoldTransitionProgressProvider:
120         Optional<UnfoldTransitionProgressProvider>
121     @Mock private lateinit var notificationInsetsController: NotificationInsetsController
122     @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
123     @Mock
124     private lateinit var primaryBouncerToGoneTransitionViewModel:
125         PrimaryBouncerToGoneTransitionViewModel
126     @Captor
127     private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
128 
129     private lateinit var underTest: NotificationShadeWindowView
130     private lateinit var controller: NotificationShadeWindowViewController
131     private lateinit var interactionEventHandler: InteractionEventHandler
132     private lateinit var testScope: TestScope
133 
134     @Before
135     fun setUp() {
136         MockitoAnnotations.initMocks(this)
137         underTest = spy(NotificationShadeWindowView(context, null))
138         whenever(
139                 underTest.findViewById<NotificationStackScrollLayout>(
140                     R.id.notification_stack_scroller
141                 )
142             )
143             .thenReturn(notificationStackScrollLayout)
144         whenever(underTest.findViewById<FrameLayout>(R.id.keyguard_bouncer_container))
145             .thenReturn(mock())
146         whenever(keyguardBouncerComponentFactory.create(any())).thenReturn(keyguardBouncerComponent)
147         whenever(keyguardBouncerComponent.securityContainerController)
148             .thenReturn(keyguardSecurityContainerController)
149         whenever(statusBarStateController.isDozing).thenReturn(false)
150         mDependency.injectTestDependency(ShadeController::class.java, shadeController)
151         whenever(dockManager.isDocked).thenReturn(false)
152         whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
153             .thenReturn(emptyFlow())
154 
155         val featureFlags = FakeFeatureFlags()
156         featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true)
157         featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false)
158         featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
159         featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
160         featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
161         testScope = TestScope()
162         controller =
163             NotificationShadeWindowViewController(
164                 lockscreenShadeTransitionController,
165                 FalsingCollectorFake(),
166                 statusBarStateController,
167                 dockManager,
168                 notificationShadeDepthController,
169                 underTest,
170                 notificationPanelViewController,
171                 ShadeExpansionStateManager(),
172                 notificationStackScrollLayoutController,
173                 statusBarKeyguardViewManager,
174                 statusBarWindowStateController,
175                 lockIconViewController,
176                 centralSurfaces,
177                 backActionInteractor,
178                 powerInteractor,
179                 notificationShadeWindowController,
180                 unfoldTransitionProgressProvider,
181                 keyguardUnlockAnimationController,
182                 notificationInsetsController,
183                 ambientState,
184                 shadeLogger,
185                 pulsingGestureListener,
186                 mLockscreenHostedDreamGestureListener,
187                 keyguardBouncerViewModel,
188                 keyguardBouncerComponentFactory,
189                 Mockito.mock(KeyguardMessageAreaController.Factory::class.java),
190                 keyguardTransitionInteractor,
191                 primaryBouncerToGoneTransitionViewModel,
192                 NotificationExpansionRepository(),
193                 featureFlags,
194                 FakeSystemClock(),
195                 BouncerMessageInteractor(
196                     FakeBouncerMessageRepository(),
197                     Mockito.mock(BouncerMessageFactory::class.java),
198                     FakeUserRepository(),
199                     CountDownTimerUtil(),
200                     featureFlags
201                 ),
202                 BouncerLogger(logcatLogBuffer("BouncerLog")),
203                 Mockito.mock(KeyEventInteractor::class.java),
204             )
205 
206         controller.setupExpandedStatusBar()
207         controller.setDragDownHelper(dragDownHelper)
208     }
209 
210     @Test
211     fun testDragDownHelperCalledWhenDraggingDown() =
212         testScope.runTest {
213             whenever(dragDownHelper.isDraggingDown).thenReturn(true)
214             val now = SystemClock.elapsedRealtime()
215             val ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0f, 0f, 0 /* meta */)
216             underTest.onTouchEvent(ev)
217             verify(dragDownHelper).onTouchEvent(ev)
218             ev.recycle()
219         }
220 
221     @Test
222     fun testInterceptTouchWhenShowingAltAuth() =
223         testScope.runTest {
224             captureInteractionEventHandler()
225 
226             // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
227             whenever(statusBarStateController.isDozing).thenReturn(false)
228             whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())).thenReturn(true)
229             whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
230 
231             // THEN we should intercept touch
232             assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isTrue()
233         }
234 
235     @Test
236     fun testNoInterceptTouch() =
237         testScope.runTest {
238             captureInteractionEventHandler()
239 
240             // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
241             whenever(statusBarStateController.isDozing).thenReturn(false)
242             whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any()))
243                 .thenReturn(false)
244             whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
245 
246             // THEN we shouldn't intercept touch
247             assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isFalse()
248         }
249 
250     @Test
251     fun testHandleTouchEventWhenShowingAltAuth() =
252         testScope.runTest {
253             captureInteractionEventHandler()
254 
255             // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
256             whenever(statusBarStateController.isDozing).thenReturn(false)
257             whenever(statusBarKeyguardViewManager.onTouch(any())).thenReturn(true)
258             whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
259 
260             // THEN we should handle the touch
261             assertThat(interactionEventHandler.handleTouchEvent(mock())).isTrue()
262         }
263 
264     private fun captureInteractionEventHandler() {
265         verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
266         interactionEventHandler = interactionEventHandlerCaptor.value
267     }
268 }
269