/* * Copyright (C) 2015 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.settingslib.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; import android.content.ComponentName; import android.content.Context; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.ArraySet; import android.view.accessibility.AccessibilityManager; import com.android.internal.R; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; public class AccessibilityUtils { public static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':'; /** * @return the set of enabled accessibility services. If there are no services, * it returns the unmodifiable {@link Collections#emptySet()}. */ public static Set getEnabledServicesFromSettings(Context context) { return getEnabledServicesFromSettings(context, UserHandle.myUserId()); } /** * Check if the accessibility service is crashed * * @param packageName The package name to check * @param serviceName The service name to check * @param installedServiceInfos The list of installed accessibility service * @return {@code true} if the accessibility service is crashed for the user. * {@code false} otherwise. */ public static boolean hasServiceCrashed(String packageName, String serviceName, List installedServiceInfos) { for (int i = 0; i < installedServiceInfos.size(); i++) { final AccessibilityServiceInfo accessibilityServiceInfo = installedServiceInfos.get(i); final ServiceInfo serviceInfo = installedServiceInfos.get(i).getResolveInfo().serviceInfo; if (TextUtils.equals(serviceInfo.packageName, packageName) && TextUtils.equals(serviceInfo.name, serviceName)) { return accessibilityServiceInfo.crashed; } } return false; } /** * @return the set of enabled accessibility services for {@param userId}. If there are no * services, it returns the unmodifiable {@link Collections#emptySet()}. */ public static Set 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 enabledServices = new HashSet<>(); final TextUtils.StringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR); colonSplitter.setString(enabledServicesSetting); for (String componentNameString : colonSplitter) { final ComponentName enabledService = ComponentName.unflattenFromString( componentNameString); if (enabledService != null) { enabledServices.add(enabledService); } } return enabledServices; } /** * @return a localized version of the text resource specified by resId */ public static CharSequence getTextForLocale(Context context, Locale locale, int resId) { final Resources res = context.getResources(); final Configuration config = new Configuration(res.getConfiguration()); config.setLocale(locale); final Context langContext = context.createConfigurationContext(config); return langContext.getText(resId); } /** * Changes an accessibility component's state. */ public static void setAccessibilityServiceState(Context context, ComponentName toggledService, boolean enabled) { setAccessibilityServiceState(context, toggledService, enabled, UserHandle.myUserId()); } /** * Changes an accessibility component's state for {@param userId}. */ public static void setAccessibilityServiceState(Context context, ComponentName toggledService, boolean enabled, int userId) { // Parse the enabled services. Set enabledServices = AccessibilityUtils.getEnabledServicesFromSettings( context, userId); if (enabledServices.isEmpty()) { enabledServices = new ArraySet<>(1); } // Determine enabled services and accessibility state. boolean accessibilityEnabled = false; if (enabled) { enabledServices.add(toggledService); // Enabling at least one service enables accessibility. accessibilityEnabled = true; } else { enabledServices.remove(toggledService); // Check how many enabled and installed services are present. Set installedServices = getInstalledServices(context); for (ComponentName enabledService : enabledServices) { if (installedServices.contains(enabledService)) { // Disabling the last service disables accessibility. accessibilityEnabled = true; break; } } } // Update the enabled services setting. StringBuilder enabledServicesBuilder = new StringBuilder(); // Keep the enabled services even if they are not installed since we // have no way to know whether the application restore process has // completed. In general the system should be responsible for the // clean up not settings. for (ComponentName enabledService : enabledServices) { enabledServicesBuilder.append(enabledService.flattenToString()); enabledServicesBuilder.append( AccessibilityUtils.ENABLED_ACCESSIBILITY_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); } /** * Get the name of the service that should be toggled by the accessibility shortcut. Use * an OEM-configurable default if the setting has never been set. * * @param context A valid context * @param userId The user whose settings should be checked * @return The component name, flattened to a string, of the target service. */ public static String getShortcutTargetServiceComponentNameString( Context context, int userId) { final String currentShortcutServiceId = Settings.Secure.getStringForUser( context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId); if (currentShortcutServiceId != null) { return currentShortcutServiceId; } return context.getString(R.string.config_defaultAccessibilityService); } private static Set getInstalledServices(Context context) { final Set installedServices = new HashSet<>(); installedServices.clear(); final List installedServiceInfos = AccessibilityManager.getInstance(context) .getInstalledAccessibilityServiceList(); if (installedServiceInfos == null) { return installedServices; } for (final AccessibilityServiceInfo info : installedServiceInfos) { final ResolveInfo resolveInfo = info.getResolveInfo(); final ComponentName installedService = new ComponentName( resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name); installedServices.add(installedService); } return installedServices; } }