diff options
| author | 2024-11-04 19:09:05 +0000 | |
|---|---|---|
| committer | 2024-11-04 19:09:05 +0000 | |
| commit | 3821b8afb6eb9a060eac8f546ae313cafaeb9cd0 (patch) | |
| tree | 96a060cbcdfe6b26624e4388001551d0ea124932 | |
| parent | 695314eb4f8090f03be042ac6be2f8f79cdd9c50 (diff) | |
Block addition of custom shortcuts
- If overlapping with system shortcuts
- If ovelapping with bookmarks
- If no modifierState provided for key based shortcut
- If using a system keycode for key based shortcut
DD: go/customizable_shortcuts
PRD: go/custom-kb-shortcuts
Bug: 365064144
Test: atest InputTests
Flag: com.android.hardware.input.enable_customizable_input_gestures
Change-Id: I69dd162f3318a84123b748d5ee5b9074844d8790
4 files changed, 648 insertions, 669 deletions
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java index f4bd402e63a2..cf1cdaf55e5c 100644 --- a/services/core/java/com/android/server/input/InputGestureManager.java +++ b/services/core/java/com/android/server/input/InputGestureManager.java @@ -16,11 +16,21 @@ package com.android.server.input; +import static android.hardware.input.InputGestureData.createKeyTrigger; +import static com.android.hardware.input.Flags.keyboardA11yShortcutControl; +import static com.android.server.flags.Flags.newBugreportKeyboardShortcut; +import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut; +import static com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.Context; import android.hardware.input.InputGestureData; import android.hardware.input.InputManager; +import android.hardware.input.InputSettings; +import android.hardware.input.KeyGestureEvent; +import android.os.SystemProperties; import android.util.IndentingPrintWriter; import android.util.SparseArray; import android.view.KeyEvent; @@ -29,15 +39,17 @@ import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; /** * A thread-safe component of {@link InputManagerService} responsible for managing pre-defined input * gestures and custom gestures defined by other system components using Input APIs. * - * TODO(b/365064144): Add implementation to persist data, identify clashes with existing shortcuts. + * TODO(b/365064144): Add implementation to persist data. * */ final class InputGestureManager { @@ -47,13 +59,242 @@ final class InputGestureManager { KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON | KeyEvent.META_META_ON; - @GuardedBy("mCustomInputGestures") + private final Context mContext; + + private static final Object mGestureLock = new Object(); + @GuardedBy("mGestureLock") private final SparseArray<Map<InputGestureData.Trigger, InputGestureData>> mCustomInputGestures = new SparseArray<>(); + @GuardedBy("mGestureLock") + private final Map<InputGestureData.Trigger, InputGestureData> mSystemShortcuts = + new HashMap<>(); + + @GuardedBy("mGestureLock") + private final Set<InputGestureData.Trigger> mBlockListedTriggers = new HashSet<>(Set.of( + createKeyTrigger(KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON), + createKeyTrigger(KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON), + createKeyTrigger(KeyEvent.KEYCODE_SPACE, KeyEvent.META_CTRL_ON), + createKeyTrigger(KeyEvent.KEYCODE_SPACE, + KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON), + createKeyTrigger(KeyEvent.KEYCODE_Z, + KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON), + createKeyTrigger(KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON), + createKeyTrigger(KeyEvent.KEYCODE_C, KeyEvent.META_CTRL_ON), + createKeyTrigger(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON), + createKeyTrigger(KeyEvent.KEYCODE_X, KeyEvent.META_CTRL_ON), + createKeyTrigger(KeyEvent.KEYCODE_Z, KeyEvent.META_CTRL_ON), + createKeyTrigger(KeyEvent.KEYCODE_Y, KeyEvent.META_CTRL_ON) + )); + + public InputGestureManager(Context context) { + mContext = context; + } + + public void systemRunning() { + initSystemShortcuts(); + blockListBookmarkedTriggers(); + } + + private void initSystemShortcuts() { + // Initialize all system shortcuts + List<InputGestureData> systemShortcuts = new ArrayList<>(List.of( + createKeyGesture( + KeyEvent.KEYCODE_A, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT + ), + createKeyGesture( + KeyEvent.KEYCODE_H, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_HOME + ), + createKeyGesture( + KeyEvent.KEYCODE_ENTER, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_HOME + ), + createKeyGesture( + KeyEvent.KEYCODE_I, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS + ), + createKeyGesture( + KeyEvent.KEYCODE_L, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN + ), + createKeyGesture( + KeyEvent.KEYCODE_N, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL + ), + createKeyGesture( + KeyEvent.KEYCODE_N, + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES + ), + createKeyGesture( + KeyEvent.KEYCODE_S, + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT + ), + createKeyGesture( + KeyEvent.KEYCODE_DEL, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_BACK + ), + createKeyGesture( + KeyEvent.KEYCODE_ESCAPE, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_BACK + ), + createKeyGesture( + KeyEvent.KEYCODE_DPAD_UP, + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION + ), + createKeyGesture( + KeyEvent.KEYCODE_DPAD_DOWN, + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE + ), + createKeyGesture( + KeyEvent.KEYCODE_DPAD_LEFT, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_BACK + ), + createKeyGesture( + KeyEvent.KEYCODE_DPAD_LEFT, + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT + ), + createKeyGesture( + KeyEvent.KEYCODE_DPAD_LEFT, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT + ), + createKeyGesture( + KeyEvent.KEYCODE_DPAD_RIGHT, + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT + ), + createKeyGesture( + KeyEvent.KEYCODE_DPAD_RIGHT, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT + ), + createKeyGesture( + KeyEvent.KEYCODE_SLASH, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER + ), + createKeyGesture( + KeyEvent.KEYCODE_TAB, + KeyEvent.META_META_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS + ) + )); + if (newBugreportKeyboardShortcut() && "1".equals(SystemProperties.get("ro.debuggable"))) { + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_DEL, + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT + )); + } + if (enableMoveToNextDisplayShortcut()) { + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_D, + KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY + )); + } + if (keyboardA11yShortcutControl()) { + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_T, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK + )); + if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()) { + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_3, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS + )); + } + if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()) { + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_4, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS + )); + } + if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()) { + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_5, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS + )); + } + if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()) { + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_6, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS + )); + } + if (enableTaskResizingKeyboardShortcuts()) { + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_LEFT_BRACKET, + KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW + )); + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_RIGHT_BRACKET, + KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW + )); + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_EQUALS, + KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW + )); + systemShortcuts.add(createKeyGesture( + KeyEvent.KEYCODE_MINUS, + KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE + )); + } + } + synchronized (mGestureLock) { + for (InputGestureData systemShortcut : systemShortcuts) { + mSystemShortcuts.put(systemShortcut.getTrigger(), systemShortcut); + } + } + } + + private void blockListBookmarkedTriggers() { + synchronized (mGestureLock) { + InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class)); + for (InputGestureData bookmark : im.getAppLaunchBookmarks()) { + mBlockListedTriggers.add(bookmark.getTrigger()); + } + } + } + @InputManager.CustomInputGestureResult public int addCustomInputGesture(int userId, InputGestureData newGesture) { - synchronized (mCustomInputGestures) { + synchronized (mGestureLock) { + if (mBlockListedTriggers.contains(newGesture.getTrigger()) + || mSystemShortcuts.containsKey(newGesture.getTrigger())) { + return InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE; + } + if (newGesture.getTrigger() instanceof InputGestureData.KeyTrigger keyTrigger) { + if (KeyEvent.isModifierKey(keyTrigger.getKeycode()) || + KeyEvent.isSystemKey(keyTrigger.getKeycode())) { + return InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE; + } + } if (!mCustomInputGestures.contains(userId)) { mCustomInputGestures.put(userId, new HashMap<>()); } @@ -69,7 +310,7 @@ final class InputGestureManager { @InputManager.CustomInputGestureResult public int removeCustomInputGesture(int userId, InputGestureData data) { - synchronized (mCustomInputGestures) { + synchronized (mGestureLock) { if (!mCustomInputGestures.contains(userId)) { return InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST; } @@ -88,14 +329,14 @@ final class InputGestureManager { } public void removeAllCustomInputGestures(int userId) { - synchronized (mCustomInputGestures) { + synchronized (mGestureLock) { mCustomInputGestures.remove(userId); } } @NonNull public List<InputGestureData> getCustomInputGestures(int userId) { - synchronized (mCustomInputGestures) { + synchronized (mGestureLock) { if (!mCustomInputGestures.contains(userId)) { return List.of(); } @@ -109,7 +350,7 @@ final class InputGestureManager { if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { return null; } - synchronized (mCustomInputGestures) { + synchronized (mGestureLock) { Map<InputGestureData.Trigger, InputGestureData> customGestures = mCustomInputGestures.get(userId); if (customGestures == null) { @@ -120,10 +361,44 @@ final class InputGestureManager { } } + @Nullable + public InputGestureData getSystemShortcutForKeyEvent(KeyEvent event) { + final int keyCode = event.getKeyCode(); + if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { + return null; + } + synchronized (mGestureLock) { + int modifierState = event.getMetaState() & KEY_GESTURE_META_MASK; + return mSystemShortcuts.get(InputGestureData.createKeyTrigger(keyCode, modifierState)); + } + } + + private static InputGestureData createKeyGesture(int keycode, int modifierState, + int keyGestureType) { + return new InputGestureData.Builder() + .setTrigger(createKeyTrigger(keycode, modifierState)) + .setKeyGestureType(keyGestureType) + .build(); + } + public void dump(IndentingPrintWriter ipw) { ipw.println("InputGestureManager:"); ipw.increaseIndent(); - synchronized (mCustomInputGestures) { + synchronized (mGestureLock) { + ipw.println("System Shortcuts:"); + ipw.increaseIndent(); + for (InputGestureData systemShortcut : mSystemShortcuts.values()) { + ipw.println(systemShortcut); + } + ipw.decreaseIndent(); + ipw.println("Blocklisted Triggers:"); + ipw.increaseIndent(); + for (InputGestureData.Trigger blocklistedTrigger : mBlockListedTriggers) { + ipw.println(blocklistedTrigger); + } + ipw.decreaseIndent(); + ipw.println("Custom Gestures:"); + ipw.increaseIndent(); int size = mCustomInputGestures.size(); for (int i = 0; i < size; i++) { Map<InputGestureData.Trigger, InputGestureData> customGestures = diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java index 5a3707dea1db..bf99fe858a9d 100644 --- a/services/core/java/com/android/server/input/KeyGestureController.java +++ b/services/core/java/com/android/server/input/KeyGestureController.java @@ -20,12 +20,8 @@ import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE; -import static com.android.hardware.input.Flags.keyboardA11yShortcutControl; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures; -import static com.android.server.flags.Flags.newBugreportKeyboardShortcut; -import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut; -import static com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts; import android.annotation.BinderThread; import android.annotation.MainThread; @@ -121,7 +117,7 @@ final class KeyGestureController { private final KeyCombinationManager mKeyCombinationManager; private final SettingsObserver mSettingsObserver; private final AppLaunchShortcutManager mAppLaunchShortcutManager; - private final InputGestureManager mInputGestureManager = new InputGestureManager(); + private final InputGestureManager mInputGestureManager; private static final Object mUserLock = new Object(); @UserIdInt @GuardedBy("mUserLock") @@ -133,7 +129,6 @@ final class KeyGestureController { private boolean mPendingHideRecentSwitcher; // Platform behaviors - private boolean mEnableBugReportKeyboardShortcut; private boolean mHasFeatureWatch; private boolean mHasFeatureLeanback; @@ -181,13 +176,12 @@ final class KeyGestureController { mKeyCombinationManager = new KeyCombinationManager(mHandler); mSettingsObserver = new SettingsObserver(mHandler); mAppLaunchShortcutManager = new AppLaunchShortcutManager(mContext); + mInputGestureManager = new InputGestureManager(mContext); initBehaviors(); initKeyCombinationRules(); } private void initBehaviors() { - mEnableBugReportKeyboardShortcut = "1".equals(SystemProperties.get("ro.debuggable")); - PackageManager pm = mContext.getPackageManager(); mHasFeatureWatch = pm.hasSystemFeature(FEATURE_WATCH); mHasFeatureLeanback = pm.hasSystemFeature(FEATURE_LEANBACK); @@ -441,6 +435,7 @@ final class KeyGestureController { public void systemRunning() { mSettingsObserver.observe(); mAppLaunchShortcutManager.systemRunning(); + mInputGestureManager.systemRunning(); } public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { @@ -517,6 +512,7 @@ final class KeyGestureController { mPendingCapsLockToggle = false; } + // Handle App launch shortcuts AppLaunchShortcutManager.InterceptKeyResult result = mAppLaunchShortcutManager.interceptKey( event); if (result.consumed()) { @@ -529,15 +525,21 @@ final class KeyGestureController { focusedToken, /* flags = */0, result.appLaunchData()); } + // Handle system shortcuts + if (firstDown) { + InputGestureData systemShortcut = mInputGestureManager.getSystemShortcutForKeyEvent( + event); + if (systemShortcut != null) { + return handleKeyGesture(deviceId, new int[]{keyCode}, metaState, + systemShortcut.getAction().keyGestureType(), + KeyGestureEvent.ACTION_GESTURE_COMPLETE, + displayId, focusedToken, /* flags = */0, + systemShortcut.getAction().appLaunchData()); + } + } + + // Handle system keys switch (keyCode) { - case KeyEvent.KEYCODE_A: - if (firstDown && event.isMetaPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - break; case KeyEvent.KEYCODE_RECENT_APPS: if (firstDown) { handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0, @@ -560,257 +562,6 @@ final class KeyGestureController { /* appLaunchData = */null); } return true; - case KeyEvent.KEYCODE_H: - case KeyEvent.KEYCODE_ENTER: - if (firstDown && event.isMetaPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_HOME, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - break; - case KeyEvent.KEYCODE_I: - if (firstDown && event.isMetaPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - break; - case KeyEvent.KEYCODE_L: - if (firstDown && event.isMetaPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - break; - case KeyEvent.KEYCODE_N: - if (firstDown && event.isMetaPressed()) { - if (event.isCtrlPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } else { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_S: - if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - break; - case KeyEvent.KEYCODE_T: - if (keyboardA11yShortcutControl()) { - if (firstDown && event.isMetaPressed() && event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_3: - if (InputSettings.isAccessibilityBounceKeysFeatureEnabled() - && keyboardA11yShortcutControl()) { - if (firstDown && event.isMetaPressed() && event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_4: - if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled() - && keyboardA11yShortcutControl()) { - if (firstDown && event.isMetaPressed() && event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_5: - if (InputSettings.isAccessibilityStickyKeysFeatureEnabled() - && keyboardA11yShortcutControl()) { - if (firstDown && event.isMetaPressed() && event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_6: - if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled() - && keyboardA11yShortcutControl()) { - if (firstDown && event.isMetaPressed() && event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_DEL: - if (newBugreportKeyboardShortcut()) { - if (firstDown && mEnableBugReportKeyboardShortcut && event.isMetaPressed() - && event.isCtrlPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - // fall through - case KeyEvent.KEYCODE_ESCAPE: - if (firstDown && event.isMetaPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_BACK, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - break; - case KeyEvent.KEYCODE_DPAD_UP: - if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - break; - case KeyEvent.KEYCODE_DPAD_LEFT: - if (firstDown && event.isMetaPressed()) { - if (event.isCtrlPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } else if (event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } else { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_BACK, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (firstDown && event.isMetaPressed()) { - if (event.isCtrlPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } else if (event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_D: - if (enableMoveToNextDisplayShortcut()) { - if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, - displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_LEFT_BRACKET: - if (enableTaskResizingKeyboardShortcuts()) { - if (firstDown && event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, - displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_RIGHT_BRACKET: - if (enableTaskResizingKeyboardShortcuts()) { - if (firstDown && event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, - displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_EQUALS: - if (enableTaskResizingKeyboardShortcuts()) { - if (firstDown && event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, - displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_MINUS: - if (enableTaskResizingKeyboardShortcuts()) { - if (firstDown && event.isAltPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, - KeyEvent.META_ALT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, - displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); - } - } - break; - case KeyEvent.KEYCODE_SLASH: - if (firstDown && event.isMetaPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } - break; case KeyEvent.KEYCODE_BRIGHTNESS_UP: case KeyEvent.KEYCODE_BRIGHTNESS_DOWN: if (down) { @@ -954,12 +705,7 @@ final class KeyGestureController { return true; case KeyEvent.KEYCODE_TAB: if (firstDown) { - if (event.isMetaPressed()) { - return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, /* appLaunchData = */null); - } else if (!mPendingHideRecentSwitcher) { + if (!mPendingHideRecentSwitcher) { final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; if (KeyEvent.metaStateHasModifiers( @@ -1020,6 +766,7 @@ final class KeyGestureController { return true; } + // Handle custom shortcuts if (firstDown) { InputGestureData customGesture; synchronized (mUserLock) { diff --git a/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt b/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt index 01c56b7148cd..862886ce69d2 100644 --- a/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt +++ b/tests/Input/src/com/android/server/input/InputGestureManagerTests.kt @@ -21,6 +21,7 @@ import android.hardware.input.InputManager import android.hardware.input.KeyGestureEvent import android.platform.test.annotations.Presubmit import android.view.KeyEvent +import androidx.test.core.app.ApplicationProvider import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -42,7 +43,7 @@ class InputGestureManagerTests { @Before fun setup() { - inputGestureManager = InputGestureManager() + inputGestureManager = InputGestureManager(ApplicationProvider.getApplicationContext()) } @Test diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index fc69a07afcdb..6c9f764bbdee 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -37,7 +37,6 @@ import android.os.Process import android.os.SystemClock import android.os.SystemProperties import android.os.test.TestLooper -import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.Presubmit import android.platform.test.flag.junit.SetFlagsRule @@ -123,6 +122,7 @@ class KeyGestureControllerTests { private var currentPid = 0 private lateinit var context: Context + private lateinit var keyGestureController: KeyGestureController private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession private lateinit var testLooper: TestLooper private var events = mutableListOf<KeyGestureEvent>() @@ -144,6 +144,7 @@ class KeyGestureControllerTests { } private fun setupBehaviors() { + Mockito.`when`(SystemProperties.get("ro.debuggable")).thenReturn("1") Mockito.`when`(resources.getBoolean(R.bool.config_enableScreenshotChord)).thenReturn(true) val testBookmarks: XmlResourceParser = context.resources.getXml( com.android.test.input.R.xml.bookmarks @@ -172,7 +173,14 @@ class KeyGestureControllerTests { ExtendedMockito.`when`(KeyCharacterMap.load(Mockito.anyInt())).thenReturn(kcm) } - private fun notifyHomeGestureCompleted(keyGestureController: KeyGestureController) { + private fun setupKeyGestureController() { + keyGestureController = KeyGestureController(context, testLooper.looper) + Mockito.`when`(iInputManager.getAppLaunchBookmarks()) + .thenReturn(keyGestureController.appLaunchBookmarks) + keyGestureController.systemRunning() + } + + private fun notifyHomeGestureCompleted() { keyGestureController.notifyKeyGestureCompleted( DEVICE_ID, intArrayOf(KeyEvent.KEYCODE_H), KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON, @@ -182,12 +190,12 @@ class KeyGestureControllerTests { @Test fun testKeyGestureEvent_registerUnregisterListener() { - val keyGestureController = KeyGestureController(context, testLooper.looper) + setupKeyGestureController() val listener = KeyGestureEventListener() // Register key gesture event listener keyGestureController.registerKeyGestureEventListener(listener, 0) - notifyHomeGestureCompleted(keyGestureController) + notifyHomeGestureCompleted() testLooper.dispatchAll() assertEquals( "Listener should get callbacks on key gesture event completed", @@ -203,7 +211,7 @@ class KeyGestureControllerTests { // Unregister listener events.clear() keyGestureController.unregisterKeyGestureEventListener(listener, 0) - notifyHomeGestureCompleted(keyGestureController) + notifyHomeGestureCompleted() testLooper.dispatchAll() assertEquals( "Listener should not get callback after being unregistered", @@ -214,7 +222,7 @@ class KeyGestureControllerTests { @Test fun testKeyGestureEvent_multipleGestureHandlers() { - val keyGestureController = KeyGestureController(context, testLooper.looper) + setupKeyGestureController() // Set up two callbacks. var callbackCount1 = 0 @@ -278,7 +286,7 @@ class KeyGestureControllerTests { } @Keep - private fun keyGestureEventHandlerTestArguments(): Array<TestData> { + private fun systemGesturesTestArguments(): Array<TestData> { return arrayOf( TestData( "META + A -> Launch Assistant", @@ -289,25 +297,6 @@ class KeyGestureControllerTests { intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), TestData( - "RECENT_APPS -> Show Overview", - intArrayOf(KeyEvent.KEYCODE_RECENT_APPS), - KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, - intArrayOf(KeyEvent.KEYCODE_RECENT_APPS), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "APP_SWITCH -> App Switch", - intArrayOf(KeyEvent.KEYCODE_APP_SWITCH), - KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH, - intArrayOf(KeyEvent.KEYCODE_APP_SWITCH), - 0, - intArrayOf( - KeyGestureEvent.ACTION_GESTURE_START, - KeyGestureEvent.ACTION_GESTURE_COMPLETE - ) - ), - TestData( "META + H -> Go Home", intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_H), KeyGestureEvent.KEY_GESTURE_TYPE_HOME, @@ -476,94 +465,6 @@ class KeyGestureControllerTests { intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), TestData( - "BRIGHTNESS_UP -> Brightness Up", - intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP), - KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP, - intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "BRIGHTNESS_DOWN -> Brightness Down", - intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_DOWN), - KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN, - intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_DOWN), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "KEYBOARD_BACKLIGHT_UP -> Keyboard Backlight Up", - intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP), - KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP, - intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "KEYBOARD_BACKLIGHT_DOWN -> Keyboard Backlight Down", - intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN), - KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN, - intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "KEYBOARD_BACKLIGHT_TOGGLE -> Keyboard Backlight Toggle", - intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE), - KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE, - intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "ALL_APPS -> Open App Drawer", - intArrayOf(KeyEvent.KEYCODE_ALL_APPS), - KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS, - intArrayOf(KeyEvent.KEYCODE_ALL_APPS), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "NOTIFICATION -> Toggle Notification Panel", - intArrayOf(KeyEvent.KEYCODE_NOTIFICATION), - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL, - intArrayOf(KeyEvent.KEYCODE_NOTIFICATION), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "LANGUAGE_SWITCH -> Switch Language Forward", - intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH), - KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, - intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "SHIFT + LANGUAGE_SWITCH -> Switch Language Backward", - intArrayOf(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_LANGUAGE_SWITCH), - KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, - intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH), - KeyEvent.META_SHIFT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "SCREENSHOT -> Take Screenshot", - intArrayOf(KeyEvent.KEYCODE_SCREENSHOT), - KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, - intArrayOf(KeyEvent.KEYCODE_SCREENSHOT), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "META -> Open Apps Drawer", - intArrayOf(KeyEvent.KEYCODE_META_LEFT), - KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS, - intArrayOf(KeyEvent.KEYCODE_META_LEFT), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( "META + ALT -> Toggle Caps Lock", intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT), KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, @@ -631,22 +532,6 @@ class KeyGestureControllerTests { intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) ), TestData( - "SYSRQ -> Take screenshot", - intArrayOf(KeyEvent.KEYCODE_SYSRQ), - KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, - intArrayOf(KeyEvent.KEYCODE_SYSRQ), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( - "ESC -> Close All Dialogs", - intArrayOf(KeyEvent.KEYCODE_ESCAPE), - KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, - intArrayOf(KeyEvent.KEYCODE_ESCAPE), - 0, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ), - TestData( "META + B -> Launch Default Browser", intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_B), KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, @@ -758,6 +643,305 @@ class KeyGestureControllerTests { AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest") ), TestData( + "META + CTRL + DEL -> Trigger Bug Report", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_CTRL_LEFT, + KeyEvent.KEYCODE_DEL + ), + KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT, + intArrayOf(KeyEvent.KEYCODE_DEL), + KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "Meta + Alt + 3 -> Toggle Bounce Keys", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_3 + ), + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS, + intArrayOf(KeyEvent.KEYCODE_3), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "Meta + Alt + 4 -> Toggle Mouse Keys", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_4 + ), + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS, + intArrayOf(KeyEvent.KEYCODE_4), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "Meta + Alt + 5 -> Toggle Sticky Keys", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_5 + ), + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS, + intArrayOf(KeyEvent.KEYCODE_5), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "Meta + Alt + 6 -> Toggle Slow Keys", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_6 + ), + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS, + intArrayOf(KeyEvent.KEYCODE_6), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "META + CTRL + D -> Move a task to next display", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_CTRL_LEFT, + KeyEvent.KEYCODE_D + ), + KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY, + intArrayOf(KeyEvent.KEYCODE_D), + KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "ALT + [ -> Resizes a task to fit the left half of the screen", + intArrayOf( + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_LEFT_BRACKET + ), + KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW, + intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET), + KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "ALT + ] -> Resizes a task to fit the right half of the screen", + intArrayOf( + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_RIGHT_BRACKET + ), + KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW, + intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET), + KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "ALT + '=' -> Maximizes a task to fit the screen", + intArrayOf( + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_EQUALS + ), + KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW, + intArrayOf(KeyEvent.KEYCODE_EQUALS), + KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "ALT + '-' -> Restores a task size to its previous bounds", + intArrayOf( + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_MINUS + ), + KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE, + intArrayOf(KeyEvent.KEYCODE_MINUS), + KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ) + ) + } + + @Test + @Parameters(method = "systemGesturesTestArguments") + @EnableFlags( + com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT, + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL, + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG, + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG, + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG, + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS, + com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT, + com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS + ) + fun testKeyGestures(test: TestData) { + setupKeyGestureController() + testKeyGestureInternal(test) + } + + @Test + @Parameters(method = "systemGesturesTestArguments") + @EnableFlags( + com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT, + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL, + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG, + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG, + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG, + com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS, + com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT, + com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS + ) + fun testCustomKeyGesturesNotAllowedForSystemGestures(test: TestData) { + setupKeyGestureController() + // Need to re-init so that bookmarks are correctly blocklisted + Mockito.`when`(iInputManager.getAppLaunchBookmarks()) + .thenReturn(keyGestureController.appLaunchBookmarks) + keyGestureController.systemRunning() + + val builder = InputGestureData.Builder() + .setKeyGestureType(test.expectedKeyGestureType) + .setTrigger( + InputGestureData.createKeyTrigger( + test.expectedKeys[0], + test.expectedModifierState + ) + ) + if (test.expectedAppLaunchData != null) { + builder.setAppLaunchData(test.expectedAppLaunchData) + } + assertEquals( + test.toString(), + InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE, + keyGestureController.addCustomInputGesture(0, builder.build().aidlData) + ) + } + + @Keep + private fun systemKeysTestArguments(): Array<TestData> { + return arrayOf( + TestData( + "RECENT_APPS -> Show Overview", + intArrayOf(KeyEvent.KEYCODE_RECENT_APPS), + KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, + intArrayOf(KeyEvent.KEYCODE_RECENT_APPS), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "APP_SWITCH -> App Switch", + intArrayOf(KeyEvent.KEYCODE_APP_SWITCH), + KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH, + intArrayOf(KeyEvent.KEYCODE_APP_SWITCH), + 0, + intArrayOf( + KeyGestureEvent.ACTION_GESTURE_START, + KeyGestureEvent.ACTION_GESTURE_COMPLETE + ) + ), + TestData( + "BRIGHTNESS_UP -> Brightness Up", + intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP), + KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP, + intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "BRIGHTNESS_DOWN -> Brightness Down", + intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_DOWN), + KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN, + intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_DOWN), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "KEYBOARD_BACKLIGHT_UP -> Keyboard Backlight Up", + intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP), + KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP, + intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "KEYBOARD_BACKLIGHT_DOWN -> Keyboard Backlight Down", + intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN), + KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN, + intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "KEYBOARD_BACKLIGHT_TOGGLE -> Keyboard Backlight Toggle", + intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE), + KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE, + intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "ALL_APPS -> Open App Drawer", + intArrayOf(KeyEvent.KEYCODE_ALL_APPS), + KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS, + intArrayOf(KeyEvent.KEYCODE_ALL_APPS), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "NOTIFICATION -> Toggle Notification Panel", + intArrayOf(KeyEvent.KEYCODE_NOTIFICATION), + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL, + intArrayOf(KeyEvent.KEYCODE_NOTIFICATION), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "LANGUAGE_SWITCH -> Switch Language Forward", + intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH), + KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, + intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "SHIFT + LANGUAGE_SWITCH -> Switch Language Backward", + intArrayOf(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_LANGUAGE_SWITCH), + KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, + intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH), + KeyEvent.META_SHIFT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "SCREENSHOT -> Take Screenshot", + intArrayOf(KeyEvent.KEYCODE_SCREENSHOT), + KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, + intArrayOf(KeyEvent.KEYCODE_SCREENSHOT), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "META -> Open Apps Drawer", + intArrayOf(KeyEvent.KEYCODE_META_LEFT), + KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS, + intArrayOf(KeyEvent.KEYCODE_META_LEFT), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "SYSRQ -> Take screenshot", + intArrayOf(KeyEvent.KEYCODE_SYSRQ), + KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, + intArrayOf(KeyEvent.KEYCODE_SYSRQ), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "ESC -> Close All Dialogs", + intArrayOf(KeyEvent.KEYCODE_ESCAPE), + KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, + intArrayOf(KeyEvent.KEYCODE_ESCAPE), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( "EXPLORER -> Launch Default Browser", intArrayOf(KeyEvent.KEYCODE_EXPLORER), KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, @@ -815,17 +999,15 @@ class KeyGestureControllerTests { } @Test - @Parameters(method = "keyGestureEventHandlerTestArguments") - fun testKeyGestures(test: TestData) { - val keyGestureController = KeyGestureController(context, testLooper.looper) - keyGestureController.systemRunning() - - testKeyGestureInternal(keyGestureController, test) + @Parameters(method = "systemKeysTestArguments") + fun testSystemKeys(test: TestData) { + setupKeyGestureController() + testKeyGestureInternal(test) } @Test fun testKeycodesFullyConsumed_irrespectiveOfHandlers() { - val keyGestureController = KeyGestureController(context, testLooper.looper) + setupKeyGestureController() val testKeys = intArrayOf( KeyEvent.KEYCODE_RECENT_APPS, KeyEvent.KEYCODE_APP_SWITCH, @@ -853,7 +1035,7 @@ class KeyGestureControllerTests { keyGestureController.registerKeyGestureHandler(handler, 0) for (key in testKeys) { - sendKeys(keyGestureController, intArrayOf(key), assertNotSentToApps = true) + sendKeys(intArrayOf(key), assertNotSentToApps = true) } } @@ -861,9 +1043,8 @@ class KeyGestureControllerTests { fun testSearchKeyGestures_defaultSearch() { Mockito.`when`(resources.getInteger(R.integer.config_searchKeyBehavior)) .thenReturn(SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH) - val keyGestureController = KeyGestureController(context, testLooper.looper) + setupKeyGestureController() testKeyGestureNotProduced( - keyGestureController, "SEARCH -> Default Search", intArrayOf(KeyEvent.KEYCODE_SEARCH), ) @@ -873,9 +1054,8 @@ class KeyGestureControllerTests { fun testSearchKeyGestures_searchActivity() { Mockito.`when`(resources.getInteger(R.integer.config_searchKeyBehavior)) .thenReturn(SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY) - val keyGestureController = KeyGestureController(context, testLooper.looper) + setupKeyGestureController() testKeyGestureInternal( - keyGestureController, TestData( "SEARCH -> Launch Search Activity", intArrayOf(KeyEvent.KEYCODE_SEARCH), @@ -891,9 +1071,8 @@ class KeyGestureControllerTests { fun testSettingKeyGestures_doNothing() { Mockito.`when`(resources.getInteger(R.integer.config_settingsKeyBehavior)) .thenReturn(SETTINGS_KEY_BEHAVIOR_NOTHING) - val keyGestureController = KeyGestureController(context, testLooper.looper) + setupKeyGestureController() testKeyGestureNotProduced( - keyGestureController, "SETTINGS -> Do Nothing", intArrayOf(KeyEvent.KEYCODE_SETTINGS), ) @@ -903,9 +1082,8 @@ class KeyGestureControllerTests { fun testSettingKeyGestures_settingsActivity() { Mockito.`when`(resources.getInteger(R.integer.config_settingsKeyBehavior)) .thenReturn(SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY) - val keyGestureController = KeyGestureController(context, testLooper.looper) + setupKeyGestureController() testKeyGestureInternal( - keyGestureController, TestData( "SETTINGS -> Launch Settings Activity", intArrayOf(KeyEvent.KEYCODE_SETTINGS), @@ -921,9 +1099,8 @@ class KeyGestureControllerTests { fun testSettingKeyGestures_notificationPanel() { Mockito.`when`(resources.getInteger(R.integer.config_settingsKeyBehavior)) .thenReturn(SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL) - val keyGestureController = KeyGestureController(context, testLooper.looper) + setupKeyGestureController() testKeyGestureInternal( - keyGestureController, TestData( "SETTINGS -> Toggle Notification Panel", intArrayOf(KeyEvent.KEYCODE_SETTINGS), @@ -936,221 +1113,12 @@ class KeyGestureControllerTests { } @Test - @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) - fun testTriggerBugReport() { - Mockito.`when`(SystemProperties.get("ro.debuggable")).thenReturn("1") - val keyGestureController = KeyGestureController(context, testLooper.looper) - testKeyGestureInternal( - keyGestureController, - TestData( - "META + CTRL + DEL -> Trigger Bug Report", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_CTRL_LEFT, - KeyEvent.KEYCODE_DEL - ), - KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT, - intArrayOf(KeyEvent.KEYCODE_DEL), - KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ) - ) - } - - @Test - @DisableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT) - fun testTriggerBugReport_flagDisabled() { - Mockito.`when`(SystemProperties.get("ro.debuggable")).thenReturn("1") - val keyGestureController = KeyGestureController(context, testLooper.looper) - testKeyGestureInternal( - keyGestureController, - TestData( - "META + CTRL + DEL -> Not Trigger Bug Report (Fallback to BACK)", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_CTRL_LEFT, - KeyEvent.KEYCODE_DEL - ), - KeyGestureEvent.KEY_GESTURE_TYPE_BACK, - intArrayOf(KeyEvent.KEYCODE_DEL), - KeyEvent.META_META_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ) - ) - } - - @Test - @EnableFlags( - com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL, - com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG, - com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG, - com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG, - com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS - ) - fun testKeyboardAccessibilityToggleShortcutPress() { - val keyGestureController = KeyGestureController(context, testLooper.looper) - testKeyGestureInternal( - keyGestureController, - TestData( - "Meta + Alt + 3 -> Toggle Bounce Keys", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_3 - ), - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS, - intArrayOf(KeyEvent.KEYCODE_3), - KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE))) - testKeyGestureInternal( - keyGestureController, - TestData( - "Meta + Alt + 4 -> Toggle Mouse Keys", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_4 - ), - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS, - intArrayOf(KeyEvent.KEYCODE_4), - KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE))) - testKeyGestureInternal( - keyGestureController, - TestData( - "Meta + Alt + 5 -> Toggle Sticky Keys", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_5 - ), - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS, - intArrayOf(KeyEvent.KEYCODE_5), - KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE))) - testKeyGestureInternal( - keyGestureController, - TestData( - "Meta + Alt + 6 -> Toggle Slow Keys", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_6 - ), - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS, - intArrayOf(KeyEvent.KEYCODE_6), - KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE))) - } - - @Test - @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT) - fun testMoveToNextDisplay() { - val keyGestureController = KeyGestureController(context, testLooper.looper) - testKeyGestureInternal( - keyGestureController, - TestData( - "META + CTRL + D -> Move a task to next display", - intArrayOf( - KeyEvent.KEYCODE_META_LEFT, - KeyEvent.KEYCODE_CTRL_LEFT, - KeyEvent.KEYCODE_D - ), - KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY, - intArrayOf(KeyEvent.KEYCODE_D), - KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ) - ) - } - - @Test - @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS) - fun testSnapLeftFreeformTask() { - val keyGestureController = KeyGestureController(context, testLooper.looper) - testKeyGestureInternal( - keyGestureController, - TestData( - "ALT + [ -> Resizes a task to fit the left half of the screen", - intArrayOf( - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_LEFT_BRACKET - ), - KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW, - intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET), - KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ) - ) - } - - @Test - @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS) - fun testSnapRightFreeformTask() { - val keyGestureController = KeyGestureController(context, testLooper.looper) - testKeyGestureInternal( - keyGestureController, - TestData( - "ALT + ] -> Resizes a task to fit the right half of the screen", - intArrayOf( - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_RIGHT_BRACKET - ), - KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW, - intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET), - KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ) - ) - } - - @Test - @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS) - fun testMaximizeFreeformTask() { - val keyGestureController = KeyGestureController(context, testLooper.looper) - testKeyGestureInternal( - keyGestureController, - TestData( - "ALT + '=' -> Maximizes a task to fit the screen", - intArrayOf( - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_EQUALS - ), - KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW, - intArrayOf(KeyEvent.KEYCODE_EQUALS), - KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ) - ) - } - - @Test - @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS) - fun testRestoreFreeformTask() { - val keyGestureController = KeyGestureController(context, testLooper.looper) - testKeyGestureInternal( - keyGestureController, - TestData( - "ALT + '-' -> Restores a task size to its previous bounds", - intArrayOf( - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_MINUS - ), - KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE, - intArrayOf(KeyEvent.KEYCODE_MINUS), - KeyEvent.META_ALT_ON, - intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ) - ) - } - - @Test fun testCapsLockPressNotified() { - val keyGestureController = KeyGestureController(context, testLooper.looper) + setupKeyGestureController() val listener = KeyGestureEventListener() keyGestureController.registerKeyGestureEventListener(listener, 0) - sendKeys(keyGestureController, intArrayOf(KeyEvent.KEYCODE_CAPS_LOCK)) + sendKeys(intArrayOf(KeyEvent.KEYCODE_CAPS_LOCK)) testLooper.dispatchAll() assertEquals( "Listener should get callbacks on key gesture event completed", @@ -1165,7 +1133,7 @@ class KeyGestureControllerTests { } @Keep - private fun keyGestureEventHandlerTestArguments_forKeyCombinations(): Array<TestData> { + private fun systemGesturesTestArguments_forKeyCombinations(): Array<TestData> { return arrayOf( TestData( "VOLUME_DOWN + POWER -> Screenshot Chord", @@ -1226,14 +1194,14 @@ class KeyGestureControllerTests { } @Test - @Parameters(method = "keyGestureEventHandlerTestArguments_forKeyCombinations") + @Parameters(method = "systemGesturesTestArguments_forKeyCombinations") @EnableFlags( com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER, com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER_MULTI_PRESS_GESTURES ) fun testKeyCombinationGestures(test: TestData) { - val keyGestureController = KeyGestureController(context, testLooper.looper) - testKeyGestureInternal(keyGestureController, test) + setupKeyGestureController() + testKeyGestureInternal(test) } @Keep @@ -1274,7 +1242,7 @@ class KeyGestureControllerTests { @Test @Parameters(method = "customInputGesturesTestArguments") fun testCustomKeyGestures(test: TestData) { - val keyGestureController = KeyGestureController(context, testLooper.looper) + setupKeyGestureController() val builder = InputGestureData.Builder() .setKeyGestureType(test.expectedKeyGestureType) .setTrigger( @@ -1282,17 +1250,17 @@ class KeyGestureControllerTests { test.expectedKeys[0], test.expectedModifierState ) - ); + ) if (test.expectedAppLaunchData != null) { builder.setAppLaunchData(test.expectedAppLaunchData) } - val inputGestureData = builder.build(); + val inputGestureData = builder.build() keyGestureController.addCustomInputGesture(0, inputGestureData.aidlData) - testKeyGestureInternal(keyGestureController, test) + testKeyGestureInternal(test) } - private fun testKeyGestureInternal(keyGestureController: KeyGestureController, test: TestData) { + private fun testKeyGestureInternal(test: TestData) { var handleEvents = mutableListOf<KeyGestureEvent>() val handler = KeyGestureHandler { event, _ -> handleEvents.add(KeyGestureEvent(event)) @@ -1301,7 +1269,7 @@ class KeyGestureControllerTests { keyGestureController.registerKeyGestureHandler(handler, 0) handleEvents.clear() - sendKeys(keyGestureController, test.keys) + sendKeys(test.keys) assertEquals( "Test: $test doesn't produce correct number of key gesture events", @@ -1340,11 +1308,7 @@ class KeyGestureControllerTests { keyGestureController.unregisterKeyGestureHandler(handler, 0) } - private fun testKeyGestureNotProduced( - keyGestureController: KeyGestureController, - testName: String, - testKeys: IntArray - ) { + private fun testKeyGestureNotProduced(testName: String, testKeys: IntArray) { var handleEvents = mutableListOf<KeyGestureEvent>() val handler = KeyGestureHandler { event, _ -> handleEvents.add(KeyGestureEvent(event)) @@ -1353,15 +1317,11 @@ class KeyGestureControllerTests { keyGestureController.registerKeyGestureHandler(handler, 0) handleEvents.clear() - sendKeys(keyGestureController, testKeys) + sendKeys(testKeys) assertEquals("Test: $testName should not produce Key gesture", 0, handleEvents.size) } - private fun sendKeys( - keyGestureController: KeyGestureController, - testKeys: IntArray, - assertNotSentToApps: Boolean = false - ) { + private fun sendKeys(testKeys: IntArray, assertNotSentToApps: Boolean = false) { var metaState = 0 val now = SystemClock.uptimeMillis() for (key in testKeys) { @@ -1370,7 +1330,7 @@ class KeyGestureControllerTests { DEVICE_ID, 0 /*scancode*/, 0 /*flags*/, InputDevice.SOURCE_KEYBOARD ) - interceptKey(keyGestureController, downEvent, assertNotSentToApps) + interceptKey(downEvent, assertNotSentToApps) metaState = metaState or MODIFIER.getOrDefault(key, 0) downEvent.recycle() @@ -1383,7 +1343,7 @@ class KeyGestureControllerTests { DEVICE_ID, 0 /*scancode*/, 0 /*flags*/, InputDevice.SOURCE_KEYBOARD ) - interceptKey(keyGestureController, upEvent, assertNotSentToApps) + interceptKey(upEvent, assertNotSentToApps) metaState = metaState and MODIFIER.getOrDefault(key, 0).inv() upEvent.recycle() @@ -1391,11 +1351,7 @@ class KeyGestureControllerTests { } } - private fun interceptKey( - keyGestureController: KeyGestureController, - event: KeyEvent, - assertNotSentToApps: Boolean - ) { + private fun interceptKey(event: KeyEvent, assertNotSentToApps: Boolean) { keyGestureController.interceptKeyBeforeQueueing(event, FLAG_INTERACTIVE) testLooper.dispatchAll() |