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