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.server.policy; 18 19 import static android.view.KeyEvent.KEYCODE_ALT_LEFT; 20 import static android.view.KeyEvent.KEYCODE_B; 21 import static android.view.KeyEvent.KEYCODE_BRIGHTNESS_DOWN; 22 import static android.view.KeyEvent.KEYCODE_C; 23 import static android.view.KeyEvent.KEYCODE_CTRL_LEFT; 24 import static android.view.KeyEvent.KEYCODE_DEL; 25 import static android.view.KeyEvent.KEYCODE_E; 26 import static android.view.KeyEvent.KEYCODE_ENTER; 27 import static android.view.KeyEvent.KEYCODE_H; 28 import static android.view.KeyEvent.KEYCODE_J; 29 import static android.view.KeyEvent.KEYCODE_M; 30 import static android.view.KeyEvent.KEYCODE_META_LEFT; 31 import static android.view.KeyEvent.KEYCODE_N; 32 import static android.view.KeyEvent.KEYCODE_P; 33 import static android.view.KeyEvent.KEYCODE_S; 34 import static android.view.KeyEvent.KEYCODE_SHIFT_LEFT; 35 import static android.view.KeyEvent.KEYCODE_SLASH; 36 import static android.view.KeyEvent.KEYCODE_SPACE; 37 import static android.view.KeyEvent.KEYCODE_TAB; 38 import static android.view.KeyEvent.KEYCODE_SCREENSHOT; 39 import static android.view.KeyEvent.KEYCODE_U; 40 import static android.view.KeyEvent.KEYCODE_Z; 41 42 import android.app.role.RoleManager; 43 import android.content.ComponentName; 44 import android.content.Intent; 45 import android.hardware.input.AppLaunchData; 46 import android.hardware.input.KeyGestureEvent; 47 import android.os.RemoteException; 48 import android.platform.test.annotations.DisableFlags; 49 import android.platform.test.annotations.EnableFlags; 50 import android.platform.test.annotations.Presubmit; 51 import android.util.SparseArray; 52 53 import androidx.test.filters.SmallTest; 54 55 import org.junit.Before; 56 import org.junit.Test; 57 58 @Presubmit 59 @SmallTest 60 @EnableFlags(com.android.hardware.input.Flags.FLAG_MODIFIER_SHORTCUT_MANAGER_REFACTOR) 61 public class ModifierShortcutTests extends ShortcutKeyTestBase { 62 63 private static final SparseArray<String> INTENT_SHORTCUTS = new SparseArray<>(); 64 private static final SparseArray<String> ROLE_SHORTCUTS = new SparseArray<>(); 65 static { 66 // These shortcuts should align with those defined in 67 // services/tests/wmtests/res/xml/bookmarks.xml INTENT_SHORTCUTS.append(KEYCODE_U, Intent.CATEGORY_APP_CALCULATOR)68 INTENT_SHORTCUTS.append(KEYCODE_U, Intent.CATEGORY_APP_CALCULATOR); INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_CONTACTS)69 INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_CONTACTS); INTENT_SHORTCUTS.append(KEYCODE_E, Intent.CATEGORY_APP_EMAIL)70 INTENT_SHORTCUTS.append(KEYCODE_E, Intent.CATEGORY_APP_EMAIL); INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CALENDAR)71 INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CALENDAR); INTENT_SHORTCUTS.append(KEYCODE_M, Intent.CATEGORY_APP_MAPS)72 INTENT_SHORTCUTS.append(KEYCODE_M, Intent.CATEGORY_APP_MAPS); 73 ROLE_SHORTCUTS.append(KEYCODE_B, RoleManager.ROLE_BROWSER)74 ROLE_SHORTCUTS.append(KEYCODE_B, RoleManager.ROLE_BROWSER); 75 } 76 private static final int ANY_DISPLAY_ID = 123; 77 78 @Before setUp()79 public void setUp() { 80 setUpPhoneWindowManager(); 81 } 82 83 /** 84 * Test meta+ shortcuts defined in bookmarks.xml. 85 */ 86 @Test 87 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testMetaShortcuts_withoutKeyGestureEventHandling()88 public void testMetaShortcuts_withoutKeyGestureEventHandling() { 89 for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) { 90 final int keyCode = INTENT_SHORTCUTS.keyAt(i); 91 final String category = INTENT_SHORTCUTS.valueAt(i); 92 93 sendKeyCombination(new int[]{KEYCODE_META_LEFT, keyCode}, 0); 94 mPhoneWindowManager.assertLaunchCategory(category); 95 } 96 97 mPhoneWindowManager.overrideRoleManager(); 98 for (int i = 0; i < ROLE_SHORTCUTS.size(); i++) { 99 final int keyCode = ROLE_SHORTCUTS.keyAt(i); 100 final String role = ROLE_SHORTCUTS.valueAt(i); 101 102 sendKeyCombination(new int[]{KEYCODE_META_LEFT, keyCode}, 0); 103 mPhoneWindowManager.assertLaunchRole(role); 104 } 105 106 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_B}, 0); 107 mPhoneWindowManager.assertLaunchRole(RoleManager.ROLE_BROWSER); 108 109 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_P}, 0); 110 mPhoneWindowManager.assertLaunchCategory(Intent.CATEGORY_APP_CONTACTS); 111 112 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_J}, 0); 113 mPhoneWindowManager.assertActivityTargetLaunched( 114 new ComponentName("com.test", "com.test.BookmarkTest")); 115 116 } 117 118 @Test 119 @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testMetaShortcuts_withKeyGestureEventHandling()120 public void testMetaShortcuts_withKeyGestureEventHandling() { 121 for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) { 122 final String category = INTENT_SHORTCUTS.valueAt(i); 123 mPhoneWindowManager.sendKeyGestureEvent( 124 new KeyGestureEvent.Builder() 125 .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION) 126 .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE) 127 .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(category)) 128 .build() 129 ); 130 mPhoneWindowManager.assertLaunchCategory(category); 131 } 132 133 mPhoneWindowManager.overrideRoleManager(); 134 for (int i = 0; i < ROLE_SHORTCUTS.size(); i++) { 135 final String role = ROLE_SHORTCUTS.valueAt(i); 136 137 mPhoneWindowManager.sendKeyGestureEvent( 138 new KeyGestureEvent.Builder() 139 .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION) 140 .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE) 141 .setAppLaunchData(AppLaunchData.createLaunchDataForRole(role)) 142 .build() 143 ); 144 mPhoneWindowManager.assertLaunchRole(role); 145 } 146 147 mPhoneWindowManager.sendKeyGestureEvent( 148 new KeyGestureEvent.Builder() 149 .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION) 150 .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE) 151 .setAppLaunchData( 152 new AppLaunchData.ComponentData("com.test", 153 "com.test.BookmarkTest")) 154 .build() 155 ); 156 mPhoneWindowManager.assertActivityTargetLaunched( 157 new ComponentName("com.test", "com.test.BookmarkTest")); 158 159 } 160 161 /** 162 * ALT + TAB to show recent apps. 163 */ 164 @Test 165 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testAltTab()166 public void testAltTab() { 167 mPhoneWindowManager.overrideStatusBarManagerInternal(); 168 sendKeyCombination(new int[]{KEYCODE_ALT_LEFT, KEYCODE_TAB}, 0); 169 mPhoneWindowManager.assertShowRecentApps(); 170 } 171 172 /** 173 * CTRL + SPACE to switch keyboard layout. 174 */ 175 @Test 176 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testCtrlSpace()177 public void testCtrlSpace() { 178 sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, /* duration= */ 0, 179 ANY_DISPLAY_ID); 180 mPhoneWindowManager.assertSwitchKeyboardLayout(/* direction= */ 1, ANY_DISPLAY_ID); 181 } 182 183 /** 184 * CTRL + SHIFT + SPACE to switch keyboard layout backwards. 185 */ 186 @Test 187 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testCtrlShiftSpace()188 public void testCtrlShiftSpace() { 189 sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE}, 190 /* duration= */ 0, ANY_DISPLAY_ID); 191 mPhoneWindowManager.assertSwitchKeyboardLayout(/* direction= */ -1, ANY_DISPLAY_ID); 192 } 193 194 /** 195 * CTRL + ALT + Z to enable accessibility service. 196 */ 197 @Test 198 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testCtrlAltZ()199 public void testCtrlAltZ() { 200 sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_ALT_LEFT, KEYCODE_Z}, 0); 201 mPhoneWindowManager.assertAccessibilityKeychordCalled(); 202 } 203 204 /** 205 * META + CTRL+ S to take screenshot. 206 */ 207 @Test 208 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testMetaCtrlS()209 public void testMetaCtrlS() { 210 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_S}, 0); 211 mPhoneWindowManager.assertTakeScreenshotCalled(); 212 } 213 214 /** 215 * META + N to expand notification panel. 216 */ 217 @Test 218 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testMetaN()219 public void testMetaN() throws RemoteException { 220 mPhoneWindowManager.overrideTogglePanel(); 221 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_N}, 0); 222 mPhoneWindowManager.assertTogglePanel(); 223 } 224 225 /** 226 * META + SLASH to toggle shortcuts menu. 227 */ 228 @Test 229 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testMetaSlash()230 public void testMetaSlash() { 231 mPhoneWindowManager.overrideStatusBarManagerInternal(); 232 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SLASH}, 0); 233 mPhoneWindowManager.assertToggleShortcutsMenu(); 234 } 235 236 /** 237 * META + ALT to toggle Cap Lock. 238 */ 239 @Test 240 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testMetaAlt()241 public void testMetaAlt() { 242 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_ALT_LEFT}, 0); 243 mPhoneWindowManager.assertToggleCapsLock(); 244 } 245 246 /** 247 * META + H to go to homescreen 248 */ 249 @Test 250 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testMetaH()251 public void testMetaH() { 252 mPhoneWindowManager.overrideLaunchHome(); 253 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_H}, 0); 254 mPhoneWindowManager.assertGoToHomescreen(); 255 } 256 257 /** 258 * META + ENTER to go to homescreen 259 */ 260 @Test 261 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testMetaEnter()262 public void testMetaEnter() { 263 mPhoneWindowManager.overrideLaunchHome(); 264 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_ENTER}, 0); 265 mPhoneWindowManager.assertGoToHomescreen(); 266 } 267 268 /** 269 * Sends a KEYCODE_BRIGHTNESS_DOWN event and validates the brightness is decreased as expected; 270 */ 271 @Test 272 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testKeyCodeBrightnessDown()273 public void testKeyCodeBrightnessDown() { 274 float[] currentBrightness = new float[]{0.1f, 0.05f, 0.0f}; 275 float[] newBrightness = new float[]{0.065738f, 0.0275134f, 0.0f}; 276 277 for (int i = 0; i < currentBrightness.length; i++) { 278 mPhoneWindowManager.prepareBrightnessDecrease(currentBrightness[i]); 279 sendKey(KEYCODE_BRIGHTNESS_DOWN); 280 mPhoneWindowManager.verifyNewBrightness(newBrightness[i]); 281 } 282 } 283 284 /** 285 * Sends a KEYCODE_SCREENSHOT and validates screenshot is taken if flag is enabled 286 */ 287 @Test 288 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testTakeScreenshot_flagEnabled()289 public void testTakeScreenshot_flagEnabled() { 290 sendKeyCombination(new int[]{KEYCODE_SCREENSHOT}, 0); 291 mPhoneWindowManager.assertTakeScreenshotCalled(); 292 } 293 294 /** 295 * META+CTRL+BACKSPACE for taking a bugreport when the flag is enabled. 296 */ 297 @Test 298 @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) 299 @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) testTakeBugReport_flagEnabled()300 public void testTakeBugReport_flagEnabled() throws RemoteException { 301 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0); 302 mPhoneWindowManager.assertTakeBugreport(true); 303 } 304 305 /** 306 * META+CTRL+BACKSPACE for taking a bugreport does nothing when the flag is disabledd. 307 */ 308 @Test 309 @DisableFlags({com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT, 310 com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER}) testTakeBugReport_flagDisabled()311 public void testTakeBugReport_flagDisabled() throws RemoteException { 312 sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0); 313 mPhoneWindowManager.assertTakeBugreport(false); 314 } 315 316 } 317