diff options
7 files changed, 459 insertions, 316 deletions
diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java new file mode 100644 index 000000000000..1daa5052a565 --- /dev/null +++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020 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.internal.accessibility.common; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Collection of common constants for accessibility shortcut. + */ +public final class ShortcutConstants { + private ShortcutConstants() {} + + public static final char SERVICES_SEPARATOR = ':'; + public static final float DISABLED_ALPHA = 0.5f; + public static final float ENABLED_ALPHA = 1.0f; + + /** + * Annotation for different user shortcut type UI type. + * + * {@code DEFAULT} for displaying default value. + * {@code SOFTWARE} for displaying specifying the accessibility services or features which + * choose accessibility button in the navigation bar as preferred shortcut. + * {@code HARDWARE} for displaying specifying the accessibility services or features which + * choose accessibility shortcut as preferred shortcut. + * {@code TRIPLETAP} for displaying specifying magnification to be toggled via quickly + * tapping screen 3 times as preferred shortcut. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + UserShortcutType.DEFAULT, + UserShortcutType.SOFTWARE, + UserShortcutType.HARDWARE, + UserShortcutType.TRIPLETAP, + }) + public @interface UserShortcutType { + int DEFAULT = 0; + int SOFTWARE = 1; // 1 << 0 + int HARDWARE = 2; // 1 << 1 + int TRIPLETAP = 4; // 1 << 2 + } + + /** + * Annotation for different accessibilityService fragment UI type. + * + * {@code LEGACY} for displaying appearance aligned with sdk version Q accessibility service + * page, but only hardware shortcut allowed and under service in version Q or early. + * {@code INVISIBLE} for displaying appearance without switch bar. + * {@code INTUITIVE} for displaying appearance with version R accessibility design. + * {@code BOUNCE} for displaying appearance with pop-up action. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + AccessibilityServiceFragmentType.LEGACY, + AccessibilityServiceFragmentType.INVISIBLE, + AccessibilityServiceFragmentType.INTUITIVE, + AccessibilityServiceFragmentType.BOUNCE, + }) + public @interface AccessibilityServiceFragmentType { + int LEGACY = 0; + int INVISIBLE = 1; + int INTUITIVE = 2; + int BOUNCE = 3; + } + + /** + * Annotation for different shortcut menu mode. + * + * {@code LAUNCH} for clicking list item to trigger the service callback. + * {@code EDIT} for clicking list item and save button to disable the service. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + ShortcutMenuMode.LAUNCH, + ShortcutMenuMode.EDIT, + }) + public @interface ShortcutMenuMode { + int LAUNCH = 0; + int EDIT = 1; + } + + /** + * Annotation for align the element index of white listing feature + * {@code WHITE_LISTING_FEATURES}. + * + * {@code COMPONENT_ID} is to get the service component name. + * {@code LABEL_ID} is to get the service label text. + * {@code ICON_ID} is to get the service icon. + * {@code FRAGMENT_TYPE} is to get the service fragment type. + * {@code SETTINGS_KEY} is to get the service settings key. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + WhiteListingFeatureElementIndex.COMPONENT_ID, + WhiteListingFeatureElementIndex.LABEL_ID, + WhiteListingFeatureElementIndex.ICON_ID, + WhiteListingFeatureElementIndex.FRAGMENT_TYPE, + WhiteListingFeatureElementIndex.SETTINGS_KEY, + }) + public @interface WhiteListingFeatureElementIndex { + int COMPONENT_ID = 0; + int LABEL_ID = 1; + int ICON_ID = 2; + int FRAGMENT_TYPE = 3; + int SETTINGS_KEY = 4; + } +} diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java new file mode 100644 index 000000000000..d0ead5e3b6ce --- /dev/null +++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 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.internal.accessibility.util; +import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityServiceFragmentType; +import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.ComponentName; +import android.content.Context; +import android.os.Build; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArraySet; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Collection of utilities for accessibility service. + */ +public final class AccessibilityUtils { + private AccessibilityUtils() {} + + /** + * Returns the set of enabled accessibility services for userId. If there are no + * services, it returns the unmodifiable {@link Collections#emptySet()}. + */ + public static Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) { + final String enabledServicesSetting = Settings.Secure.getStringForUser( + context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + userId); + if (TextUtils.isEmpty(enabledServicesSetting)) { + return Collections.emptySet(); + } + + final Set<ComponentName> enabledServices = new HashSet<>(); + final TextUtils.StringSplitter colonSplitter = + new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR); + colonSplitter.setString(enabledServicesSetting); + + for (String componentNameString : colonSplitter) { + final ComponentName enabledService = ComponentName.unflattenFromString( + componentNameString); + if (enabledService != null) { + enabledServices.add(enabledService); + } + } + + return enabledServices; + } + + /** + * Changes an accessibility component's state. + */ + public static void setAccessibilityServiceState(Context context, ComponentName componentName, + boolean enabled) { + setAccessibilityServiceState(context, componentName, enabled, UserHandle.myUserId()); + } + + /** + * Changes an accessibility component's state for {@param userId}. + */ + public static void setAccessibilityServiceState(Context context, ComponentName componentName, + boolean enabled, int userId) { + Set<ComponentName> enabledServices = getEnabledServicesFromSettings( + context, userId); + + if (enabledServices.isEmpty()) { + enabledServices = new ArraySet<>(/* capacity= */ 1); + } + + if (enabled) { + enabledServices.add(componentName); + } else { + enabledServices.remove(componentName); + } + + final StringBuilder enabledServicesBuilder = new StringBuilder(); + for (ComponentName enabledService : enabledServices) { + enabledServicesBuilder.append(enabledService.flattenToString()); + enabledServicesBuilder.append( + SERVICES_SEPARATOR); + } + + final int enabledServicesBuilderLength = enabledServicesBuilder.length(); + if (enabledServicesBuilderLength > 0) { + enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1); + } + + Settings.Secure.putStringForUser(context.getContentResolver(), + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + enabledServicesBuilder.toString(), userId); + } + + /** + * Gets the corresponding fragment type of a given accessibility service. + * + * @param accessibilityServiceInfo The accessibilityService's info. + * @return int from {@link AccessibilityServiceFragmentType}. + */ + public static @AccessibilityServiceFragmentType int getAccessibilityServiceFragmentType( + AccessibilityServiceInfo accessibilityServiceInfo) { + final int targetSdk = accessibilityServiceInfo.getResolveInfo() + .serviceInfo.applicationInfo.targetSdkVersion; + final boolean requestA11yButton = (accessibilityServiceInfo.flags + & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; + + if (targetSdk <= Build.VERSION_CODES.Q) { + return AccessibilityServiceFragmentType.LEGACY; + } + return requestA11yButton + ? AccessibilityServiceFragmentType.INVISIBLE + : AccessibilityServiceFragmentType.INTUITIVE; + } +} diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java new file mode 100644 index 000000000000..7df712fbcf7b --- /dev/null +++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2020 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.internal.accessibility.util; +import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; +import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; + +import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; + +import android.annotation.NonNull; +import android.content.Context; +import android.provider.Settings; +import android.text.TextUtils; +import android.view.accessibility.AccessibilityManager.ShortcutType; + +import java.util.StringJoiner; + +/** + * Collection of utilities for accessibility shortcut. + */ +public final class ShortcutUtils { + private ShortcutUtils() {} + + private static final TextUtils.SimpleStringSplitter sStringColonSplitter = + new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR); + + /** + * Opts out component name into colon-separated {@code shortcutType} key's string in Settings. + * + * @param context The current context. + * @param shortcutType The preferred shortcut type user selected. + * @param componentId The component id that need to be opted out from Settings. + */ + public static void optOutValueFromSettings( + Context context, @UserShortcutType int shortcutType, String componentId) { + final StringJoiner joiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR)); + final String targetsKey = convertToKey(shortcutType); + final String targetsValue = Settings.Secure.getString(context.getContentResolver(), + targetsKey); + + if (TextUtils.isEmpty(targetsValue)) { + return; + } + + sStringColonSplitter.setString(targetsValue); + while (sStringColonSplitter.hasNext()) { + final String id = sStringColonSplitter.next(); + if (TextUtils.isEmpty(id) || componentId.equals(id)) { + continue; + } + joiner.add(id); + } + + Settings.Secure.putString(context.getContentResolver(), targetsKey, joiner.toString()); + } + + /** + * Returns if component name existed in one of {@code shortcutTypes} string in Settings. + * + * @param context The current context. + * @param shortcutTypes A combination of {@link UserShortcutType}. + * @param componentId The component name that need to be checked existed in Settings. + * @return {@code true} if componentName existed in Settings. + */ + public static boolean hasValuesInSettings(Context context, int shortcutTypes, + @NonNull String componentId) { + boolean exist = false; + if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) { + exist = hasValueInSettings(context, UserShortcutType.SOFTWARE, componentId); + } + if (((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE)) { + exist |= hasValueInSettings(context, UserShortcutType.HARDWARE, componentId); + } + return exist; + } + + + /** + * Returns if component name existed in Settings. + * + * @param context The current context. + * @param shortcutType The preferred shortcut type user selected. + * @param componentId The component id that need to be checked existed in Settings. + * @return {@code true} if componentName existed in Settings. + */ + public static boolean hasValueInSettings(Context context, @UserShortcutType int shortcutType, + @NonNull String componentId) { + final String targetKey = convertToKey(shortcutType); + final String targetString = Settings.Secure.getString(context.getContentResolver(), + targetKey); + + if (TextUtils.isEmpty(targetString)) { + return false; + } + + sStringColonSplitter.setString(targetString); + while (sStringColonSplitter.hasNext()) { + final String id = sStringColonSplitter.next(); + if (componentId.equals(id)) { + return true; + } + } + + return false; + } + + /** + * Converts {@link UserShortcutType} to key in Settings. + * + * @param type The shortcut type. + * @return Mapping key in Settings. + */ + public static String convertToKey(@UserShortcutType int type) { + switch (type) { + case UserShortcutType.SOFTWARE: + return Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT; + case UserShortcutType.HARDWARE: + return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; + case UserShortcutType.TRIPLETAP: + return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED; + default: + throw new IllegalArgumentException( + "Unsupported user shortcut type: " + type); + } + } + + /** + * Converts {@link ShortcutType} to {@link UserShortcutType}. + * + * @param type The shortcut type. + * @return {@link UserShortcutType}. + */ + public static @UserShortcutType int convertToUserType(@ShortcutType int type) { + switch (type) { + case ACCESSIBILITY_BUTTON: + return UserShortcutType.SOFTWARE; + case ACCESSIBILITY_SHORTCUT_KEY: + return UserShortcutType.HARDWARE; + default: + throw new IllegalArgumentException( + "Unsupported shortcut type:" + type); + } + } +} diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index 86d2ed6c15c0..852deb208ea6 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -22,16 +22,25 @@ import static android.view.accessibility.AccessibilityManager.ShortcutType; import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; -import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.COMPONENT_ID; -import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.FRAGMENT_TYPE; -import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.ICON_ID; -import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.LABEL_ID; -import static com.android.internal.app.AccessibilityButtonChooserActivity.WhiteListingFeatureElementIndex.SETTINGS_KEY; +import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityServiceFragmentType; +import static com.android.internal.accessibility.common.ShortcutConstants.DISABLED_ALPHA; +import static com.android.internal.accessibility.common.ShortcutConstants.ENABLED_ALPHA; +import static com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; +import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.COMPONENT_ID; +import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.FRAGMENT_TYPE; +import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.ICON_ID; +import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.LABEL_ID; +import static com.android.internal.accessibility.common.ShortcutConstants.WhiteListingFeatureElementIndex.SETTINGS_KEY; +import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType; +import static com.android.internal.accessibility.util.AccessibilityUtils.setAccessibilityServiceState; +import static com.android.internal.accessibility.util.ShortcutUtils.convertToUserType; +import static com.android.internal.accessibility.util.ShortcutUtils.hasValuesInSettings; +import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings; import static com.android.internal.util.Preconditions.checkArgument; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityShortcutInfo; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; @@ -44,12 +53,8 @@ import android.content.res.TypedArray; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Bundle; -import android.os.UserHandle; import android.provider.Settings; -import android.text.TextUtils; -import android.util.ArraySet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -64,24 +69,14 @@ import android.widget.TextView; import com.android.internal.R; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.StringJoiner; /** * Activity used to display and persist a service or feature target for the Accessibility button. */ public class AccessibilityButtonChooserActivity extends Activity { - private static final char SERVICES_SEPARATOR = ':'; - private static final float DISABLED_ALPHA = 0.5f; - private static final float ENABLED_ALPHA = 1.0f; - private static final TextUtils.SimpleStringSplitter sStringColonSplitter = - new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR); @ShortcutType private int mShortcutType; @UserShortcutType @@ -90,97 +85,6 @@ public class AccessibilityButtonChooserActivity extends Activity { private AlertDialog mAlertDialog; private TargetAdapter mTargetAdapter; - /** - * Annotation for different user shortcut type UI type. - * - * {@code DEFAULT} for displaying default value. - * {@code SOFTWARE} for displaying specifying the accessibility services or features which - * choose accessibility button in the navigation bar as preferred shortcut. - * {@code HARDWARE} for displaying specifying the accessibility services or features which - * choose accessibility shortcut as preferred shortcut. - * {@code TRIPLETAP} for displaying specifying magnification to be toggled via quickly - * tapping screen 3 times as preferred shortcut. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - UserShortcutType.DEFAULT, - UserShortcutType.SOFTWARE, - UserShortcutType.HARDWARE, - UserShortcutType.TRIPLETAP, - }) - /** Denotes the user shortcut type. */ - private @interface UserShortcutType { - int DEFAULT = 0; - int SOFTWARE = 1; // 1 << 0 - int HARDWARE = 2; // 1 << 1 - int TRIPLETAP = 4; // 1 << 2 - } - - /** - * Annotation for different accessibilityService fragment UI type. - * - * {@code LEGACY} for displaying appearance aligned with sdk version Q accessibility service - * page, but only hardware shortcut allowed and under service in version Q or early. - * {@code INVISIBLE} for displaying appearance without switch bar. - * {@code INTUITIVE} for displaying appearance with version R accessibility design. - * {@code BOUNCE} for displaying appearance with pop-up action. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - AccessibilityServiceFragmentType.LEGACY, - AccessibilityServiceFragmentType.INVISIBLE, - AccessibilityServiceFragmentType.INTUITIVE, - AccessibilityServiceFragmentType.BOUNCE, - }) - private @interface AccessibilityServiceFragmentType { - int LEGACY = 0; - int INVISIBLE = 1; - int INTUITIVE = 2; - int BOUNCE = 3; - } - - /** - * Annotation for different shortcut menu mode. - * - * {@code LAUNCH} for clicking list item to trigger the service callback. - * {@code EDIT} for clicking list item and save button to disable the service. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - ShortcutMenuMode.LAUNCH, - ShortcutMenuMode.EDIT, - }) - private @interface ShortcutMenuMode { - int LAUNCH = 0; - int EDIT = 1; - } - - /** - * Annotation for align the element index of white listing feature - * {@code WHITE_LISTING_FEATURES}. - * - * {@code COMPONENT_ID} is to get the service component name. - * {@code LABEL_ID} is to get the service label text. - * {@code ICON_ID} is to get the service icon. - * {@code FRAGMENT_TYPE} is to get the service fragment type. - * {@code SETTINGS_KEY} is to get the service settings key. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - WhiteListingFeatureElementIndex.COMPONENT_ID, - WhiteListingFeatureElementIndex.LABEL_ID, - WhiteListingFeatureElementIndex.ICON_ID, - WhiteListingFeatureElementIndex.FRAGMENT_TYPE, - WhiteListingFeatureElementIndex.SETTINGS_KEY, - }) - @interface WhiteListingFeatureElementIndex { - int COMPONENT_ID = 0; - int LABEL_ID = 1; - int ICON_ID = 2; - int FRAGMENT_TYPE = 3; - int SETTINGS_KEY = 4; - } - private static final String[][] WHITE_LISTING_FEATURES = { { COLOR_INVERSION_COMPONENT_NAME.flattenToString(), @@ -224,8 +128,11 @@ public class AccessibilityButtonChooserActivity extends Activity { mTargets.addAll(getServiceTargets(this, mShortcutType)); + final String selectDialogTitle = + getString(R.string.accessibility_select_shortcut_menu_title); mTargetAdapter = new TargetAdapter(mTargets, mShortcutType); mAlertDialog = new AlertDialog.Builder(this) + .setTitle(selectDialogTitle) .setAdapter(mTargetAdapter, /* listener= */ null) .setPositiveButton( getString(R.string.edit_accessibility_shortcut_menu_button), @@ -242,27 +149,6 @@ public class AccessibilityButtonChooserActivity extends Activity { super.onDestroy(); } - /** - * Gets the corresponding fragment type of a given accessibility service. - * - * @param accessibilityServiceInfo The accessibilityService's info. - * @return int from {@link AccessibilityServiceFragmentType}. - */ - private static @AccessibilityServiceFragmentType int getAccessibilityServiceFragmentType( - AccessibilityServiceInfo accessibilityServiceInfo) { - final int targetSdk = accessibilityServiceInfo.getResolveInfo() - .serviceInfo.applicationInfo.targetSdkVersion; - final boolean requestA11yButton = (accessibilityServiceInfo.flags - & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; - - if (targetSdk <= Build.VERSION_CODES.Q) { - return AccessibilityServiceFragmentType.LEGACY; - } - return requestA11yButton - ? AccessibilityServiceFragmentType.INVISIBLE - : AccessibilityServiceFragmentType.INTUITIVE; - } - private static List<AccessibilityButtonTarget> getServiceTargets(@NonNull Context context, @ShortcutType int shortcutType) { final List<AccessibilityButtonTarget> targets = new ArrayList<>(); @@ -761,193 +647,17 @@ public class AccessibilityButtonChooserActivity extends Activity { private void updateDialogListeners() { final boolean isEditMenuMode = (mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT); + final int selectDialogTitleId = R.string.accessibility_select_shortcut_menu_title; + final int editDialogTitleId = + (mShortcutType == ACCESSIBILITY_BUTTON) + ? R.string.accessibility_edit_shortcut_menu_button_title + : R.string.accessibility_edit_shortcut_menu_volume_title; + + mAlertDialog.setTitle(getString(isEditMenuMode ? editDialogTitleId : selectDialogTitleId)); mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener( isEditMenuMode ? view -> onCancelButtonClicked() : view -> onEditButtonClicked()); mAlertDialog.getListView().setOnItemClickListener( isEditMenuMode ? this::onTargetDeleted : this::onTargetSelected); } - - /** - * @return the set of enabled accessibility services for {@param userId}. If there are no - * services, it returns the unmodifiable {@link Collections#emptySet()}. - */ - private Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) { - final String enabledServicesSetting = Settings.Secure.getStringForUser( - context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, - userId); - if (TextUtils.isEmpty(enabledServicesSetting)) { - return Collections.emptySet(); - } - - final Set<ComponentName> enabledServices = new HashSet<>(); - final TextUtils.StringSplitter colonSplitter = - new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR); - colonSplitter.setString(enabledServicesSetting); - - for (String componentNameString : colonSplitter) { - final ComponentName enabledService = ComponentName.unflattenFromString( - componentNameString); - if (enabledService != null) { - enabledServices.add(enabledService); - } - } - - return enabledServices; - } - - /** - * Changes an accessibility component's state. - */ - private void setAccessibilityServiceState(Context context, ComponentName componentName, - boolean enabled) { - setAccessibilityServiceState(context, componentName, enabled, UserHandle.myUserId()); - } - - /** - * Changes an accessibility component's state for {@param userId}. - */ - private void setAccessibilityServiceState(Context context, ComponentName componentName, - boolean enabled, int userId) { - Set<ComponentName> enabledServices = getEnabledServicesFromSettings( - context, userId); - - if (enabledServices.isEmpty()) { - enabledServices = new ArraySet<>(/* capacity= */ 1); - } - - if (enabled) { - enabledServices.add(componentName); - } else { - enabledServices.remove(componentName); - } - - final StringBuilder enabledServicesBuilder = new StringBuilder(); - for (ComponentName enabledService : enabledServices) { - enabledServicesBuilder.append(enabledService.flattenToString()); - enabledServicesBuilder.append( - SERVICES_SEPARATOR); - } - - final int enabledServicesBuilderLength = enabledServicesBuilder.length(); - if (enabledServicesBuilderLength > 0) { - enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1); - } - - Settings.Secure.putStringForUser(context.getContentResolver(), - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, - enabledServicesBuilder.toString(), userId); - } - - /** - * Opts out component name into colon-separated {@code shortcutType} key's string in Settings. - * - * @param context The current context. - * @param shortcutType The preferred shortcut type user selected. - * @param componentId The component id that need to be opted out from Settings. - */ - private void optOutValueFromSettings( - Context context, @UserShortcutType int shortcutType, String componentId) { - final StringJoiner joiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR)); - final String targetsKey = convertToKey(shortcutType); - final String targetsValue = Settings.Secure.getString(context.getContentResolver(), - targetsKey); - - if (TextUtils.isEmpty(targetsValue)) { - return; - } - - sStringColonSplitter.setString(targetsValue); - while (sStringColonSplitter.hasNext()) { - final String id = sStringColonSplitter.next(); - if (TextUtils.isEmpty(id) || componentId.equals(id)) { - continue; - } - joiner.add(id); - } - - Settings.Secure.putString(context.getContentResolver(), targetsKey, joiner.toString()); - } - - /** - * Returns if component name existed in one of {@code shortcutTypes} string in Settings. - * - * @param context The current context. - * @param shortcutTypes A combination of {@link UserShortcutType}. - * @param componentId The component name that need to be checked existed in Settings. - * @return {@code true} if componentName existed in Settings. - */ - private boolean hasValuesInSettings(Context context, int shortcutTypes, - @NonNull String componentId) { - boolean exist = false; - if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) { - exist = hasValueInSettings(context, UserShortcutType.SOFTWARE, componentId); - } - if (((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE)) { - exist |= hasValueInSettings(context, UserShortcutType.HARDWARE, componentId); - } - return exist; - } - - - /** - * Returns if component name existed in Settings. - * - * @param context The current context. - * @param shortcutType The preferred shortcut type user selected. - * @param componentId The component id that need to be checked existed in Settings. - * @return {@code true} if componentName existed in Settings. - */ - private boolean hasValueInSettings(Context context, @UserShortcutType int shortcutType, - @NonNull String componentId) { - final String targetKey = convertToKey(shortcutType); - final String targetString = Settings.Secure.getString(context.getContentResolver(), - targetKey); - - if (TextUtils.isEmpty(targetString)) { - return false; - } - - sStringColonSplitter.setString(targetString); - while (sStringColonSplitter.hasNext()) { - final String id = sStringColonSplitter.next(); - if (componentId.equals(id)) { - return true; - } - } - - return false; - } - - /** - * Converts {@link UserShortcutType} to key in Settings. - * - * @param type The shortcut type. - * @return Mapping key in Settings. - */ - private String convertToKey(@UserShortcutType int type) { - switch (type) { - case UserShortcutType.SOFTWARE: - return Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT; - case UserShortcutType.HARDWARE: - return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; - case UserShortcutType.TRIPLETAP: - return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED; - default: - throw new IllegalArgumentException( - "Unsupported user shortcut type: " + type); - } - } - - private static @UserShortcutType int convertToUserType(@ShortcutType int type) { - switch (type) { - case ACCESSIBILITY_BUTTON: - return UserShortcutType.SOFTWARE; - case ACCESSIBILITY_SHORTCUT_KEY: - return UserShortcutType.HARDWARE; - default: - throw new IllegalArgumentException( - "Unsupported shortcut type:" + type); - } - } } diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml index d19e313055ae..c01766b2c748 100644 --- a/core/res/res/layout/accessibility_button_chooser_item.xml +++ b/core/res/res/layout/accessibility_button_chooser_item.xml @@ -38,7 +38,9 @@ android:layout_height="wrap_content" android:layout_marginStart="14dp" android:layout_weight="1" - android:textColor="?attr/textColorPrimary"/> + android:textSize="20sp" + android:textColor="?attr/textColorPrimary" + android:fontFamily="sans-serif-medium"/> <FrameLayout android:id="@+id/accessibility_button_target_item_container" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5f390b26ea5d..9bf19dd591a7 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4399,6 +4399,21 @@ accessibility feature. </string> + <!-- Title for accessibility select shortcut menu dialog. [CHAR LIMIT=100] --> + <string name="accessibility_select_shortcut_menu_title">Tap the accessibility app you want to use</string> + + <!-- Title for accessibility edit shortcut selection menu dialog, and dialog is triggered + from accessibility button. [CHAR LIMIT=100] --> + <string name="accessibility_edit_shortcut_menu_button_title">Choose apps you want to use with + accessibility button + </string> + + <!-- Title for accessibility edit shortcut selection menu dialog, and dialog is triggered + from volume key shortcut. [CHAR LIMIT=100] --> + <string name="accessibility_edit_shortcut_menu_volume_title">Choose apps you want to use with + volume key shortcut + </string> + <!-- Text in button that edit the accessibility shortcut menu, user can delete any service item in the menu list. [CHAR LIMIT=100] --> <string name="edit_accessibility_shortcut_menu_button">Edit shortcuts</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1570f1d8232b..536a55474cf2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3239,6 +3239,10 @@ <java-symbol type="string" name="config_defaultAccessibilityService" /> <java-symbol type="string" name="accessibility_shortcut_spoken_feedback" /> + <java-symbol type="string" name="accessibility_select_shortcut_menu_title" /> + <java-symbol type="string" name="accessibility_edit_shortcut_menu_button_title" /> + <java-symbol type="string" name="accessibility_edit_shortcut_menu_volume_title" /> + <!-- Accessibility Button --> <java-symbol type="layout" name="accessibility_button_chooser_item" /> <java-symbol type="id" name="accessibility_button_target_icon" /> |