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 package com.android.systemui.notetask
17 
18 import android.app.role.RoleManager
19 import android.app.role.RoleManager.ROLE_NOTES
20 import android.hardware.input.InputManager
21 import android.hardware.input.KeyGestureEvent
22 import android.os.UserHandle
23 import android.os.UserManager
24 import android.platform.test.annotations.DisableFlags
25 import android.platform.test.annotations.EnableFlags
26 import android.platform.test.flag.junit.SetFlagsRule
27 import android.view.KeyEvent
28 import android.view.KeyEvent.ACTION_DOWN
29 import android.view.KeyEvent.ACTION_UP
30 import android.view.KeyEvent.KEYCODE_N
31 import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL
32 import androidx.test.ext.junit.runners.AndroidJUnit4
33 import androidx.test.filters.SmallTest
34 import com.android.keyguard.KeyguardUpdateMonitor
35 import com.android.systemui.SysuiTestCase
36 import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT
37 import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
38 import com.android.systemui.settings.FakeUserTracker
39 import com.android.systemui.statusbar.CommandQueue
40 import com.android.systemui.util.concurrency.FakeExecutor
41 import com.android.systemui.util.mockito.any
42 import com.android.systemui.util.mockito.eq
43 import com.android.systemui.util.mockito.mock
44 import com.android.systemui.util.mockito.whenever
45 import com.android.systemui.util.mockito.withArgCaptor
46 import com.android.systemui.util.time.FakeSystemClock
47 import com.android.wm.shell.bubbles.Bubbles
48 import com.google.common.truth.Truth.assertThat
49 import java.util.Optional
50 import kotlinx.coroutines.ExperimentalCoroutinesApi
51 import org.junit.Before
52 import org.junit.Rule
53 import org.junit.Test
54 import org.junit.runner.RunWith
55 import org.mockito.Mock
56 import org.mockito.Mockito.never
57 import org.mockito.Mockito.times
58 import org.mockito.Mockito.verify
59 import org.mockito.Mockito.verifyNoMoreInteractions
60 import org.mockito.MockitoAnnotations.initMocks
61 
62 /** atest SystemUITests:NoteTaskInitializerTest */
63 @OptIn(ExperimentalCoroutinesApi::class, InternalNoteTaskApi::class)
64 @SmallTest
65 @RunWith(AndroidJUnit4::class)
66 internal class NoteTaskInitializerTest : SysuiTestCase() {
67 
68     @get:Rule val setFlagsRule = SetFlagsRule()
69 
70     @Mock lateinit var commandQueue: CommandQueue
71     @Mock lateinit var inputManager: InputManager
72     @Mock lateinit var bubbles: Bubbles
73     @Mock lateinit var controller: NoteTaskController
74     @Mock lateinit var roleManager: RoleManager
75     @Mock lateinit var userManager: UserManager
76     @Mock lateinit var keyguardMonitor: KeyguardUpdateMonitor
77 
78     private val executor = FakeExecutor(FakeSystemClock())
79     private val userTracker = FakeUserTracker()
80     private val handlerCallbacks = mutableListOf<Runnable>()
81 
82     @Before
setUpnull83     fun setUp() {
84         initMocks(this)
85         whenever(keyguardMonitor.isUserUnlocked(userTracker.userId)).thenReturn(true)
86     }
87 
createUnderTestnull88     private fun createUnderTest(isEnabled: Boolean, bubbles: Bubbles?): NoteTaskInitializer =
89         NoteTaskInitializer(
90             controller = controller,
91             commandQueue = commandQueue,
92             optionalBubbles = Optional.ofNullable(bubbles),
93             isEnabled = isEnabled,
94             roleManager = roleManager,
95             userTracker = userTracker,
96             keyguardUpdateMonitor = keyguardMonitor,
97             inputManager = inputManager,
98             backgroundExecutor = executor,
99         )
100 
101     private fun createKeyEvent(
102         action: Int,
103         code: Int,
104         downTime: Long = 0L,
105         eventTime: Long = 0L,
106         metaState: Int = 0,
107     ): KeyEvent = KeyEvent(downTime, eventTime, action, code, 0 /*repeat*/, metaState)
108 
109     @Test
110     fun initialize_withUserUnlocked() {
111         whenever(keyguardMonitor.isUserUnlocked(userTracker.userId)).thenReturn(true)
112 
113         createUnderTest(isEnabled = true, bubbles = bubbles).initialize()
114 
115         verify(roleManager).addOnRoleHoldersChangedListenerAsUser(any(), any(), any())
116         verify(controller).updateNoteTaskForCurrentUserAndManagedProfiles()
117         verify(keyguardMonitor).registerCallback(any())
118     }
119 
120     @Test
initialize_withUserLockednull121     fun initialize_withUserLocked() {
122         whenever(keyguardMonitor.isUserUnlocked(userTracker.userId)).thenReturn(false)
123 
124         createUnderTest(isEnabled = true, bubbles = bubbles).initialize()
125 
126         verify(roleManager).addOnRoleHoldersChangedListenerAsUser(any(), any(), any())
127         verify(controller, never()).setNoteTaskShortcutEnabled(any(), any())
128         verify(keyguardMonitor).registerCallback(any())
129         assertThat(userTracker.callbacks).isNotEmpty()
130     }
131 
132     @Test
initialize_flagDisablednull133     fun initialize_flagDisabled() {
134         val underTest = createUnderTest(isEnabled = false, bubbles = bubbles)
135 
136         underTest.initialize()
137 
138         verifyNoMoreInteractions(
139             commandQueue,
140             bubbles,
141             controller,
142             roleManager,
143             userManager,
144             keyguardMonitor,
145         )
146     }
147 
148     @Test
initialize_bubblesNotPresentnull149     fun initialize_bubblesNotPresent() {
150         val underTest = createUnderTest(isEnabled = true, bubbles = null)
151 
152         underTest.initialize()
153 
154         verifyNoMoreInteractions(
155             commandQueue,
156             bubbles,
157             controller,
158             roleManager,
159             userManager,
160             keyguardMonitor,
161         )
162     }
163 
164     @Test
165     @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
initialize_handleSystemKeynull166     fun initialize_handleSystemKey() {
167         val expectedKeyEvent =
168             createKeyEvent(
169                 ACTION_DOWN,
170                 KEYCODE_N,
171                 metaState = KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
172             )
173         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
174         underTest.initialize()
175         val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
176 
177         callback.handleSystemKey(expectedKeyEvent)
178 
179         verify(controller).showNoteTask(any())
180     }
181 
182     @Test
183     @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
handlesShortcut_keyGestureTypeOpenNotesnull184     fun handlesShortcut_keyGestureTypeOpenNotes() {
185         val gestureEvent =
186             KeyGestureEvent.Builder()
187                 .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES)
188                 .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
189                 .build()
190         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
191         underTest.initialize()
192         val callback = withArgCaptor {
193             verify(inputManager).registerKeyGestureEventHandler(capture())
194         }
195 
196         assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isTrue()
197 
198         executor.runAllReady()
199         verify(controller).showNoteTask(eq(KEYBOARD_SHORTCUT))
200     }
201 
202     @Test
203     @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
handlesShortcut_stylusTailButtonnull204     fun handlesShortcut_stylusTailButton() {
205         val gestureEvent =
206             KeyGestureEvent.Builder()
207                 .setKeycodes(intArrayOf(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
208                 .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES)
209                 .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
210                 .build()
211         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
212         underTest.initialize()
213         val callback = withArgCaptor {
214             verify(inputManager).registerKeyGestureEventHandler(capture())
215         }
216 
217         assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isTrue()
218 
219         executor.runAllReady()
220         verify(controller).showNoteTask(eq(TAIL_BUTTON))
221     }
222 
223     @Test
224     @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
ignoresUnrelatedShortcutsnull225     fun ignoresUnrelatedShortcuts() {
226         val gestureEvent =
227             KeyGestureEvent.Builder()
228                 .setKeycodes(intArrayOf(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
229                 .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
230                 .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
231                 .build()
232         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
233         underTest.initialize()
234         val callback = withArgCaptor {
235             verify(inputManager).registerKeyGestureEventHandler(capture())
236         }
237 
238         assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isFalse()
239 
240         executor.runAllReady()
241         verify(controller, never()).showNoteTask(any())
242     }
243 
244     @Test
initialize_userUnlocked_shouldUpdateNoteTasknull245     fun initialize_userUnlocked_shouldUpdateNoteTask() {
246         whenever(keyguardMonitor.isUserUnlocked(userTracker.userId)).thenReturn(false)
247         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
248         underTest.initialize()
249         val callback = withArgCaptor { verify(keyguardMonitor).registerCallback(capture()) }
250         whenever(keyguardMonitor.isUserUnlocked(userTracker.userId)).thenReturn(true)
251 
252         callback.onUserUnlocked()
253 
254         verify(controller).updateNoteTaskForCurrentUserAndManagedProfiles()
255     }
256 
257     @Test
initialize_onRoleHoldersChanged_shouldRunOnRoleHoldersChangednull258     fun initialize_onRoleHoldersChanged_shouldRunOnRoleHoldersChanged() {
259         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
260         underTest.initialize()
261         val callback = withArgCaptor {
262             verify(roleManager)
263                 .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL))
264         }
265 
266         callback.onRoleHoldersChanged(ROLE_NOTES, userTracker.userHandle)
267 
268         verify(controller).onRoleHoldersChanged(ROLE_NOTES, userTracker.userHandle)
269     }
270 
271     @Test
initialize_onProfilesChanged_shouldUpdateNoteTasknull272     fun initialize_onProfilesChanged_shouldUpdateNoteTask() {
273         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
274         underTest.initialize()
275 
276         userTracker.callbacks.first().onProfilesChanged(emptyList())
277 
278         verify(controller, times(2)).updateNoteTaskForCurrentUserAndManagedProfiles()
279     }
280 
281     @Test
initialize_onUserChanged_shouldUpdateNoteTasknull282     fun initialize_onUserChanged_shouldUpdateNoteTask() {
283         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
284         underTest.initialize()
285 
286         userTracker.callbacks.first().onUserChanged(0, mock())
287 
288         verify(controller, times(2)).updateNoteTaskForCurrentUserAndManagedProfiles()
289     }
290 
291     @Test
292     @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
tailButtonGestureDetection_singlePress_shouldShowNoteTaskOnUpnull293     fun tailButtonGestureDetection_singlePress_shouldShowNoteTaskOnUp() {
294         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
295         underTest.initialize()
296         val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
297 
298         callback.handleSystemKey(
299             createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
300         )
301         verify(controller, never()).showNoteTask(any())
302 
303         callback.handleSystemKey(
304             createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
305         )
306 
307         verify(controller).showNoteTask(any())
308     }
309 
310     @Test
311     @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
tailButtonGestureDetection_doublePress_shouldNotShowNoteTaskTwicenull312     fun tailButtonGestureDetection_doublePress_shouldNotShowNoteTaskTwice() {
313         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
314         underTest.initialize()
315         val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
316 
317         callback.handleSystemKey(
318             createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
319         )
320         callback.handleSystemKey(
321             createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 50)
322         )
323         callback.handleSystemKey(
324             createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 99, eventTime = 99)
325         )
326         callback.handleSystemKey(
327             createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 99, eventTime = 150)
328         )
329 
330         verify(controller, times(1)).showNoteTask(any())
331     }
332 
333     @Test
334     @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
tailButtonGestureDetection_longPress_shouldNotShowNoteTasknull335     fun tailButtonGestureDetection_longPress_shouldNotShowNoteTask() {
336         val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
337         underTest.initialize()
338         val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) }
339 
340         callback.handleSystemKey(
341             createKeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 0)
342         )
343         callback.handleSystemKey(
344             createKeyEvent(ACTION_UP, KEYCODE_STYLUS_BUTTON_TAIL, downTime = 0, eventTime = 1000)
345         )
346 
347         verify(controller, never()).showNoteTask(any())
348     }
349 }
350