1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.accessibility.util; 18 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; 19 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; 20 21 import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR; 22 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; 23 24 import android.annotation.NonNull; 25 import android.content.Context; 26 import android.provider.Settings; 27 import android.text.TextUtils; 28 import android.view.accessibility.AccessibilityManager; 29 import android.view.accessibility.AccessibilityManager.ShortcutType; 30 31 import java.util.List; 32 import java.util.StringJoiner; 33 34 /** 35 * Collection of utilities for accessibility shortcut. 36 */ 37 public final class ShortcutUtils { ShortcutUtils()38 private ShortcutUtils() {} 39 40 private static final TextUtils.SimpleStringSplitter sStringColonSplitter = 41 new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR); 42 43 /** 44 * Opts in component id into colon-separated {@link UserShortcutType} 45 * key's string from Settings. 46 * 47 * @param context The current context. 48 * @param shortcutType The preferred shortcut type user selected. 49 * @param componentId The component id that need to be opted in Settings. 50 */ optInValueToSettings(Context context, @UserShortcutType int shortcutType, @NonNull String componentId)51 public static void optInValueToSettings(Context context, @UserShortcutType int shortcutType, 52 @NonNull String componentId) { 53 final StringJoiner joiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR)); 54 final String targetKey = convertToKey(shortcutType); 55 final String targetString = Settings.Secure.getString(context.getContentResolver(), 56 targetKey); 57 58 if (isComponentIdExistingInSettings(context, shortcutType, componentId)) { 59 return; 60 } 61 62 if (!TextUtils.isEmpty(targetString)) { 63 joiner.add(targetString); 64 } 65 joiner.add(componentId); 66 67 Settings.Secure.putString(context.getContentResolver(), targetKey, joiner.toString()); 68 } 69 70 /** 71 * Opts out of component id into colon-separated {@link UserShortcutType} key's string from 72 * Settings. 73 * 74 * @param context The current context. 75 * @param shortcutType The preferred shortcut type user selected. 76 * @param componentId The component id that need to be opted out of Settings. 77 */ optOutValueFromSettings( Context context, @UserShortcutType int shortcutType, @NonNull String componentId)78 public static void optOutValueFromSettings( 79 Context context, @UserShortcutType int shortcutType, @NonNull String componentId) { 80 final StringJoiner joiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR)); 81 final String targetsKey = convertToKey(shortcutType); 82 final String targetsValue = Settings.Secure.getString(context.getContentResolver(), 83 targetsKey); 84 85 if (TextUtils.isEmpty(targetsValue)) { 86 return; 87 } 88 89 sStringColonSplitter.setString(targetsValue); 90 while (sStringColonSplitter.hasNext()) { 91 final String id = sStringColonSplitter.next(); 92 if (TextUtils.isEmpty(id) || componentId.equals(id)) { 93 continue; 94 } 95 joiner.add(id); 96 } 97 98 Settings.Secure.putString(context.getContentResolver(), targetsKey, joiner.toString()); 99 } 100 101 /** 102 * Returns if component id existed in Settings. 103 * 104 * @param context The current context. 105 * @param shortcutType The preferred shortcut type user selected. 106 * @param componentId The component id that need to be checked existed in Settings. 107 * @return {@code true} if component id existed in Settings. 108 */ isComponentIdExistingInSettings(Context context, @UserShortcutType int shortcutType, @NonNull String componentId)109 public static boolean isComponentIdExistingInSettings(Context context, 110 @UserShortcutType int shortcutType, @NonNull String componentId) { 111 final String targetKey = convertToKey(shortcutType); 112 final String targetString = Settings.Secure.getString(context.getContentResolver(), 113 targetKey); 114 115 if (TextUtils.isEmpty(targetString)) { 116 return false; 117 } 118 119 sStringColonSplitter.setString(targetString); 120 while (sStringColonSplitter.hasNext()) { 121 final String id = sStringColonSplitter.next(); 122 if (componentId.equals(id)) { 123 return true; 124 } 125 } 126 127 return false; 128 } 129 130 /** 131 * Returns if a {@code shortcutType} shortcut contains {@code componentId}. 132 * 133 * @param context The current context. 134 * @param shortcutType The preferred shortcut type user selected. 135 * @param componentId The component id that need to be checked. 136 * @return {@code true} if a component id is contained. 137 */ isShortcutContained(Context context, @ShortcutType int shortcutType, @NonNull String componentId)138 public static boolean isShortcutContained(Context context, @ShortcutType int shortcutType, 139 @NonNull String componentId) { 140 final AccessibilityManager am = (AccessibilityManager) context.getSystemService( 141 Context.ACCESSIBILITY_SERVICE); 142 final List<String> requiredTargets = am.getAccessibilityShortcutTargets(shortcutType); 143 return requiredTargets.contains(componentId); 144 } 145 146 /** 147 * Converts {@link UserShortcutType} to {@link Settings.Secure} key. 148 * 149 * @param type The shortcut type. 150 * @return Mapping key in Settings. 151 */ convertToKey(@serShortcutType int type)152 public static String convertToKey(@UserShortcutType int type) { 153 switch (type) { 154 case UserShortcutType.SOFTWARE: 155 return Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS; 156 case UserShortcutType.HARDWARE: 157 return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; 158 case UserShortcutType.TRIPLETAP: 159 return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED; 160 default: 161 throw new IllegalArgumentException( 162 "Unsupported user shortcut type: " + type); 163 } 164 } 165 166 /** 167 * Converts {@link ShortcutType} to {@link UserShortcutType}. 168 * 169 * @param type The shortcut type. 170 * @return Mapping type from {@link UserShortcutType}. 171 */ convertToUserType(@hortcutType int type)172 public static @UserShortcutType int convertToUserType(@ShortcutType int type) { 173 switch (type) { 174 case ACCESSIBILITY_BUTTON: 175 return UserShortcutType.SOFTWARE; 176 case ACCESSIBILITY_SHORTCUT_KEY: 177 return UserShortcutType.HARDWARE; 178 default: 179 throw new IllegalArgumentException( 180 "Unsupported shortcut type:" + type); 181 } 182 } 183 } 184