1 /*
2  * Copyright (C) 2022 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.hardware.display.AmbientDisplayConfiguration
20 import android.os.PowerManager
21 import android.provider.Settings.Secure.DOZE_DOUBLE_TAP_GESTURE
22 import android.provider.Settings.Secure.DOZE_TAP_SCREEN_GESTURE
23 import android.testing.AndroidTestingRunner
24 import android.testing.TestableLooper.RunWithLooper
25 import android.view.MotionEvent
26 import androidx.test.filters.SmallTest
27 import com.android.systemui.SysuiTestCase
28 import com.android.systemui.classifier.FalsingCollector
29 import com.android.systemui.dock.DockManager
30 import com.android.systemui.dump.DumpManager
31 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
32 import com.android.systemui.keyguard.domain.interactor.DozeInteractor
33 import com.android.systemui.plugins.FalsingManager
34 import com.android.systemui.plugins.statusbar.StatusBarStateController
35 import com.android.systemui.power.data.repository.FakePowerRepository
36 import com.android.systemui.power.domain.interactor.PowerInteractor
37 import com.android.systemui.settings.UserTracker
38 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
39 import com.android.systemui.tuner.TunerService
40 import com.android.systemui.tuner.TunerService.Tunable
41 import com.android.systemui.util.mockito.eq
42 import com.google.common.truth.Truth.assertThat
43 import org.junit.Before
44 import org.junit.Test
45 import org.junit.runner.RunWith
46 import org.mockito.ArgumentCaptor
47 import org.mockito.ArgumentMatchers.anyInt
48 import org.mockito.Mock
49 import org.mockito.Mockito.never
50 import org.mockito.Mockito.verify
51 import org.mockito.MockitoAnnotations
52 import org.mockito.Mockito.`when` as whenever
53 
54 @RunWith(AndroidTestingRunner::class)
55 @RunWithLooper(setAsMainLooper = true)
56 @SmallTest
57 class PulsingGestureListenerTest : SysuiTestCase() {
58     @Mock
59     private lateinit var dockManager: DockManager
60     @Mock
61     private lateinit var falsingManager: FalsingManager
62     @Mock
63     private lateinit var falsingCollector: FalsingCollector
64     @Mock
65     private lateinit var ambientDisplayConfiguration: AmbientDisplayConfiguration
66     @Mock
67     private lateinit var tunerService: TunerService
68     @Mock
69     private lateinit var dumpManager: DumpManager
70     @Mock
71     private lateinit var statusBarStateController: StatusBarStateController
72     @Mock
73     private lateinit var shadeLogger: ShadeLogger
74     @Mock
75     private lateinit var userTracker: UserTracker
76     @Mock
77     private lateinit var dozeInteractor: DozeInteractor
78     @Mock
79     private lateinit var screenOffAnimationController: ScreenOffAnimationController
80 
81     private lateinit var powerRepository: FakePowerRepository
82     private lateinit var tunableCaptor: ArgumentCaptor<Tunable>
83     private lateinit var underTest: PulsingGestureListener
84 
85     @Before
86     fun setUp() {
87         MockitoAnnotations.initMocks(this)
88 
89         powerRepository = FakePowerRepository()
90 
91         underTest = PulsingGestureListener(
92                 falsingManager,
93                 dockManager,
94                 PowerInteractor(
95                     powerRepository,
96                     FakeKeyguardRepository(),
97                     falsingCollector,
98                     screenOffAnimationController,
99                     statusBarStateController,
100                 ),
101                 ambientDisplayConfiguration,
102                 statusBarStateController,
103                 shadeLogger,
104                 dozeInteractor,
105                 userTracker,
106                 tunerService,
107                 dumpManager
108         )
109         whenever(dockManager.isDocked).thenReturn(false)
110         whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true)
111     }
112 
113     @Test
114     fun testGestureDetector_singleTapEnabled() {
115         whenever(statusBarStateController.isDozing).thenReturn(true)
116 
117         // GIVEN tap is enabled, prox not covered
118         whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
119         updateSettings()
120         whenever(falsingManager.isProximityNear).thenReturn(false)
121 
122         // GIVEN the falsing manager does NOT think the tap is a false tap
123         whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
124 
125         // WHEN there's a tap
126         underTest.onSingleTapUp(upEv)
127 
128         // THEN wake up device if dozing
129         assertThat(powerRepository.lastWakeWhy).isNotNull()
130         assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_TAP)
131     }
132 
133     @Test
134     fun testGestureDetector_doubleTapEnabled() {
135         whenever(statusBarStateController.isDozing).thenReturn(true)
136 
137         // GIVEN double tap is enabled, prox not covered
138         whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
139         updateSettings()
140         whenever(falsingManager.isProximityNear).thenReturn(false)
141 
142         // GIVEN the falsing manager does NOT think the tap is a false tap
143         whenever(falsingManager.isFalseDoubleTap).thenReturn(false)
144 
145         // WHEN there's a double tap
146         underTest.onDoubleTapEvent(upEv)
147 
148         // THEN wake up device if dozing
149         assertThat(powerRepository.lastWakeWhy).isNotNull()
150         assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_TAP)
151     }
152 
153     @Test
154     fun testGestureDetector_doubleTapEnabled_onDownEvent_noFalsingCheck() {
155         // GIVEN tap is enabled
156         whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
157 
158         // WHEN there's a double tap on DOWN event
159         underTest.onDoubleTapEvent(downEv)
160 
161         // THEN don't check the falsing manager, should only be checked on the UP event
162         verify(falsingManager, never()).isFalseDoubleTap()
163     }
164 
165     @Test
166     fun testGestureDetector_singleTapEnabled_falsing() {
167         whenever(statusBarStateController.isDozing).thenReturn(true)
168 
169         // GIVEN tap is enabled, prox not covered
170         whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
171         updateSettings()
172         whenever(falsingManager.isProximityNear).thenReturn(false)
173 
174         // GIVEN the falsing manager thinks the tap is a false tap
175         whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
176 
177         // WHEN there's a tap
178         underTest.onSingleTapUp(upEv)
179 
180         // THEN the device doesn't wake up
181         assertThat(powerRepository.lastWakeWhy).isNull()
182         assertThat(powerRepository.lastWakeReason).isNull()
183     }
184 
185     @Test
186     fun testSingleTap_notDozing_noFalsingCheck() {
187         whenever(statusBarStateController.isDozing).thenReturn(false)
188 
189         // GIVEN tap is enabled
190         whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
191         // WHEN there's a tap
192         underTest.onSingleTapUp(upEv)
193 
194         // THEN the falsing manager never gets a call (because the device wasn't dozing
195         // during the tap)
196         verify(falsingManager, never()).isFalseTap(anyInt())
197     }
198 
199     @Test
200     fun testDoubleTap_notDozing_noFalsingCheck() {
201         whenever(statusBarStateController.isDozing).thenReturn(false)
202 
203         // GIVEN tap is enabled
204         whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
205         // WHEN there's a tap
206         underTest.onDoubleTapEvent(upEv)
207 
208         // THEN the falsing manager never gets a call (because the device wasn't dozing
209         // during the tap)
210         verify(falsingManager, never()).isFalseTap(anyInt())
211     }
212 
213     @Test
214     fun testGestureDetector_doubleTapEnabled_falsing() {
215         whenever(statusBarStateController.isDozing).thenReturn(true)
216 
217         // GIVEN double tap is enabled, prox not covered
218         whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
219         updateSettings()
220         whenever(falsingManager.isProximityNear).thenReturn(false)
221 
222         // GIVEN the falsing manager thinks the tap is a false tap
223         whenever(falsingManager.isFalseDoubleTap).thenReturn(true)
224 
225         // WHEN there's a double tap ACTION_UP event
226         underTest.onDoubleTapEvent(upEv)
227 
228         // THEN the device doesn't wake up
229         assertThat(powerRepository.lastWakeWhy).isNull()
230         assertThat(powerRepository.lastWakeReason).isNull()
231     }
232 
233     @Test
234     fun testGestureDetector_singleTapEnabled_proxCovered() {
235         whenever(statusBarStateController.isDozing).thenReturn(true)
236 
237         // GIVEN tap is enabled, not a false tap based on classifiers
238         whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
239         updateSettings()
240         whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
241 
242         // GIVEN prox is covered
243         whenever(falsingManager.isProximityNear()).thenReturn(true)
244 
245         // WHEN there's a tap
246         underTest.onSingleTapUp(upEv)
247 
248         // THEN the device doesn't wake up
249         assertThat(powerRepository.lastWakeWhy).isNull()
250         assertThat(powerRepository.lastWakeReason).isNull()
251     }
252 
253     @Test
254     fun testGestureDetector_doubleTapEnabled_proxCovered() {
255         whenever(statusBarStateController.isDozing).thenReturn(true)
256 
257         // GIVEN double tap is enabled, not a false tap based on classifiers
258         whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
259         updateSettings()
260         whenever(falsingManager.isFalseDoubleTap).thenReturn(false)
261 
262         // GIVEN prox is covered
263         whenever(falsingManager.isProximityNear()).thenReturn(true)
264 
265         // WHEN there's a tap
266         underTest.onDoubleTapEvent(upEv)
267 
268         // THEN the device doesn't wake up
269         assertThat(powerRepository.lastWakeWhy).isNull()
270         assertThat(powerRepository.lastWakeReason).isNull()
271     }
272 
273     fun updateSettings() {
274         tunableCaptor = ArgumentCaptor.forClass(Tunable::class.java)
275         verify(tunerService).addTunable(
276                 tunableCaptor.capture(),
277                 eq(DOZE_DOUBLE_TAP_GESTURE),
278                 eq(DOZE_TAP_SCREEN_GESTURE))
279         tunableCaptor.value.onTuningChanged(DOZE_DOUBLE_TAP_GESTURE, "")
280         tunableCaptor.value.onTuningChanged(DOZE_TAP_SCREEN_GESTURE, "")
281     }
282 }
283 
284 private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
285 private val upEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
286