• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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 
18 package com.android.systemui.keyguard.domain.interactor
19 
20 import android.content.Intent
21 import android.os.PowerManager
22 import android.platform.test.annotations.DisableFlags
23 import android.platform.test.annotations.EnableFlags
24 import android.platform.test.flag.junit.SetFlagsRule
25 import android.provider.Settings
26 import android.view.accessibility.accessibilityManagerWrapper
27 import androidx.test.ext.junit.runners.AndroidJUnit4
28 import androidx.test.filters.SmallTest
29 import com.android.internal.logging.testing.UiEventLoggerFake
30 import com.android.internal.logging.uiEventLogger
31 import com.android.systemui.Flags.FLAG_DOUBLE_TAP_TO_SLEEP
32 import com.android.systemui.SysuiTestCase
33 import com.android.systemui.coroutines.collectLastValue
34 import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
35 import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
36 import com.android.systemui.flags.fakeFeatureFlagsClassic
37 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
38 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
39 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
40 import com.android.systemui.keyguard.shared.model.KeyguardState
41 import com.android.systemui.kosmos.testScope
42 import com.android.systemui.res.R
43 import com.android.systemui.shade.pulsingGestureListener
44 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
45 import com.android.systemui.testKosmos
46 import com.android.systemui.util.mockito.mock
47 import com.android.systemui.util.mockito.whenever
48 import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
49 import com.android.systemui.util.time.fakeSystemClock
50 import com.google.common.truth.Truth.assertThat
51 import kotlinx.coroutines.runBlocking
52 import kotlinx.coroutines.test.advanceTimeBy
53 import kotlinx.coroutines.test.runCurrent
54 import kotlinx.coroutines.test.runTest
55 import org.junit.After
56 import org.junit.Before
57 import org.junit.Rule
58 import org.junit.Test
59 import org.junit.runner.RunWith
60 import org.mockito.Mock
61 import org.mockito.ArgumentMatchers.anyInt
62 import org.mockito.ArgumentMatchers.anyLong
63 import org.mockito.Mockito.never
64 import org.mockito.Mockito.verify
65 import org.mockito.MockitoAnnotations
66 
67 @SmallTest
68 @RunWith(AndroidJUnit4::class)
69 @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
70 class KeyguardTouchHandlingInteractorTest : SysuiTestCase() {
71     private val kosmos =
72         testKosmos().apply {
73             this.accessibilityManagerWrapper = mock<AccessibilityManagerWrapper>()
74             this.uiEventLogger = mock<UiEventLoggerFake>()
75         }
76 
77     @get:Rule val setFlagsRule = SetFlagsRule()
78 
79     private lateinit var underTest: KeyguardTouchHandlingInteractor
80 
81     private val logger = kosmos.uiEventLogger
82     private val testScope = kosmos.testScope
83     private val keyguardRepository = kosmos.fakeKeyguardRepository
84     private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
85     private val secureSettingsRepository = kosmos.userAwareSecureSettingsRepository
86 
87     @Mock private lateinit var powerManager: PowerManager
88 
89     @Before
90     fun setUp() {
91         MockitoAnnotations.initMocks(this)
92         overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, true)
93         overrideResource(com.android.internal.R.bool.config_supportDoubleTapSleep, true)
94         whenever(kosmos.accessibilityManagerWrapper.getRecommendedTimeoutMillis(anyInt(), anyInt()))
95             .thenAnswer { it.arguments[0] }
96 
97         runBlocking { createUnderTest() }
98     }
99 
100     @After
101     fun tearDown() {
102         val testableResource = mContext.getOrCreateTestableResources()
103         testableResource.removeOverride(R.bool.long_press_keyguard_customize_lockscreen_enabled)
104         testableResource.removeOverride(com.android.internal.R.bool.config_supportDoubleTapSleep)
105     }
106 
107     @Test
108     fun isLongPressEnabled() =
109         testScope.runTest {
110             val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
111             KeyguardState.values().forEach { keyguardState ->
112                 setUpState(keyguardState = keyguardState)
113 
114                 if (keyguardState == KeyguardState.LOCKSCREEN) {
115                     assertThat(isEnabled()).isTrue()
116                 } else {
117                     assertThat(isEnabled()).isFalse()
118                 }
119             }
120         }
121 
122     @Test
123     fun isLongPressEnabled_alwaysFalseWhenQuickSettingsAreVisible() =
124         testScope.runTest {
125             val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled)
126             KeyguardState.values().forEach { keyguardState ->
127                 setUpState(keyguardState = keyguardState, isQuickSettingsVisible = true)
128 
129                 assertThat(isEnabled()).isFalse()
130             }
131         }
132 
133     @Test
134     fun isLongPressEnabled_alwaysFalseWhenConfigEnabledBooleanIsFalse() =
135         testScope.runTest {
136             overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, false)
137             createUnderTest()
138             val isEnabled by collectLastValue(underTest.isLongPressHandlingEnabled)
139             runCurrent()
140 
141             assertThat(isEnabled).isFalse()
142         }
143 
144     @Test
145     fun longPressed_menuClicked_showsSettings() =
146         testScope.runTest {
147             val isMenuVisible by collectLastValue(underTest.isMenuVisible)
148             val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings)
149             runCurrent()
150 
151             underTest.onLongPress()
152             assertThat(isMenuVisible).isTrue()
153 
154             underTest.onMenuTouchGestureEnded(/* isClick= */ true)
155 
156             assertThat(isMenuVisible).isFalse()
157             assertThat(shouldOpenSettings).isTrue()
158         }
159 
160     @Test
161     fun onSettingsShown_consumesSettingsShowEvent() =
162         testScope.runTest {
163             val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings)
164             runCurrent()
165 
166             underTest.onLongPress()
167             underTest.onMenuTouchGestureEnded(/* isClick= */ true)
168             assertThat(shouldOpenSettings).isTrue()
169 
170             underTest.onSettingsShown()
171             assertThat(shouldOpenSettings).isFalse()
172         }
173 
174     @Test
175     fun onTouchedOutside_neverShowsSettings() =
176         testScope.runTest {
177             val isMenuVisible by collectLastValue(underTest.isMenuVisible)
178             val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings)
179             runCurrent()
180 
181             underTest.onTouchedOutside()
182 
183             assertThat(isMenuVisible).isFalse()
184             assertThat(shouldOpenSettings).isFalse()
185         }
186 
187     @Test
188     fun longPressed_isA11yAction_doesNotShowMenu_opensSettings() =
189         testScope.runTest {
190             createUnderTest()
191             val isMenuVisible by collectLastValue(underTest.isMenuVisible)
192             val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings)
193             val isA11yAction = true
194             runCurrent()
195 
196             underTest.onLongPress(isA11yAction)
197 
198             assertThat(isMenuVisible).isFalse()
199             assertThat(shouldOpenSettings).isTrue()
200         }
201 
202     @Test
203     fun longPressed_closeDialogsBroadcastReceived_popupDismissed() =
204         testScope.runTest {
205             val isMenuVisible by collectLastValue(underTest.isMenuVisible)
206             runCurrent()
207 
208             underTest.onLongPress()
209             assertThat(isMenuVisible).isTrue()
210 
211             fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
212                 context,
213                 Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
214             )
215 
216             assertThat(isMenuVisible).isFalse()
217         }
218 
219     @Test
220     fun closesDialogAfterTimeout() =
221         testScope.runTest {
222             val isMenuVisible by collectLastValue(underTest.isMenuVisible)
223             runCurrent()
224 
225             underTest.onLongPress()
226             assertThat(isMenuVisible).isTrue()
227 
228             advanceTimeBy(KeyguardTouchHandlingInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
229 
230             assertThat(isMenuVisible).isFalse()
231         }
232 
233     @Test
234     fun closesDialogAfterTimeout_onlyAfterTouchGestureEnded() =
235         testScope.runTest {
236             val isMenuVisible by collectLastValue(underTest.isMenuVisible)
237             runCurrent()
238 
239             underTest.onLongPress()
240             assertThat(isMenuVisible).isTrue()
241             underTest.onMenuTouchGestureStarted()
242 
243             advanceTimeBy(KeyguardTouchHandlingInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
244             assertThat(isMenuVisible).isTrue()
245 
246             underTest.onMenuTouchGestureEnded(/* isClick= */ false)
247             advanceTimeBy(KeyguardTouchHandlingInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS)
248             assertThat(isMenuVisible).isFalse()
249         }
250 
251     @Test
252     fun logsWhenMenuIsShown() =
253         testScope.runTest {
254             collectLastValue(underTest.isMenuVisible)
255             runCurrent()
256 
257             underTest.onLongPress()
258 
259             verify(logger)
260                 .log(KeyguardTouchHandlingInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN)
261         }
262 
263     @Test
264     fun logsWhenMenuIsClicked() =
265         testScope.runTest {
266             collectLastValue(underTest.isMenuVisible)
267             runCurrent()
268 
269             underTest.onLongPress()
270             underTest.onMenuTouchGestureEnded(/* isClick= */ true)
271 
272             verify(logger)
273                 .log(KeyguardTouchHandlingInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED)
274         }
275 
276     @Test
277     fun triggersFaceAuthWhenLockscreenIsClicked() =
278         testScope.runTest {
279             collectLastValue(underTest.isMenuVisible)
280             runCurrent()
281             kosmos.fakeDeviceEntryFaceAuthRepository.canRunFaceAuth.value = true
282 
283             underTest.onClick(100.0f, 100.0f)
284             runCurrent()
285 
286             val runningAuthRequest =
287                 kosmos.fakeDeviceEntryFaceAuthRepository.runningAuthRequest.value
288             assertThat(runningAuthRequest?.first)
289                 .isEqualTo(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED)
290             assertThat(runningAuthRequest?.second).isEqualTo(true)
291         }
292 
293     @Test
294     fun showMenu_leaveLockscreen_returnToLockscreen_menuNotVisible() =
295         testScope.runTest {
296             val isMenuVisible by collectLastValue(underTest.isMenuVisible)
297             runCurrent()
298             underTest.onLongPress()
299             assertThat(isMenuVisible).isTrue()
300 
301             keyguardTransitionRepository.sendTransitionSteps(
302                 from = KeyguardState.LOCKSCREEN,
303                 to = KeyguardState.GONE,
304                 testScope,
305             )
306             assertThat(isMenuVisible).isFalse()
307 
308             keyguardTransitionRepository.sendTransitionSteps(
309                 from = KeyguardState.GONE,
310                 to = KeyguardState.LOCKSCREEN,
311                 testScope,
312             )
313             assertThat(isMenuVisible).isFalse()
314         }
315 
316     @Test
317     @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
318     fun isDoubleTapEnabled_flagEnabled_userSettingEnabled_onlyTrueInLockScreenState() {
319         testScope.runTest {
320             secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true)
321 
322             val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled)
323             KeyguardState.entries.forEach { keyguardState ->
324                 setUpState(keyguardState = keyguardState)
325 
326                 if (keyguardState == KeyguardState.LOCKSCREEN) {
327                     assertThat(isEnabled()).isTrue()
328                 } else {
329                     assertThat(isEnabled()).isFalse()
330                 }
331             }
332         }
333     }
334 
335     @Test
336     @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
337     fun isDoubleTapEnabled_flagEnabled_userSettingDisabled_alwaysFalse() {
338         testScope.runTest {
339             secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, false)
340 
341             val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled)
342             KeyguardState.entries.forEach { keyguardState ->
343                 setUpState(keyguardState = keyguardState)
344 
345                 assertThat(isEnabled()).isFalse()
346             }
347         }
348     }
349 
350     @Test
351     @DisableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
352     fun isDoubleTapEnabled_flagDisabled_userSettingEnabled_alwaysFalse() {
353         testScope.runTest {
354             secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true)
355 
356             val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled)
357             KeyguardState.entries.forEach { keyguardState ->
358                 setUpState(keyguardState = keyguardState)
359 
360                 assertThat(isEnabled()).isFalse()
361             }
362         }
363     }
364 
365 
366 
367     @Test
368     @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
369     fun isDoubleTapEnabled_flagEnabledAndConfigDisabled_alwaysFalse() {
370         testScope.runTest {
371             secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true)
372             overrideResource(com.android.internal.R.bool.config_supportDoubleTapSleep, false)
373             createUnderTest()
374 
375             val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled)
376             KeyguardState.entries.forEach { keyguardState ->
377                 setUpState(keyguardState = keyguardState)
378 
379                 assertThat(isEnabled()).isFalse()
380             }
381         }
382     }
383 
384     @Test
385     @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
386     fun isDoubleTapEnabled_quickSettingsVisible_alwaysFalse() {
387         testScope.runTest {
388             secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true)
389 
390             val isEnabled = collectLastValue(underTest.isDoubleTapHandlingEnabled)
391             KeyguardState.entries.forEach { keyguardState ->
392                 setUpState(keyguardState = keyguardState, isQuickSettingsVisible = true)
393 
394                 assertThat(isEnabled()).isFalse()
395             }
396         }
397     }
398 
399     @Test
400     @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
401     fun onDoubleClick_doubleTapEnabled() {
402         testScope.runTest {
403             secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, true)
404             val isEnabled by collectLastValue(underTest.isDoubleTapHandlingEnabled)
405             runCurrent()
406 
407             underTest.onDoubleClick()
408 
409             assertThat(isEnabled).isTrue()
410             verify(powerManager).goToSleep(anyLong())
411         }
412     }
413 
414     @Test
415     @EnableFlags(FLAG_DOUBLE_TAP_TO_SLEEP)
416     fun onDoubleClick_doubleTapDisabled() {
417         testScope.runTest {
418             secureSettingsRepository.setBoolean(Settings.Secure.DOUBLE_TAP_TO_SLEEP, false)
419             val isEnabled by collectLastValue(underTest.isDoubleTapHandlingEnabled)
420             runCurrent()
421 
422             underTest.onDoubleClick()
423 
424             assertThat(isEnabled).isFalse()
425             verify(powerManager, never()).goToSleep(anyLong())
426         }
427     }
428 
429     private suspend fun createUnderTest(isRevampedWppFeatureEnabled: Boolean = true) {
430         // This needs to be re-created for each test outside of kosmos since the flag values are
431         // read during initialization to set up flows. Maybe there is a better way to handle that.
432         underTest =
433             KeyguardTouchHandlingInteractor(
434                 context = mContext,
435                 scope = testScope.backgroundScope,
436                 transitionInteractor = kosmos.keyguardTransitionInteractor,
437                 repository = keyguardRepository,
438                 logger = logger,
439                 featureFlags = kosmos.fakeFeatureFlagsClassic,
440                 broadcastDispatcher = fakeBroadcastDispatcher,
441                 accessibilityManager = kosmos.accessibilityManagerWrapper,
442                 pulsingGestureListener = kosmos.pulsingGestureListener,
443                 faceAuthInteractor = kosmos.deviceEntryFaceAuthInteractor,
444                 secureSettingsRepository = secureSettingsRepository,
445                 powerManager = powerManager,
446                 systemClock = kosmos.fakeSystemClock,
447             )
448         setUpState()
449     }
450 
451     private suspend fun setUpState(
452         keyguardState: KeyguardState = KeyguardState.LOCKSCREEN,
453         isQuickSettingsVisible: Boolean = false,
454     ) {
455         keyguardTransitionRepository.sendTransitionSteps(
456             from = KeyguardState.AOD,
457             to = keyguardState,
458             testScope = testScope,
459         )
460         keyguardRepository.setQuickSettingsVisible(isVisible = isQuickSettingsVisible)
461     }
462 }
463