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

Commit c808b228 authored by Wenyu Zhang's avatar Wenyu Zhang Committed by Android (Google) Code Review
Browse files

Merge changes from topic "refactor-voice-access-shortcut" into main

* changes:
  a11y: Handle voice access keyboard shortcut in A11yManagerService
  a11y: Remove VoiceAccessShortcutController from PhoneWindowManager
parents e3b2dbbf e1dea820
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -4817,6 +4817,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
+0 −16
Original line number Diff line number Diff line
@@ -83,7 +83,6 @@ import static android.view.contentprotection.flags.Flags.createAccessibilityOver

import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
@@ -488,8 +487,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {

    private TalkbackShortcutController mTalkbackShortcutController;

    private VoiceAccessShortcutController mVoiceAccessShortcutController;

    private WindowWakeUpPolicy mWindowWakeUpPolicy;

    boolean mSafeMode;
@@ -2263,10 +2260,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            return new TalkbackShortcutController(mContext);
        }

        VoiceAccessShortcutController getVoiceAccessShortcutController() {
            return new VoiceAccessShortcutController(mContext);
        }

        WindowWakeUpPolicy getWindowWakeUpPolicy() {
            return new WindowWakeUpPolicy(mContext);
        }
@@ -2504,7 +2497,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                com.android.internal.R.integer.config_keyguardDrawnTimeout);
        mKeyguardDelegate = injector.getKeyguardServiceDelegate();
        mTalkbackShortcutController = injector.getTalkbackShortcutController();
        mVoiceAccessShortcutController = injector.getVoiceAccessShortcutController();
        mWindowWakeUpPolicy = injector.getWindowWakeUpPolicy();
        initSingleKeyGestureRules(injector.getLooper());
        initKeyGestures();
@@ -3415,9 +3407,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        if (enableTalkbackAndMagnifierKeyGestures()) {
            supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK);
        }
        if (enableVoiceAccessKeyGestures()) {
            supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS);
        }
        if (!com.android.window.flags.Flags.enableKeyGestureHandlerForRecents()) {
            // When enableKeyGestureHandlerForRecents is enabled, the event is handled in the
            // recents app.
@@ -3622,11 +3611,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                            TalkbackShortcutController.ShortcutSource.KEYBOARD);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS:
                if (complete) {
                    mVoiceAccessShortcutController.toggleVoiceAccess(mCurrentUserId);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
                AppLaunchData data = event.getAppLaunchData();
                if (complete && canLaunchApp && data != null) {
+0 −62
Original line number Diff line number Diff line
/*
 * Copyright 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.policy;

import android.content.ComponentName;
import android.content.Context;
import android.util.Slog;

import com.android.internal.accessibility.util.AccessibilityUtils;

import androidx.annotation.VisibleForTesting;

import java.util.Set;

/** This class controls voice access shortcut related operations such as toggling, querying. */
class VoiceAccessShortcutController {
    private static final String TAG = VoiceAccessShortcutController.class.getSimpleName();
    private static final String VOICE_ACCESS_LABEL = "Voice Access";

    private final Context mContext;

    VoiceAccessShortcutController(Context context) {
        mContext = context;
    }

    /**
     * A function that toggles voice access service.
     *
     * @return whether voice access is enabled after being toggled.
     */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    boolean toggleVoiceAccess(int userId) {
        final Set<ComponentName> enabledServices =
                AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId);
        ComponentName componentName =
                AccessibilityUtils.getInstalledAccessibilityServiceComponentNameByLabel(
                        mContext, VOICE_ACCESS_LABEL);
        if (componentName == null) {
            Slog.e(TAG, "Toggle Voice Access failed due to componentName being null");
            return false;
        }

        boolean newState = !enabledServices.contains(componentName);
        AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, newState, userId);

        return newState;
    }
}
Loading