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

Commit e1dea820 authored by Wenyu Zhang's avatar Wenyu Zhang
Browse files

a11y: Handle voice access keyboard shortcut in A11yManagerService

Use new key gesture handler API to handle voice access keyboard
shortcut.

Change-Id: I034f945c5fa4a0619605cfbbed1a37e0901e5aa4
Bug: 413279228
Test: atest AccessibilityManagerServiceTest
Flag: EXEMPT refactor
parent 32382aa3
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -4819,6 +4819,11 @@
         name is not preinstalled then this shortcut will do nothing. -->
    <string name="config_defaultSelectToSpeakService" translatable="false"></string>

    <!-- The component name, flattened to a string, for the default voice access service to be
     enabled by the accessibility keyboard shortcut. If the service with the specified component
     name is not preinstalled then this shortcut will do nothing. -->
    <string name="config_defaultVoiceAccessService" translatable="false"></string>

    <!-- URI for default Accessibility notification sound when to enable accessibility shortcut. -->
    <string name="config_defaultAccessibilityNotificationSound" translatable="false"></string>

+1 −0
Original line number Diff line number Diff line
@@ -3821,6 +3821,7 @@
  <java-symbol type="string" name="reduce_bright_colors_feature_name" />
  <java-symbol type="string" name="config_defaultAccessibilityService" />
  <java-symbol type="string" name="config_defaultSelectToSpeakService" />
  <java-symbol type="string" name="config_defaultVoiceAccessService" />
  <java-symbol type="string" name="config_defaultAccessibilityNotificationSound" />
  <java-symbol type="string" name="accessibility_shortcut_spoken_feedback" />
  <java-symbol type="array" name="config_trustedAccessibilityServices" />
+38 −9
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static android.view.accessibility.AccessibilityManager.FlashNotificationReason;

import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
@@ -653,10 +654,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        registerBroadcastReceivers();
        new AccessibilityContentObserver(mMainHandler).register(
                mContext.getContentResolver());
        List<Integer> supportedGestures = new ArrayList<>();
        if (enableTalkbackAndMagnifierKeyGestures()) {
            List<Integer> supportedGestures = List.of(
                    KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
                    KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK);
            supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION);
            supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK);
        }
        if (enableVoiceAccessKeyGestures()) {
            supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS);
        }
        if (!supportedGestures.isEmpty()) {
            mInputManager.registerKeyGestureEventHandler(supportedGestures,
                    mKeyGestureEventHandler);
        }
@@ -706,6 +712,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
        return mIsAccessibilityButtonShown;
    }

    private String getTargetNameFromKeyGestureType(int gestureType) {
        return switch (gestureType) {
            case KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK -> mContext.getString(
                    R.string.config_defaultSelectToSpeakService);
            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS -> mContext.getString(
                    R.string.config_defaultVoiceAccessService);
            default -> "";
        };
    }

    @VisibleForTesting
    void handleKeyGestureEvent(KeyGestureEvent event) {
        final boolean complete =
@@ -722,7 +738,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
                targetName = MAGNIFICATION_CONTROLLER_NAME;
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK:
                targetName = mContext.getString(R.string.config_defaultSelectToSpeakService);
            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS:
                targetName = getTargetNameFromKeyGestureType(gestureType);
                if (targetName.isEmpty()) {
                    return;
                }
@@ -4353,12 +4370,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
                        + "userId: %s",
                enable, shortcutType, shortcutTargets, userId));

        if (shortcutType == UserShortcutType.KEY_GESTURE
                && !enableTalkbackAndMagnifierKeyGestures()) {
        if (shortcutType == UserShortcutType.KEY_GESTURE) {
            if(!enableTalkbackAndMagnifierKeyGestures() &&
                    (shortcutTargets.contains(MAGNIFICATION_CONTROLLER_NAME) ||
                            shortcutTargets.contains(mContext.getString(
                                    R.string.config_defaultSelectToSpeakService)))) {
                Slog.w(LOG_TAG,
                    "KEY_GESTURE type shortcuts are disabled by feature flag");
                        "KEY_GESTURE type magnification and select to speak shortcuts are "
                                + "disabled by feature flag");
                return;
            }
            if (!enableVoiceAccessKeyGestures() && shortcutTargets.contains(mContext.getString(
                    R.string.config_defaultVoiceAccessService))) {
                Slog.w(LOG_TAG,
                        "KEY_GESTURE type voice access shortcuts are "
                                + "disabled by feature flag");
                return;
            }
        }

        final String shortcutTypeSettingKey = ShortcutUtils.convertToKey(shortcutType);
        if (shortcutType == UserShortcutType.TRIPLETAP
+30 −0
Original line number Diff line number Diff line
@@ -2261,6 +2261,36 @@ public class AccessibilityManagerServiceTest {
                mA11yms.getCurrentUserIdLocked())).isEmpty();
    }

    @Test
    @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_VOICE_ACCESS_KEY_GESTURES)
    public void handleKeyGestureEvent_activateVoiceAccess_trustedService() {
        setupAccessibilityServiceConnection(FLAG_REQUEST_ACCESSIBILITY_BUTTON);
        mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);

        final AccessibilityServiceInfo trustedService = mockAccessibilityServiceInfo(
                new ComponentName("package_a", "class_a"),
                /* isSystemApp= */ true, /* isAlwaysOnService= */ true);
        AccessibilityUserState userState = mA11yms.getCurrentUserState();
        userState.mInstalledServices.add(trustedService);
        mTestableContext.getOrCreateTestableResources().addOverride(
                R.string.config_defaultVoiceAccessService,
                trustedService.getComponentName().flattenToString());
        mTestableContext.getOrCreateTestableResources().addOverride(
                R.array.config_trustedAccessibilityServices,
                new String[]{trustedService.getComponentName().flattenToString()});

        assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
                mA11yms.getCurrentUserIdLocked())).isEmpty();

        mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType(
                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS).setAction(
                KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());

        assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE,
                mA11yms.getCurrentUserIdLocked())).containsExactly(
                trustedService.getComponentName().flattenToString());
    }

    @Test
    public void displayListReturnsDisplays() {
        mTestDisplayManagerWrapper.mDisplays = createFakeDisplayList(