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