Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d89f6e7 authored by David Padlipsky's avatar David Padlipsky
Browse files

Implement shortcut controls for a11y keyboard features

Bug: 331682470
Test: atest com.android.server.policy.KeyGestureEventTests
Flag: com.android.hardware.input.keyboard_a11y_shortcut_control
Change-Id: I9a71bb01b2e768f3d162cc51bfc069a084da3fe4
parent c19a2266
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -71,6 +71,20 @@ public class InputSettings {
    @SuppressLint("UnflaggedApi") // TestApi without associated feature.
    public static final int DEFAULT_POINTER_SPEED = 0;

    /**
     * Bounce Keys Threshold: The default value of the threshold (500 ms).
     *
     * @hide
     */
    public static final int DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS = 500;

    /**
     * Slow Keys Threshold: The default value of the threshold (500 ms).
     *
     * @hide
     */
    public static final int DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS = 500;

    /**
     * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1).
     * @hide
+16 −0
Original line number Diff line number Diff line
@@ -109,6 +109,10 @@ public final class KeyGestureEvent {
    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 KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS = 64;
    public static final int KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS = 65;
    public static final int KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS = 66;
    public static final int KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS = 67;

    public static final int FLAG_CANCELLED = 1;

@@ -187,6 +191,10 @@ public final class KeyGestureEvent {
            KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
            KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY,
            KEY_GESTURE_TYPE_TOGGLE_TALKBACK,
            KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
            KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
            KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
            KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface KeyGestureType {
@@ -733,6 +741,14 @@ public final class KeyGestureEvent {
                return "KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS";
            case KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
                return "KEY_GESTURE_TYPE_TOGGLE_TALKBACK";
            case KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
                return "KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS";
            case KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
                return "KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS";
            case KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
                return "KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS";
            case KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
                return "KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS";
            default:
                return Integer.toHexString(value);
        }
+49 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.hardware.input.AidlKeyGestureEvent;
import android.hardware.input.IKeyGestureEventListener;
import android.hardware.input.IKeyGestureHandler;
import android.hardware.input.InputManager;
import android.hardware.input.InputSettings;
import android.hardware.input.KeyGestureEvent;
import android.os.Handler;
import android.os.IBinder;
@@ -589,6 +590,54 @@ final class KeyGestureController {
                    }
                }
                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);
                    }
                }
                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);
                    }
                }
                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);
                    }
                }
                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);
                    }
                }
                break;
            case KeyEvent.KEYCODE_DEL:
                if (newBugreportKeyboardShortcut()) {
                    if (firstDown && mEnableBugReportKeyboardShortcut && event.isMetaPressed()
+126 −0
Original line number Diff line number Diff line
@@ -151,6 +151,7 @@ import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.hardware.input.AppLaunchData;
import android.hardware.input.InputManager;
import android.hardware.input.InputSettings;
import android.hardware.input.KeyGestureEvent;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
@@ -3617,6 +3618,68 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    }
                }
                break;
            case KeyEvent.KEYCODE_3:
                if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()
                        && keyboardA11yShortcutControl()) {
                    if (firstDown && event.isMetaPressed()
                            && event.isAltPressed()) {
                        final boolean bounceKeysEnabled =
                                InputSettings.isAccessibilityBounceKeysEnabled(
                                        mContext);
                        InputSettings.setAccessibilityBounceKeysThreshold(mContext,
                                bounceKeysEnabled ? 0
                                        : InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS);
                        notifyKeyGestureCompleted(event,
                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS);
                        return true;
                    }
                }
                break;
            case KeyEvent.KEYCODE_4:
                if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
                        && keyboardA11yShortcutControl()) {
                    if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
                        final boolean mouseKeysEnabled =
                                InputSettings.isAccessibilityMouseKeysEnabled(
                                        mContext);
                        InputSettings.setAccessibilityMouseKeysEnabled(mContext,
                                !mouseKeysEnabled);
                        notifyKeyGestureCompleted(event,
                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS);
                        return true;
                    }
                }
                break;
            case KeyEvent.KEYCODE_5:
                if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()
                        && keyboardA11yShortcutControl()) {
                    if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
                        final boolean stickyKeysEnabled =
                                InputSettings.isAccessibilityStickyKeysEnabled(
                                        mContext);
                        InputSettings.setAccessibilityStickyKeysEnabled(mContext,
                                !stickyKeysEnabled);
                        notifyKeyGestureCompleted(event,
                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS);
                        return true;
                    }
                }
                break;
            case KeyEvent.KEYCODE_6:
                if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
                        && keyboardA11yShortcutControl()) {
                    if (firstDown && event.isMetaPressed() && event.isAltPressed()) {
                        final boolean slowKeysEnabled =
                                InputSettings.isAccessibilitySlowKeysEnabled(mContext);
                        InputSettings.setAccessibilitySlowKeysThreshold(mContext,
                                slowKeysEnabled ? 0
                                        : InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS);
                        notifyKeyGestureCompleted(event,
                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS);
                        return true;
                    }
                }
                break;
            case KeyEvent.KEYCODE_DEL:
                if (newBugreportKeyboardShortcut()) {
                    if (mEnableBugReportKeyboardShortcut && firstDown
@@ -4053,6 +4116,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                                .isAccessibilityShortcutAvailable(false);
                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
                        return keyboardA11yShortcutControl();
                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
                        return InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
                                && keyboardA11yShortcutControl();
                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
                        return InputSettings.isAccessibilityBounceKeysFeatureEnabled()
                                && keyboardA11yShortcutControl();
                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
                        return InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
                                && keyboardA11yShortcutControl();
                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
                        return InputSettings.isAccessibilityStickyKeysFeatureEnabled()
                                && keyboardA11yShortcutControl();
                    default:
                        return false;
                }
@@ -4286,6 +4361,57 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                if (complete && isUserSetupComplete() && !keyguardOn
                        && data != null && mModifierShortcutManager.launchApplication(data)) {
                    dismissKeyboardShortcutsMenu();
                }
                return true;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
                if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()
                        && keyboardA11yShortcutControl()) {
                    if (complete) {
                        final boolean bounceKeysEnabled =
                                InputSettings.isAccessibilityBounceKeysEnabled(
                                        mContext);
                        InputSettings.setAccessibilityBounceKeysThreshold(mContext,
                                bounceKeysEnabled ? 0
                                        : InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS);
                    }
                    return true;
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
                if (InputSettings.isAccessibilityMouseKeysFeatureFlagEnabled()
                        && keyboardA11yShortcutControl()) {
                    if (complete) {
                        final boolean mouseKeysEnabled =
                                InputSettings.isAccessibilityMouseKeysEnabled(
                                        mContext);
                        InputSettings.setAccessibilityMouseKeysEnabled(mContext,
                                !mouseKeysEnabled);
                    }
                    return true;
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
                if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()
                        && keyboardA11yShortcutControl()) {
                    if (complete) {
                        final boolean stickyKeysEnabled =
                                InputSettings.isAccessibilityStickyKeysEnabled(mContext);
                        InputSettings.setAccessibilityStickyKeysEnabled(mContext,
                                !stickyKeysEnabled);
                    }
                    return true;
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
                if (InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
                        && keyboardA11yShortcutControl()) {
                    if (complete) {
                        final boolean slowKeysEnabled =
                                InputSettings.isAccessibilitySlowKeysEnabled(mContext);
                        InputSettings.setAccessibilitySlowKeysThreshold(mContext,
                                slowKeysEnabled ? 0
                                        : InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS);
                    }
                    return true;
                }
                break;
+83 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAV
import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
import static com.android.server.policy.PhoneWindowManager.SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL;

import android.hardware.input.InputSettings;
import android.hardware.input.KeyGestureEvent;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
@@ -405,6 +406,36 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
                META_ON | ALT_ON);
    }

    @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})
    @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
    public void testKeyboardAccessibilityToggleShortcutPress() {
        testShortcutInternal("Meta + Alt + 3 -> Toggle Bounce Keys",
                new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_3},
                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
                KeyEvent.KEYCODE_3,
                META_ON | ALT_ON);
        testShortcutInternal("Meta + Alt + 4 -> Toggle Mouse Keys",
                new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_4},
                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
                KeyEvent.KEYCODE_4,
                META_ON | ALT_ON);
        testShortcutInternal("Meta + Alt + 5 -> Toggle Sticky Keys",
                new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_5},
                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
                KeyEvent.KEYCODE_5,
                META_ON | ALT_ON);
        testShortcutInternal("Meta + Alt + 6 -> Toggle Slow Keys",
                new int[]{META_KEY, ALT_KEY, KeyEvent.KEYCODE_6},
                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS,
                KeyEvent.KEYCODE_6,
                META_ON | ALT_ON);
    }

    private void testShortcutInternal(String testName, int[] testKeys,
            @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
            int expectedModifierState) {
@@ -723,4 +754,56 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK));
        mPhoneWindowManager.assertTalkBack(false);
    }

    @Test
    @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG})
    public void testKeyGestureToggleStickyKeys() {
        Assert.assertTrue(
                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS));
        Assert.assertTrue(InputSettings.isAccessibilityStickyKeysEnabled(mContext));

        Assert.assertTrue(
                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS));
        Assert.assertFalse(InputSettings.isAccessibilityStickyKeysEnabled(mContext));
    }

    @Test
    @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG})
    public void testKeyGestureToggleSlowKeys() {
        Assert.assertTrue(
                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS));
        Assert.assertTrue(InputSettings.isAccessibilitySlowKeysEnabled(mContext));

        Assert.assertTrue(
                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS));
        Assert.assertFalse(InputSettings.isAccessibilitySlowKeysEnabled(mContext));
    }

    @Test
    @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS})
    public void testKeyGestureToggleMouseKeys() {
        Assert.assertTrue(
                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS));
        Assert.assertTrue(InputSettings.isAccessibilityMouseKeysEnabled(mContext));

        Assert.assertTrue(
                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS));
        Assert.assertFalse(InputSettings.isAccessibilityMouseKeysEnabled(mContext));
    }

    @Test
    @EnableFlags({com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SHORTCUT_CONTROL,
            com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG})
    public void testKeyGestureToggleBounceKeys() {
        Assert.assertTrue(
                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS));
        Assert.assertTrue(InputSettings.isAccessibilityBounceKeysEnabled(mContext));

        Assert.assertTrue(
                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS));
        Assert.assertFalse(InputSettings.isAccessibilityBounceKeysEnabled(mContext));
    }
}
Loading