Loading core/java/android/hardware/input/KeyGestureEvent.java +4 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT = 60; public static final int KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS = 61; public static final int KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY = 62; public static final int KEY_GESTURE_TYPE_TOGGLE_TALKBACK = 63; public static final int FLAG_CANCELLED = 1; Loading Loading @@ -177,6 +178,7 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT, KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY, KEY_GESTURE_TYPE_TOGGLE_TALKBACK, }) @Retention(RetentionPolicy.SOURCE) public @interface KeyGestureType { Loading Loading @@ -597,6 +599,8 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT"; case KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS: return "KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS"; case KEY_GESTURE_TYPE_TOGGLE_TALKBACK: return "KEY_GESTURE_TYPE_TOGGLE_TALKBACK"; default: return Integer.toHexString(value); } Loading services/core/java/com/android/server/input/KeyGestureController.java +12 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ 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; Loading Loading @@ -577,6 +578,17 @@ final class KeyGestureController { focusedToken, /* flags = */0); } 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); } } break; case KeyEvent.KEYCODE_DEL: if (newBugreportKeyboardShortcut()) { if (firstDown && mEnableBugReportKeyboardShortcut && event.isMetaPressed() Loading services/core/java/com/android/server/policy/PhoneWindowManager.java +25 −1 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled; import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable; import static com.android.hardware.input.Flags.keyboardA11yShortcutControl; import static com.android.hardware.input.Flags.modifierShortcutDump; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures; Loading Loading @@ -1615,7 +1616,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TRIPLE_PRESS_PRIMARY_NOTHING: break; case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY: mTalkbackShortcutController.toggleTalkback(mCurrentUserId); mTalkbackShortcutController.toggleTalkback(mCurrentUserId, TalkbackShortcutController.ShortcutSource.GESTURE); if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) { performHapticFeedback(HapticFeedbackConstants.CONFIRM, "Stem primary - Triple Press - Toggle Accessibility"); Loading Loading @@ -3603,6 +3605,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } break; case KeyEvent.KEYCODE_T: if (keyboardA11yShortcutControl()) { if (firstDown && event.isMetaPressed() && event.isAltPressed()) { mTalkbackShortcutController.toggleTalkback(mCurrentUserId, TalkbackShortcutController.ShortcutSource.KEYBOARD); notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK); return true; } } break; case KeyEvent.KEYCODE_DEL: if (newBugreportKeyboardShortcut()) { if (mEnableBugReportKeyboardShortcut && firstDown Loading Loading @@ -4034,6 +4047,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD: return mDefaultDisplayPolicy.isAwake() && mAccessibilityShortcutController .isAccessibilityShortcutAvailable(false); case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK: return keyboardA11yShortcutControl(); default: return false; } Loading Loading @@ -4251,6 +4266,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { mContext.closeSystemDialogs(); } return true; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK: if (keyboardA11yShortcutControl()) { if (complete) { mTalkbackShortcutController.toggleTalkback(mCurrentUserId, TalkbackShortcutController.ShortcutSource.KEYBOARD); } return true; } break; } return false; } Loading services/core/java/com/android/server/policy/TalkbackShortcutController.java +9 −4 Original line number Diff line number Diff line Loading @@ -44,6 +44,11 @@ class TalkbackShortcutController { private final Context mContext; private final PackageManager mPackageManager; public enum ShortcutSource { GESTURE, KEYBOARD, } TalkbackShortcutController(Context context) { mContext = context; mPackageManager = mContext.getPackageManager(); Loading @@ -55,7 +60,7 @@ class TalkbackShortcutController { * @return talkback state after toggle. {@code true} if talkback is enabled, {@code false} if * talkback is disabled */ boolean toggleTalkback(int userId) { boolean toggleTalkback(int userId, ShortcutSource source) { final Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId); ComponentName componentName = getTalkbackComponent(); Loading @@ -65,13 +70,13 @@ class TalkbackShortcutController { boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName); if (isTalkBackShortcutGestureEnabled()) { if (source == ShortcutSource.KEYBOARD || isTalkBackShortcutGestureEnabled()) { isTalkbackAlreadyEnabled = !isTalkbackAlreadyEnabled; AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, isTalkbackAlreadyEnabled); isTalkbackAlreadyEnabled, userId); // log stem triple press telemetry if it's a talkback enabled event. if (isTalkbackAlreadyEnabled) { if (source == ShortcutSource.GESTURE && isTalkbackAlreadyEnabled) { logStemTriplePressAccessibilityTelemetry(componentName); } } Loading services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java +24 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.view.KeyEvent; import androidx.test.filters.MediumTest; import com.android.hardware.input.Flags; import com.android.internal.annotations.Keep; import junit.framework.Assert; Loading Loading @@ -393,6 +394,17 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { META_ON | CTRL_ON); } @Test @EnableFlags(Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL) @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) public void testToggleTalkbackPress() { testShortcutInternal("Meta + Alt + T -> Toggle talkback", new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_T}, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK, KeyEvent.KEYCODE_T, META_ON | ALT_ON); } private void testShortcutInternal(String testName, int[] testKeys, @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey, int expectedModifierState) { Loading Loading @@ -699,4 +711,16 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS)); mPhoneWindowManager.assertCloseAllDialogs(); } @Test @EnableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL) public void testKeyGestureToggleTalkback() { Assert.assertTrue( sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); mPhoneWindowManager.assertTalkBack(true); Assert.assertTrue( sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); mPhoneWindowManager.assertTalkBack(false); } } Loading
core/java/android/hardware/input/KeyGestureEvent.java +4 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT = 60; public static final int KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS = 61; public static final int KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY = 62; public static final int KEY_GESTURE_TYPE_TOGGLE_TALKBACK = 63; public static final int FLAG_CANCELLED = 1; Loading Loading @@ -177,6 +178,7 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT, KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY, KEY_GESTURE_TYPE_TOGGLE_TALKBACK, }) @Retention(RetentionPolicy.SOURCE) public @interface KeyGestureType { Loading Loading @@ -597,6 +599,8 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT"; case KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS: return "KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS"; case KEY_GESTURE_TYPE_TOGGLE_TALKBACK: return "KEY_GESTURE_TYPE_TOGGLE_TALKBACK"; default: return Integer.toHexString(value); } Loading
services/core/java/com/android/server/input/KeyGestureController.java +12 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ 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; Loading Loading @@ -577,6 +578,17 @@ final class KeyGestureController { focusedToken, /* flags = */0); } 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); } } break; case KeyEvent.KEYCODE_DEL: if (newBugreportKeyboardShortcut()) { if (firstDown && mEnableBugReportKeyboardShortcut && event.isMetaPressed() Loading
services/core/java/com/android/server/policy/PhoneWindowManager.java +25 −1 Original line number Diff line number Diff line Loading @@ -84,6 +84,7 @@ import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled; import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable; import static com.android.hardware.input.Flags.keyboardA11yShortcutControl; import static com.android.hardware.input.Flags.modifierShortcutDump; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures; Loading Loading @@ -1615,7 +1616,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TRIPLE_PRESS_PRIMARY_NOTHING: break; case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY: mTalkbackShortcutController.toggleTalkback(mCurrentUserId); mTalkbackShortcutController.toggleTalkback(mCurrentUserId, TalkbackShortcutController.ShortcutSource.GESTURE); if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) { performHapticFeedback(HapticFeedbackConstants.CONFIRM, "Stem primary - Triple Press - Toggle Accessibility"); Loading Loading @@ -3603,6 +3605,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } break; case KeyEvent.KEYCODE_T: if (keyboardA11yShortcutControl()) { if (firstDown && event.isMetaPressed() && event.isAltPressed()) { mTalkbackShortcutController.toggleTalkback(mCurrentUserId, TalkbackShortcutController.ShortcutSource.KEYBOARD); notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK); return true; } } break; case KeyEvent.KEYCODE_DEL: if (newBugreportKeyboardShortcut()) { if (mEnableBugReportKeyboardShortcut && firstDown Loading Loading @@ -4034,6 +4047,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD: return mDefaultDisplayPolicy.isAwake() && mAccessibilityShortcutController .isAccessibilityShortcutAvailable(false); case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK: return keyboardA11yShortcutControl(); default: return false; } Loading Loading @@ -4251,6 +4266,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { mContext.closeSystemDialogs(); } return true; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK: if (keyboardA11yShortcutControl()) { if (complete) { mTalkbackShortcutController.toggleTalkback(mCurrentUserId, TalkbackShortcutController.ShortcutSource.KEYBOARD); } return true; } break; } return false; } Loading
services/core/java/com/android/server/policy/TalkbackShortcutController.java +9 −4 Original line number Diff line number Diff line Loading @@ -44,6 +44,11 @@ class TalkbackShortcutController { private final Context mContext; private final PackageManager mPackageManager; public enum ShortcutSource { GESTURE, KEYBOARD, } TalkbackShortcutController(Context context) { mContext = context; mPackageManager = mContext.getPackageManager(); Loading @@ -55,7 +60,7 @@ class TalkbackShortcutController { * @return talkback state after toggle. {@code true} if talkback is enabled, {@code false} if * talkback is disabled */ boolean toggleTalkback(int userId) { boolean toggleTalkback(int userId, ShortcutSource source) { final Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId); ComponentName componentName = getTalkbackComponent(); Loading @@ -65,13 +70,13 @@ class TalkbackShortcutController { boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName); if (isTalkBackShortcutGestureEnabled()) { if (source == ShortcutSource.KEYBOARD || isTalkBackShortcutGestureEnabled()) { isTalkbackAlreadyEnabled = !isTalkbackAlreadyEnabled; AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, isTalkbackAlreadyEnabled); isTalkbackAlreadyEnabled, userId); // log stem triple press telemetry if it's a talkback enabled event. if (isTalkbackAlreadyEnabled) { if (source == ShortcutSource.GESTURE && isTalkbackAlreadyEnabled) { logStemTriplePressAccessibilityTelemetry(componentName); } } Loading
services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java +24 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.view.KeyEvent; import androidx.test.filters.MediumTest; import com.android.hardware.input.Flags; import com.android.internal.annotations.Keep; import junit.framework.Assert; Loading Loading @@ -393,6 +394,17 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { META_ON | CTRL_ON); } @Test @EnableFlags(Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL) @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) public void testToggleTalkbackPress() { testShortcutInternal("Meta + Alt + T -> Toggle talkback", new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_T}, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK, KeyEvent.KEYCODE_T, META_ON | ALT_ON); } private void testShortcutInternal(String testName, int[] testKeys, @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey, int expectedModifierState) { Loading Loading @@ -699,4 +711,16 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS)); mPhoneWindowManager.assertCloseAllDialogs(); } @Test @EnableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL) public void testKeyGestureToggleTalkback() { Assert.assertTrue( sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); mPhoneWindowManager.assertTalkBack(true); Assert.assertTrue( sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); mPhoneWindowManager.assertTalkBack(false); } }