From 292152573ad9e8b7563e114a8942451be3d58a34 Mon Sep 17 00:00:00 2001 From: Arthur Hung Date: Wed, 11 Aug 2021 01:38:15 +0000 Subject: [PATCH] Consuming common key combinations in app priority Some apps or games could use the combo keys such as holding Ctrl or Alt to trigger its own shortcut or behavior. To prevent the system shortcut might be handled before dispatching to the app, we move these keys handling to 'dispatchUnhandledKey' when the app didn't handle. This will also fix the corresponding state when the modifier key is released. Bug: 156619364 Bug: 213096704 Test: atest AppKeyCombinationsTest Change-Id: I42770045c61ad6396b27132b8a61d34fc121d77e --- core/java/android/view/View.java | 4 +- core/java/android/view/ViewRootImpl.java | 2 +- .../server/input/InputShellCommand.java | 7 ++- .../policy/ModifierShortcutManager.java | 7 ++- .../server/policy/PhoneWindowManager.java | 63 +++++++++++++------ 5 files changed, 59 insertions(+), 24 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 8fda48b1a36a7..258359e982648 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15272,7 +15272,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param event the KeyEvent object that defines the button action */ public boolean onKeyDown(int keyCode, KeyEvent event) { - if (KeyEvent.isConfirmKey(keyCode)) { + if (event.hasNoModifiers() && KeyEvent.isConfirmKey(keyCode)) { if ((mViewFlags & ENABLED_MASK) == DISABLED) { return true; } @@ -15329,7 +15329,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param event The KeyEvent object that defines the button action. */ public boolean onKeyUp(int keyCode, KeyEvent event) { - if (KeyEvent.isConfirmKey(keyCode)) { + if (event.hasNoModifiers() && KeyEvent.isConfirmKey(keyCode)) { if ((mViewFlags & ENABLED_MASK) == DISABLED) { return true; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 748e551ecfc44..f86abec353ccf 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -7596,7 +7596,7 @@ public final class ViewRootImpl implements ViewParent, // When a new focused view is selected, we consume the navigation key because // navigation doesn't make much sense unless a view already has focus so // the key's purpose is to set focus. - if (isNavigationKey(event)) { + if (event.hasNoModifiers() && isNavigationKey(event)) { return ensureTouchMode(false); } diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java index 9fa6fad4f9269..773dc680d4cbf 100644 --- a/services/core/java/com/android/server/input/InputShellCommand.java +++ b/services/core/java/com/android/server/input/InputShellCommand.java @@ -560,7 +560,12 @@ public class InputShellCommand extends ShellCommand { sleep(duration); for (KeyEvent event: events) { - injectKeyEventAsync(KeyEvent.changeAction(event, KeyEvent.ACTION_UP)); + final int keyCode = event.getKeyCode(); + final KeyEvent upEvent = new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, + 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/, + inputSource); + injectKeyEventAsync(upEvent); + metaState &= ~MODIFIER.getOrDefault(keyCode, 0); } } diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java index e857d32c34491..784e177d6362c 100644 --- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java +++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java @@ -112,10 +112,15 @@ class ModifierShortcutManager { * @return The intent that matches the shortcut, or null if not found. */ private Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) { + // If a modifier key other than shift is also pressed, skip it. + final boolean isShiftOn = KeyEvent.metaStateHasModifiers(metaState, KeyEvent.META_SHIFT_ON); + if (!isShiftOn && !KeyEvent.metaStateHasNoModifiers(metaState)) { + return null; + } + ShortcutInfo shortcut = null; // If the Shift key is pressed, then search for the shift shortcuts. - boolean isShiftOn = (metaState & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON; SparseArray shortcutMap = isShiftOn ? mShiftShortcuts : mIntentShortcuts; // First try the exact keycode (with modifiers). diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4cced17917f69..28f65cf6d1a04 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2696,6 +2696,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean canceled = event.isCanceled(); final int displayId = event.getDisplayId(); final long key_consumed = -1; + final long key_not_consumed = 0; if (DEBUG_INPUT) { Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount=" @@ -2885,7 +2886,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_TAB: if (event.isMetaPressed()) { // Pass through keyboard navigation keys. - return 0; + return key_not_consumed; } // Display task switcher for ALT-TAB. if (down && repeatCount == 0) { @@ -2916,9 +2917,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { return key_consumed; case KeyEvent.KEYCODE_SPACE: - // Handle keyboard layout switching. - if ((metaState & (KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK)) == 0) { - return 0; + // Handle keyboard layout switching. (META + SPACE) + if ((metaState & KeyEvent.META_META_MASK) == 0) { + return key_not_consumed; } // Share the same behavior with KEYCODE_LANGUAGE_SWITCH. case KeyEvent.KEYCODE_LANGUAGE_SWITCH: @@ -2992,7 +2993,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } // Let the application handle the key. - return 0; + return key_not_consumed; } /** @@ -3058,6 +3059,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { + ", policyFlags=" + policyFlags); } + if (interceptUnhandledKey(event)) { + return null; + } + KeyEvent fallbackEvent = null; if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { final KeyCharacterMap kcm = event.getKeyCharacterMap(); @@ -3112,13 +3117,46 @@ public class PhoneWindowManager implements WindowManagerPolicy { return fallbackEvent; } + private boolean interceptUnhandledKey(KeyEvent event) { + final int keyCode = event.getKeyCode(); + final int repeatCount = event.getRepeatCount(); + final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; + final int metaState = event.getModifiers(); + + switch(keyCode) { + case KeyEvent.KEYCODE_SPACE: + if (down && repeatCount == 0) { + // Handle keyboard layout switching. (CTRL + SPACE) + if (KeyEvent.metaStateHasModifiers(metaState, KeyEvent.META_CTRL_ON)) { + int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; + mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); + return true; + } + } + break; + case KeyEvent.KEYCODE_Z: + if (down && KeyEvent.metaStateHasModifiers(metaState, + KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)) { + // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users. + if (mAccessibilityShortcutController + .isAccessibilityShortcutAvailable(isKeyguardLocked())) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT)); + return true; + } + } + break; + } + + return false; + } + private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent, int policyFlags) { int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags); if ((actions & ACTION_PASS_TO_USER) != 0) { long delayMillis = interceptKeyBeforeDispatching( focusedToken, fallbackEvent, policyFlags); - if (delayMillis == 0) { + if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent)) { return true; } } @@ -3989,19 +4027,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users. - if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(isKeyguardLocked())) { - switch (keyCode) { - case KeyEvent.KEYCODE_Z: { - if (down && event.isCtrlPressed() && event.isAltPressed()) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT)); - result &= ~ACTION_PASS_TO_USER; - } - break; - } - } - } - if (useHapticFeedback) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false, "Virtual Key - Press");