1 /* 2 * Copyright (C) 2015 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.settingslib.accessibility; 18 19 import android.accessibilityservice.AccessibilityServiceInfo; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.pm.ResolveInfo; 23 import android.content.res.Configuration; 24 import android.content.res.Resources; 25 import android.os.UserHandle; 26 import android.provider.Settings; 27 import android.text.TextUtils; 28 import android.util.ArraySet; 29 import android.view.accessibility.AccessibilityManager; 30 31 import com.android.internal.R; 32 33 import java.util.Collections; 34 import java.util.HashSet; 35 import java.util.List; 36 import java.util.Locale; 37 import java.util.Set; 38 39 public class AccessibilityUtils { 40 public static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':'; 41 42 final static TextUtils.SimpleStringSplitter sStringColonSplitter = 43 new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR); 44 45 /** 46 * @return the set of enabled accessibility services. If there are no services, 47 * it returns the unmodifiable {@link Collections#emptySet()}. 48 */ getEnabledServicesFromSettings(Context context)49 public static Set<ComponentName> getEnabledServicesFromSettings(Context context) { 50 return getEnabledServicesFromSettings(context, UserHandle.myUserId()); 51 } 52 53 /** 54 * @return the set of enabled accessibility services for {@param userId}. If there are no 55 * services, it returns the unmodifiable {@link Collections#emptySet()}. 56 */ getEnabledServicesFromSettings(Context context, int userId)57 public static Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) { 58 final String enabledServicesSetting = Settings.Secure.getStringForUser( 59 context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 60 userId); 61 if (enabledServicesSetting == null) { 62 return Collections.emptySet(); 63 } 64 65 final Set<ComponentName> enabledServices = new HashSet<>(); 66 final TextUtils.SimpleStringSplitter colonSplitter = sStringColonSplitter; 67 colonSplitter.setString(enabledServicesSetting); 68 69 while (colonSplitter.hasNext()) { 70 final String componentNameString = colonSplitter.next(); 71 final ComponentName enabledService = ComponentName.unflattenFromString( 72 componentNameString); 73 if (enabledService != null) { 74 enabledServices.add(enabledService); 75 } 76 } 77 78 return enabledServices; 79 } 80 81 /** 82 * @return a localized version of the text resource specified by resId 83 */ getTextForLocale(Context context, Locale locale, int resId)84 public static CharSequence getTextForLocale(Context context, Locale locale, int resId) { 85 final Resources res = context.getResources(); 86 final Configuration config = new Configuration(res.getConfiguration()); 87 config.setLocale(locale); 88 final Context langContext = context.createConfigurationContext(config); 89 return langContext.getText(resId); 90 } 91 92 /** 93 * Changes an accessibility component's state. 94 */ setAccessibilityServiceState(Context context, ComponentName toggledService, boolean enabled)95 public static void setAccessibilityServiceState(Context context, ComponentName toggledService, 96 boolean enabled) { 97 setAccessibilityServiceState(context, toggledService, enabled, UserHandle.myUserId()); 98 } 99 100 /** 101 * Changes an accessibility component's state for {@param userId}. 102 */ setAccessibilityServiceState(Context context, ComponentName toggledService, boolean enabled, int userId)103 public static void setAccessibilityServiceState(Context context, ComponentName toggledService, 104 boolean enabled, int userId) { 105 // Parse the enabled services. 106 Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings( 107 context, userId); 108 109 if (enabledServices.isEmpty()) { 110 enabledServices = new ArraySet<>(1); 111 } 112 113 // Determine enabled services and accessibility state. 114 boolean accessibilityEnabled = false; 115 if (enabled) { 116 enabledServices.add(toggledService); 117 // Enabling at least one service enables accessibility. 118 accessibilityEnabled = true; 119 } else { 120 enabledServices.remove(toggledService); 121 // Check how many enabled and installed services are present. 122 Set<ComponentName> installedServices = getInstalledServices(context); 123 for (ComponentName enabledService : enabledServices) { 124 if (installedServices.contains(enabledService)) { 125 // Disabling the last service disables accessibility. 126 accessibilityEnabled = true; 127 break; 128 } 129 } 130 } 131 132 // Update the enabled services setting. 133 StringBuilder enabledServicesBuilder = new StringBuilder(); 134 // Keep the enabled services even if they are not installed since we 135 // have no way to know whether the application restore process has 136 // completed. In general the system should be responsible for the 137 // clean up not settings. 138 for (ComponentName enabledService : enabledServices) { 139 enabledServicesBuilder.append(enabledService.flattenToString()); 140 enabledServicesBuilder.append( 141 AccessibilityUtils.ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR); 142 } 143 final int enabledServicesBuilderLength = enabledServicesBuilder.length(); 144 if (enabledServicesBuilderLength > 0) { 145 enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1); 146 } 147 Settings.Secure.putStringForUser(context.getContentResolver(), 148 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 149 enabledServicesBuilder.toString(), userId); 150 } 151 152 /** 153 * Get the name of the service that should be toggled by the accessibility shortcut. Use 154 * an OEM-configurable default if the setting has never been set. 155 * 156 * @param context A valid context 157 * @param userId The user whose settings should be checked 158 * 159 * @return The component name, flattened to a string, of the target service. 160 */ getShortcutTargetServiceComponentNameString( Context context, int userId)161 public static String getShortcutTargetServiceComponentNameString( 162 Context context, int userId) { 163 final String currentShortcutServiceId = Settings.Secure.getStringForUser( 164 context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, 165 userId); 166 if (currentShortcutServiceId != null) { 167 return currentShortcutServiceId; 168 } 169 return context.getString(R.string.config_defaultAccessibilityService); 170 } 171 172 /** 173 * Check if the accessibility shortcut is enabled for a user 174 * 175 * @param context A valid context 176 * @param userId The user of interest 177 * @return {@code true} if the shortcut is enabled for the user. {@code false} otherwise. 178 * Note that the shortcut may be enabled, but no action associated with it. 179 */ isShortcutEnabled(Context context, int userId)180 public static boolean isShortcutEnabled(Context context, int userId) { 181 return Settings.Secure.getIntForUser(context.getContentResolver(), 182 Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, userId) == 1; 183 } 184 getInstalledServices(Context context)185 private static Set<ComponentName> getInstalledServices(Context context) { 186 final Set<ComponentName> installedServices = new HashSet<>(); 187 installedServices.clear(); 188 189 final List<AccessibilityServiceInfo> installedServiceInfos = 190 AccessibilityManager.getInstance(context) 191 .getInstalledAccessibilityServiceList(); 192 if (installedServiceInfos == null) { 193 return installedServices; 194 } 195 196 for (final AccessibilityServiceInfo info : installedServiceInfos) { 197 final ResolveInfo resolveInfo = info.getResolveInfo(); 198 final ComponentName installedService = new ComponentName( 199 resolveInfo.serviceInfo.packageName, 200 resolveInfo.serviceInfo.name); 201 installedServices.add(installedService); 202 } 203 return installedServices; 204 } 205 206 } 207