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