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

Commit b11b75ac authored by Rhed Jao's avatar Rhed Jao Committed by Android (Google) Code Review
Browse files

Merge "Spoken feedback when a11y shortcut dialog is shown"

parents 946fbaa7 6182af7a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2905,6 +2905,7 @@ package android.accessibilityservice {
    field public static final deprecated int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
    field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
    field public static final int FLAG_REQUEST_FINGERPRINT_GESTURES = 512; // 0x200
    field public static final int FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK = 1024; // 0x400
    field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
    field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40
    field public int eventTypes;
+9 −0
Original line number Diff line number Diff line
@@ -314,6 +314,12 @@ public class AccessibilityServiceInfo implements Parcelable {
     */
    public static final int FLAG_REQUEST_FINGERPRINT_GESTURES = 0x00000200;

    /**
     * This flag requests that accessibility shortcut warning dialog has spoken feedback when
     * dialog is shown.
     */
    public static final int FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK = 0x00000400;

    /** {@hide} */
    public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;

@@ -414,6 +420,7 @@ public class AccessibilityServiceInfo implements Parcelable {
     * @see #FLAG_RETRIEVE_INTERACTIVE_WINDOWS
     * @see #FLAG_ENABLE_ACCESSIBILITY_VOLUME
     * @see #FLAG_REQUEST_ACCESSIBILITY_BUTTON
     * @see #FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK
     */
    public int flags;

@@ -1050,6 +1057,8 @@ public class AccessibilityServiceInfo implements Parcelable {
                return "FLAG_REQUEST_ACCESSIBILITY_BUTTON";
            case FLAG_REQUEST_FINGERPRINT_GESTURES:
                return "FLAG_REQUEST_FINGERPRINT_GESTURES";
            case FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK:
                return "FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK";
            default:
                return null;
        }
+129 −21
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.internal.accessibility;

import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;

import static com.android.internal.util.ArrayUtils.convertToLongArray;

import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.ActivityManager;
import android.app.ActivityThread;
@@ -34,23 +38,23 @@ import android.os.Handler;
import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.speech.tts.Voice;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;

import android.widget.Toast;

import com.android.internal.R;
import com.android.internal.util.function.pooled.PooledLambda;

import java.util.Collections;
import java.util.Locale;
import java.util.Map;

import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;

import static com.android.internal.util.ArrayUtils.convertToLongArray;

/**
 * Class to help manage the accessibility shortcut
 */
@@ -70,6 +74,7 @@ public class AccessibilityShortcutController {
    private static Map<ComponentName, ToggleableFrameworkFeatureInfo> sFrameworkShortcutFeaturesMap;

    private final Context mContext;
    private final Handler mHandler;
    private AlertDialog mAlertDialog;
    private boolean mIsShortcutEnabled;
    private boolean mEnabledOnLockScreen;
@@ -123,6 +128,7 @@ public class AccessibilityShortcutController {

    public AccessibilityShortcutController(Context context, Handler handler, int initialUserId) {
        mContext = context;
        mHandler = handler;
        mUserId = initialUserId;

        // Keep track of state of shortcut settings
@@ -189,22 +195,6 @@ public class AccessibilityShortcutController {
        final int userId = ActivityManager.getCurrentUser();
        final int dialogAlreadyShown = Settings.Secure.getIntForUser(
                cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId);
        // Use USAGE_ASSISTANCE_ACCESSIBILITY for TVs to ensure that TVs play the ringtone as they
        // have less ways of providing feedback like vibration.
        final int audioAttributesUsage = hasFeatureLeanback()
                ? AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
                : AudioAttributes.USAGE_NOTIFICATION_EVENT;

        // Play a notification tone
        final Ringtone tone =
                RingtoneManager.getRingtone(mContext, Settings.System.DEFAULT_NOTIFICATION_URI);
        if (tone != null) {
            tone.setAudioAttributes(new AudioAttributes.Builder()
                .setUsage(audioAttributesUsage)
                .build());
            tone.play();
        }

        // Play a notification vibration
        Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
        if ((vibrator != null) && vibrator.hasVibrator()) {
@@ -223,6 +213,9 @@ public class AccessibilityShortcutController {
            if (mAlertDialog == null) {
                return;
            }
            if (!performTtsPrompt(mAlertDialog)) {
                playNotificationTone();
            }
            Window w = mAlertDialog.getWindow();
            WindowManager.LayoutParams attr = w.getAttributes();
            attr.type = TYPE_KEYGUARD_DIALOG;
@@ -231,6 +224,7 @@ public class AccessibilityShortcutController {
            Settings.Secure.putIntForUser(
                    cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1, userId);
        } else {
            playNotificationTone();
            if (mAlertDialog != null) {
                mAlertDialog.dismiss();
                mAlertDialog = null;
@@ -344,6 +338,102 @@ public class AccessibilityShortcutController {
        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
    }

    private void playNotificationTone() {
        // Use USAGE_ASSISTANCE_ACCESSIBILITY for TVs to ensure that TVs play the ringtone as they
        // have less ways of providing feedback like vibration.
        final int audioAttributesUsage = hasFeatureLeanback()
                ? AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
                : AudioAttributes.USAGE_NOTIFICATION_EVENT;

        // Play a notification tone
        final Ringtone tone = mFrameworkObjectProvider.getRingtone(mContext,
                Settings.System.DEFAULT_NOTIFICATION_URI);
        if (tone != null) {
            tone.setAudioAttributes(new AudioAttributes.Builder()
                    .setUsage(audioAttributesUsage)
                    .build());
            tone.play();
        }
    }

    private boolean performTtsPrompt(AlertDialog alertDialog) {
        final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
        if (serviceInfo == null) {
            return false;
        }
        if ((serviceInfo.flags & AccessibilityServiceInfo
                .FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK) == 0) {
            return false;
        }
        final TtsPrompt tts = new TtsPrompt();
        alertDialog.setOnDismissListener(dialog -> tts.dismiss());
        return true;
    }

    /**
     * Class to wrap TextToSpeech for shortcut dialog spoken feedback.
     */
    private class TtsPrompt implements TextToSpeech.OnInitListener {
        private final CharSequence mText;
        private boolean mDismiss;
        private TextToSpeech mTts;

        TtsPrompt() {
            mText = mContext.getString(R.string.accessibility_shortcut_spoken_feedback);
            mTts = mFrameworkObjectProvider.getTextToSpeech(mContext, this);
        }

        /**
         * Releases the resources used by the TextToSpeech, when dialog dismiss.
         */
        public void dismiss() {
            mDismiss = true;
            mHandler.sendMessage(PooledLambda.obtainMessage(TextToSpeech::shutdown, mTts));
        }

        @Override
        public void onInit(int status) {
            if (status != TextToSpeech.SUCCESS) {
                Slog.d(TAG, "Tts init fail, status=" + Integer.toString(status));
                playNotificationTone();
                return;
            }
            mHandler.sendMessage(PooledLambda.obtainMessage(TtsPrompt::play, this));
        }

        private void play() {
            if (mDismiss) {
                return;
            }
            int status = TextToSpeech.ERROR;
            if (setLanguage(Locale.getDefault())) {
                status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null);
            }
            if (status != TextToSpeech.SUCCESS) {
                Slog.d(TAG, "Tts play fail");
                playNotificationTone();
            }
        }

        /**
         * @return false if tts language is not available
         */
        private boolean setLanguage(final Locale locale) {
            int status = mTts.isLanguageAvailable(locale);
            if (status == TextToSpeech.LANG_MISSING_DATA
                    || status == TextToSpeech.LANG_NOT_SUPPORTED) {
                return false;
            }
            mTts.setLanguage(locale);
            Voice voice = mTts.getVoice();
            if (voice == null || (voice.getFeatures() != null && voice.getFeatures()
                    .contains(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED))) {
                return false;
            }
            return true;
        }
    }

    /**
     * Immutable class to hold info about framework features that can be controlled by shortcut
     */
@@ -406,5 +496,23 @@ public class AccessibilityShortcutController {
        public Context getSystemUiContext() {
            return ActivityThread.currentActivityThread().getSystemUiContext();
        }

        /**
         * @param ctx A context for TextToSpeech
         * @param listener TextToSpeech initialization callback
         * @return TextToSpeech instance
         */
        public TextToSpeech getTextToSpeech(Context ctx, TextToSpeech.OnInitListener listener) {
            return new TextToSpeech(ctx, listener);
        }

        /**
         * @param ctx context for ringtone
         * @param uri ringtone uri
         * @return Ringtone instance
         */
        public Ringtone getRingtone(Context ctx, Uri uri) {
            return RingtoneManager.getRingtone(ctx, uri);
        }
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -3606,6 +3606,8 @@
            <flag name="flagRequestAccessibilityButton" value="0x00000100" />
            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_FINGERPRINT_GESTURES}. -->
            <flag name="flagRequestFingerprintGestures" value="0x00000200" />
            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK}. -->
            <flag name="flagRequestShortcutWarningDialogSpokenFeedback" value="0x00000400" />
        </attr>
        <!-- Component name of an activity that allows the user to modify
             the settings for this service. This setting cannot be changed at runtime. -->
+3 −0
Original line number Diff line number Diff line
@@ -4334,6 +4334,9 @@
    <string name="accessibility_shortcut_disabling_service">Accessibility Shortcut turned
        <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> off</string>

    <!-- Text spoken when accessibility shortcut warning dialog is shown. [CHAR LIMIT=none] -->
    <string name="accessibility_shortcut_spoken_feedback">Use Accessibility Shortcut again to start the current accessibility feature</string>

    <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
    <string name="accessibility_button_prompt_text">Choose a feature to use when you tap the Accessibility button:</string>

Loading