Home | History | Annotate | Download | only in accessibility
      1 /*
      2  * Copyright (C) 2009 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.settings.accessibility;
     18 
     19 import static android.os.Vibrator.VibrationIntensity;
     20 
     21 import android.accessibilityservice.AccessibilityServiceInfo;
     22 import android.app.admin.DevicePolicyManager;
     23 import android.content.ComponentName;
     24 import android.content.Context;
     25 import android.content.pm.ResolveInfo;
     26 import android.content.pm.ServiceInfo;
     27 import android.content.res.Resources;
     28 import android.graphics.drawable.Drawable;
     29 import android.net.Uri;
     30 import android.os.Bundle;
     31 import android.os.Handler;
     32 import android.os.UserHandle;
     33 import android.os.Vibrator;
     34 import android.provider.SearchIndexableResource;
     35 import android.provider.Settings;
     36 import android.support.annotation.VisibleForTesting;
     37 import android.support.v14.preference.SwitchPreference;
     38 import android.support.v4.content.ContextCompat;
     39 import android.support.v7.preference.ListPreference;
     40 import android.support.v7.preference.Preference;
     41 import android.support.v7.preference.PreferenceCategory;
     42 import android.support.v7.preference.PreferenceScreen;
     43 import android.text.TextUtils;
     44 import android.util.ArrayMap;
     45 import android.view.KeyCharacterMap;
     46 import android.view.KeyEvent;
     47 import android.view.accessibility.AccessibilityManager;
     48 
     49 import com.android.internal.accessibility.AccessibilityShortcutController;
     50 import com.android.internal.content.PackageMonitor;
     51 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     52 import com.android.internal.view.RotationPolicy;
     53 import com.android.internal.view.RotationPolicy.RotationPolicyListener;
     54 import com.android.settings.R;
     55 import com.android.settings.SettingsPreferenceFragment;
     56 import com.android.settings.Utils;
     57 import com.android.settings.search.BaseSearchIndexProvider;
     58 import com.android.settings.search.Indexable;
     59 import com.android.settingslib.RestrictedLockUtils;
     60 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
     61 import com.android.settingslib.RestrictedPreference;
     62 import com.android.settingslib.accessibility.AccessibilityUtils;
     63 
     64 import java.util.ArrayList;
     65 import java.util.Collection;
     66 import java.util.HashMap;
     67 import java.util.List;
     68 import java.util.Map;
     69 import java.util.Set;
     70 
     71 /**
     72  * Activity with the accessibility settings.
     73  */
     74 public class AccessibilitySettings extends SettingsPreferenceFragment implements
     75         Preference.OnPreferenceChangeListener, Indexable {
     76 
     77     // Index of the first preference in a preference category.
     78     private static final int FIRST_PREFERENCE_IN_CATEGORY_INDEX = -1;
     79 
     80     // Preference categories
     81     private static final String CATEGORY_SCREEN_READER = "screen_reader_category";
     82     private static final String CATEGORY_AUDIO_AND_CAPTIONS = "audio_and_captions_category";
     83     private static final String CATEGORY_DISPLAY = "display_category";
     84     private static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category";
     85     private static final String CATEGORY_EXPERIMENTAL = "experimental_category";
     86     private static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category";
     87 
     88     private static final String[] CATEGORIES = new String[] {
     89             CATEGORY_SCREEN_READER, CATEGORY_AUDIO_AND_CAPTIONS, CATEGORY_DISPLAY,
     90             CATEGORY_INTERACTION_CONTROL, CATEGORY_EXPERIMENTAL, CATEGORY_DOWNLOADED_SERVICES
     91     };
     92 
     93     // Preferences
     94     private static final String TOGGLE_HIGH_TEXT_CONTRAST_PREFERENCE =
     95             "toggle_high_text_contrast_preference";
     96     private static final String TOGGLE_INVERSION_PREFERENCE =
     97             "toggle_inversion_preference";
     98     private static final String TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE =
     99             "toggle_power_button_ends_call_preference";
    100     private static final String TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE =
    101             "toggle_lock_screen_rotation_preference";
    102     private static final String TOGGLE_LARGE_POINTER_ICON =
    103             "toggle_large_pointer_icon";
    104     private static final String TOGGLE_DISABLE_ANIMATIONS = "toggle_disable_animations";
    105     private static final String TOGGLE_MASTER_MONO =
    106             "toggle_master_mono";
    107     private static final String SELECT_LONG_PRESS_TIMEOUT_PREFERENCE =
    108             "select_long_press_timeout_preference";
    109     private static final String ACCESSIBILITY_SHORTCUT_PREFERENCE =
    110             "accessibility_shortcut_preference";
    111     private static final String CAPTIONING_PREFERENCE_SCREEN =
    112             "captioning_preference_screen";
    113     private static final String DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN =
    114             "magnification_preference_screen";
    115     private static final String FONT_SIZE_PREFERENCE_SCREEN =
    116             "font_size_preference_screen";
    117     private static final String TTS_SETTINGS_PREFERENCE =
    118             "tts_settings_preference";
    119     private static final String AUTOCLICK_PREFERENCE_SCREEN =
    120             "autoclick_preference_screen";
    121     private static final String VIBRATION_PREFERENCE_SCREEN =
    122             "vibration_preference_screen";
    123     private static final String DISPLAY_DALTONIZER_PREFERENCE_SCREEN =
    124             "daltonizer_preference_screen";
    125 
    126     // Extras passed to sub-fragments.
    127     static final String EXTRA_PREFERENCE_KEY = "preference_key";
    128     static final String EXTRA_CHECKED = "checked";
    129     static final String EXTRA_TITLE = "title";
    130     static final String EXTRA_TITLE_RES = "title_res";
    131     static final String EXTRA_RESOLVE_INFO = "resolve_info";
    132     static final String EXTRA_SUMMARY = "summary";
    133     static final String EXTRA_SUMMARY_RES = "summary_res";
    134     static final String EXTRA_SETTINGS_TITLE = "settings_title";
    135     static final String EXTRA_COMPONENT_NAME = "component_name";
    136     static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name";
    137     static final String EXTRA_VIDEO_RAW_RESOURCE_ID = "video_resource";
    138     static final String EXTRA_LAUNCHED_FROM_SUW = "from_suw";
    139 
    140     // Timeout before we update the services if packages are added/removed
    141     // since the AccessibilityManagerService has to do that processing first
    142     // to generate the AccessibilityServiceInfo we need for proper
    143     // presentation.
    144     private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000;
    145 
    146     // Settings that should be changed when toggling animations
    147     private static final String[] TOGGLE_ANIMATION_TARGETS = {
    148             Settings.Global.WINDOW_ANIMATION_SCALE, Settings.Global.TRANSITION_ANIMATION_SCALE,
    149             Settings.Global.ANIMATOR_DURATION_SCALE
    150     };
    151     private static final String ANIMATION_ON_VALUE = "1";
    152     private static final String ANIMATION_OFF_VALUE = "0";
    153 
    154     private final Map<String, String> mLongPressTimeoutValueToTitleMap = new HashMap<>();
    155 
    156     private final Handler mHandler = new Handler();
    157 
    158     private final Runnable mUpdateRunnable = new Runnable() {
    159         @Override
    160         public void run() {
    161             if (getActivity() != null) {
    162                 updateServicePreferences();
    163             }
    164         }
    165     };
    166 
    167     private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() {
    168         @Override
    169         public void onPackageAdded(String packageName, int uid) {
    170             sendUpdate();
    171         }
    172 
    173         @Override
    174         public void onPackageAppeared(String packageName, int reason) {
    175             sendUpdate();
    176         }
    177 
    178         @Override
    179         public void onPackageDisappeared(String packageName, int reason) {
    180             sendUpdate();
    181         }
    182 
    183         @Override
    184         public void onPackageRemoved(String packageName, int uid) {
    185             sendUpdate();
    186         }
    187 
    188         private void sendUpdate() {
    189             mHandler.postDelayed(mUpdateRunnable, DELAY_UPDATE_SERVICES_MILLIS);
    190         }
    191     };
    192 
    193     private final SettingsContentObserver mSettingsContentObserver;
    194 
    195     private final RotationPolicyListener mRotationPolicyListener = new RotationPolicyListener() {
    196         @Override
    197         public void onChange() {
    198             updateLockScreenRotationCheckbox();
    199         }
    200     };
    201 
    202     private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap =
    203             new ArrayMap<>();
    204     private final Map<Preference, PreferenceCategory> mServicePreferenceToPreferenceCategoryMap =
    205             new ArrayMap<>();
    206     private final Map<ComponentName, PreferenceCategory> mPreBundledServiceComponentToCategoryMap =
    207             new ArrayMap<>();
    208 
    209     private SwitchPreference mToggleHighTextContrastPreference;
    210     private SwitchPreference mTogglePowerButtonEndsCallPreference;
    211     private SwitchPreference mToggleLockScreenRotationPreference;
    212     private SwitchPreference mToggleLargePointerIconPreference;
    213     private SwitchPreference mToggleDisableAnimationsPreference;
    214     private SwitchPreference mToggleMasterMonoPreference;
    215     private ListPreference mSelectLongPressTimeoutPreference;
    216     private Preference mCaptioningPreferenceScreen;
    217     private Preference mDisplayMagnificationPreferenceScreen;
    218     private Preference mFontSizePreferenceScreen;
    219     private Preference mAutoclickPreferenceScreen;
    220     private Preference mAccessibilityShortcutPreferenceScreen;
    221     private Preference mDisplayDaltonizerPreferenceScreen;
    222     private Preference mVibrationPreferenceScreen;
    223     private SwitchPreference mToggleInversionPreference;
    224 
    225     private int mLongPressTimeoutDefault;
    226 
    227     private DevicePolicyManager mDpm;
    228 
    229     /**
    230      * Check if the color transforms are color accelerated. Some transforms are experimental only
    231      * on non-accelerated platforms due to the performance implications.
    232      *
    233      * @param context The current context
    234      */
    235     public static boolean isColorTransformAccelerated(Context context) {
    236         return context.getResources()
    237                 .getBoolean(com.android.internal.R.bool.config_setColorTransformAccelerated);
    238     }
    239 
    240     public AccessibilitySettings() {
    241         // Observe changes to anything that the shortcut can toggle, so we can reflect updates
    242         final Collection<AccessibilityShortcutController.ToggleableFrameworkFeatureInfo> features =
    243                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap().values();
    244         final List<String> shortcutFeatureKeys = new ArrayList<>(features.size());
    245         for (AccessibilityShortcutController.ToggleableFrameworkFeatureInfo feature : features) {
    246             shortcutFeatureKeys.add(feature.getSettingKey());
    247         }
    248         mSettingsContentObserver = new SettingsContentObserver(mHandler, shortcutFeatureKeys) {
    249             @Override
    250             public void onChange(boolean selfChange, Uri uri) {
    251                 updateAllPreferences();
    252             }
    253         };
    254     }
    255 
    256     @Override
    257     public int getMetricsCategory() {
    258         return MetricsEvent.ACCESSIBILITY;
    259     }
    260 
    261     @Override
    262     public int getHelpResource() {
    263         return R.string.help_uri_accessibility;
    264     }
    265 
    266     @Override
    267     public void onCreate(Bundle icicle) {
    268         super.onCreate(icicle);
    269         addPreferencesFromResource(R.xml.accessibility_settings);
    270         initializeAllPreferences();
    271         mDpm = (DevicePolicyManager) (getActivity()
    272                 .getSystemService(Context.DEVICE_POLICY_SERVICE));
    273     }
    274 
    275     @Override
    276     public void onResume() {
    277         super.onResume();
    278         updateAllPreferences();
    279 
    280         mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false);
    281         mSettingsContentObserver.register(getContentResolver());
    282         if (RotationPolicy.isRotationSupported(getActivity())) {
    283             RotationPolicy.registerRotationPolicyListener(getActivity(),
    284                     mRotationPolicyListener);
    285         }
    286     }
    287 
    288     @Override
    289     public void onPause() {
    290         mSettingsPackageMonitor.unregister();
    291         mSettingsContentObserver.unregister(getContentResolver());
    292         if (RotationPolicy.isRotationSupported(getActivity())) {
    293             RotationPolicy.unregisterRotationPolicyListener(getActivity(),
    294                     mRotationPolicyListener);
    295         }
    296         super.onPause();
    297     }
    298 
    299     @Override
    300     public boolean onPreferenceChange(Preference preference, Object newValue) {
    301         if (mSelectLongPressTimeoutPreference == preference) {
    302             handleLongPressTimeoutPreferenceChange((String) newValue);
    303             return true;
    304         } else if (mToggleInversionPreference == preference) {
    305             handleToggleInversionPreferenceChange((Boolean) newValue);
    306             return true;
    307         }
    308         return false;
    309     }
    310 
    311     private void handleLongPressTimeoutPreferenceChange(String stringValue) {
    312         Settings.Secure.putInt(getContentResolver(),
    313                 Settings.Secure.LONG_PRESS_TIMEOUT, Integer.parseInt(stringValue));
    314         mSelectLongPressTimeoutPreference.setSummary(
    315                 mLongPressTimeoutValueToTitleMap.get(stringValue));
    316     }
    317 
    318     private void handleToggleInversionPreferenceChange(boolean checked) {
    319         Settings.Secure.putInt(getContentResolver(),
    320                 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, (checked ? 1 : 0));
    321     }
    322 
    323     @Override
    324     public boolean onPreferenceTreeClick(Preference preference) {
    325         if (mToggleHighTextContrastPreference == preference) {
    326             handleToggleTextContrastPreferenceClick();
    327             return true;
    328         } else if (mTogglePowerButtonEndsCallPreference == preference) {
    329             handleTogglePowerButtonEndsCallPreferenceClick();
    330             return true;
    331         } else if (mToggleLockScreenRotationPreference == preference) {
    332             handleLockScreenRotationPreferenceClick();
    333             return true;
    334         } else if (mToggleLargePointerIconPreference == preference) {
    335             handleToggleLargePointerIconPreferenceClick();
    336             return true;
    337         } else if (mToggleDisableAnimationsPreference == preference) {
    338             handleToggleDisableAnimations();
    339             return true;
    340         } else if (mToggleMasterMonoPreference == preference) {
    341             handleToggleMasterMonoPreferenceClick();
    342             return true;
    343         }
    344         return super.onPreferenceTreeClick(preference);
    345     }
    346 
    347     public static CharSequence getServiceSummary(Context context, AccessibilityServiceInfo info,
    348             boolean serviceEnabled) {
    349         final String serviceState = serviceEnabled
    350                 ? context.getString(R.string.accessibility_summary_state_enabled)
    351                 : context.getString(R.string.accessibility_summary_state_disabled);
    352         final CharSequence serviceSummary = info.loadSummary(context.getPackageManager());
    353         final String stateSummaryCombo = context.getString(
    354                 R.string.preference_summary_default_combination,
    355                 serviceState, serviceSummary);
    356 
    357         return (TextUtils.isEmpty(serviceSummary))
    358                 ? serviceState
    359                 : stateSummaryCombo;
    360     }
    361 
    362     private void handleToggleTextContrastPreferenceClick() {
    363         Settings.Secure.putInt(getContentResolver(),
    364                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
    365                 (mToggleHighTextContrastPreference.isChecked() ? 1 : 0));
    366     }
    367 
    368     private void handleTogglePowerButtonEndsCallPreferenceClick() {
    369         Settings.Secure.putInt(getContentResolver(),
    370                 Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
    371                 (mTogglePowerButtonEndsCallPreference.isChecked()
    372                         ? Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP
    373                         : Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF));
    374     }
    375 
    376     private void handleLockScreenRotationPreferenceClick() {
    377         RotationPolicy.setRotationLockForAccessibility(getActivity(),
    378                 !mToggleLockScreenRotationPreference.isChecked());
    379     }
    380 
    381     private void handleToggleLargePointerIconPreferenceClick() {
    382         Settings.Secure.putInt(getContentResolver(),
    383                 Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
    384                 mToggleLargePointerIconPreference.isChecked() ? 1 : 0);
    385     }
    386 
    387     private void handleToggleDisableAnimations() {
    388         String newAnimationValue = mToggleDisableAnimationsPreference.isChecked()
    389                 ? ANIMATION_OFF_VALUE : ANIMATION_ON_VALUE;
    390         for (String animationPreference : TOGGLE_ANIMATION_TARGETS) {
    391             Settings.Global.putString(getContentResolver(), animationPreference, newAnimationValue);
    392         }
    393     }
    394 
    395     private void handleToggleMasterMonoPreferenceClick() {
    396         Settings.System.putIntForUser(getContentResolver(), Settings.System.MASTER_MONO,
    397                 mToggleMasterMonoPreference.isChecked() ? 1 : 0, UserHandle.USER_CURRENT);
    398     }
    399 
    400     private void initializeAllPreferences() {
    401         for (int i = 0; i < CATEGORIES.length; i++) {
    402             PreferenceCategory prefCategory = (PreferenceCategory) findPreference(CATEGORIES[i]);
    403             mCategoryToPrefCategoryMap.put(CATEGORIES[i], prefCategory);
    404         }
    405 
    406         // Text contrast.
    407         mToggleHighTextContrastPreference =
    408                 (SwitchPreference) findPreference(TOGGLE_HIGH_TEXT_CONTRAST_PREFERENCE);
    409 
    410         // Display inversion.
    411         mToggleInversionPreference = (SwitchPreference) findPreference(TOGGLE_INVERSION_PREFERENCE);
    412         mToggleInversionPreference.setOnPreferenceChangeListener(this);
    413 
    414         // Power button ends calls.
    415         mTogglePowerButtonEndsCallPreference =
    416                 (SwitchPreference) findPreference(TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE);
    417         if (!KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER)
    418                 || !Utils.isVoiceCapable(getActivity())) {
    419             mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL)
    420                     .removePreference(mTogglePowerButtonEndsCallPreference);
    421         }
    422 
    423         // Lock screen rotation.
    424         mToggleLockScreenRotationPreference =
    425                 (SwitchPreference) findPreference(TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE);
    426         if (!RotationPolicy.isRotationSupported(getActivity())) {
    427             mCategoryToPrefCategoryMap.get(CATEGORY_INTERACTION_CONTROL)
    428                     .removePreference(mToggleLockScreenRotationPreference);
    429         }
    430 
    431         // Large pointer icon.
    432         mToggleLargePointerIconPreference =
    433                 (SwitchPreference) findPreference(TOGGLE_LARGE_POINTER_ICON);
    434 
    435         mToggleDisableAnimationsPreference =
    436                 (SwitchPreference) findPreference(TOGGLE_DISABLE_ANIMATIONS);
    437 
    438         // Master Mono
    439         mToggleMasterMonoPreference =
    440                 (SwitchPreference) findPreference(TOGGLE_MASTER_MONO);
    441 
    442         // Long press timeout.
    443         mSelectLongPressTimeoutPreference =
    444                 (ListPreference) findPreference(SELECT_LONG_PRESS_TIMEOUT_PREFERENCE);
    445         mSelectLongPressTimeoutPreference.setOnPreferenceChangeListener(this);
    446         if (mLongPressTimeoutValueToTitleMap.size() == 0) {
    447             String[] timeoutValues = getResources().getStringArray(
    448                     R.array.long_press_timeout_selector_values);
    449             mLongPressTimeoutDefault = Integer.parseInt(timeoutValues[0]);
    450             String[] timeoutTitles = getResources().getStringArray(
    451                     R.array.long_press_timeout_selector_titles);
    452             final int timeoutValueCount = timeoutValues.length;
    453             for (int i = 0; i < timeoutValueCount; i++) {
    454                 mLongPressTimeoutValueToTitleMap.put(timeoutValues[i], timeoutTitles[i]);
    455             }
    456         }
    457 
    458         // Captioning.
    459         mCaptioningPreferenceScreen = findPreference(CAPTIONING_PREFERENCE_SCREEN);
    460 
    461         // Display magnification.
    462         mDisplayMagnificationPreferenceScreen = findPreference(
    463                 DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN);
    464         configureMagnificationPreferenceIfNeeded(mDisplayMagnificationPreferenceScreen);
    465 
    466         // Font size.
    467         mFontSizePreferenceScreen = findPreference(FONT_SIZE_PREFERENCE_SCREEN);
    468 
    469         // Autoclick after pointer stops.
    470         mAutoclickPreferenceScreen = findPreference(AUTOCLICK_PREFERENCE_SCREEN);
    471 
    472         // Display color adjustments.
    473         mDisplayDaltonizerPreferenceScreen = findPreference(DISPLAY_DALTONIZER_PREFERENCE_SCREEN);
    474 
    475         // Accessibility shortcut.
    476         mAccessibilityShortcutPreferenceScreen = findPreference(ACCESSIBILITY_SHORTCUT_PREFERENCE);
    477 
    478         // Vibrations.
    479         mVibrationPreferenceScreen = findPreference(VIBRATION_PREFERENCE_SCREEN);
    480     }
    481 
    482     private void updateAllPreferences() {
    483         updateSystemPreferences();
    484         updateServicePreferences();
    485     }
    486 
    487     protected void updateServicePreferences() {
    488         // Since services category is auto generated we have to do a pass
    489         // to generate it since services can come and go and then based on
    490         // the global accessibility state to decided whether it is enabled.
    491 
    492         // Generate.
    493         ArrayList<Preference> servicePreferences =
    494                 new ArrayList<>(mServicePreferenceToPreferenceCategoryMap.keySet());
    495         for (int i = 0; i < servicePreferences.size(); i++) {
    496             Preference service = servicePreferences.get(i);
    497             PreferenceCategory category = mServicePreferenceToPreferenceCategoryMap.get(service);
    498             category.removePreference(service);
    499         }
    500 
    501         initializePreBundledServicesMapFromArray(CATEGORY_SCREEN_READER,
    502                 R.array.config_preinstalled_screen_reader_services);
    503         initializePreBundledServicesMapFromArray(CATEGORY_AUDIO_AND_CAPTIONS,
    504                 R.array.config_preinstalled_audio_and_caption_services);
    505         initializePreBundledServicesMapFromArray(CATEGORY_DISPLAY,
    506                 R.array.config_preinstalled_display_services);
    507         initializePreBundledServicesMapFromArray(CATEGORY_INTERACTION_CONTROL,
    508                 R.array.config_preinstalled_interaction_control_services);
    509 
    510         AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getActivity());
    511 
    512         List<AccessibilityServiceInfo> installedServices =
    513                 accessibilityManager.getInstalledAccessibilityServiceList();
    514         List<AccessibilityServiceInfo> enabledServiceInfos = accessibilityManager
    515                 .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
    516         Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(
    517                 getActivity());
    518         List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
    519                 UserHandle.myUserId());
    520 
    521         PreferenceCategory downloadedServicesCategory =
    522                 mCategoryToPrefCategoryMap.get(CATEGORY_DOWNLOADED_SERVICES);
    523         // Temporarily add the downloaded services category back if it was previously removed.
    524         if (findPreference(CATEGORY_DOWNLOADED_SERVICES) == null) {
    525             getPreferenceScreen().addPreference(downloadedServicesCategory);
    526         }
    527 
    528         for (int i = 0, count = installedServices.size(); i < count; ++i) {
    529             final AccessibilityServiceInfo info = installedServices.get(i);
    530             final ResolveInfo resolveInfo = info.getResolveInfo();
    531 
    532             final RestrictedPreference preference =
    533                     new RestrictedPreference(downloadedServicesCategory.getContext());
    534             final String title = resolveInfo.loadLabel(getPackageManager()).toString();
    535 
    536             Drawable icon;
    537             if (resolveInfo.getIconResource() == 0) {
    538                 icon = ContextCompat.getDrawable(getContext(), R.mipmap.ic_accessibility_generic);
    539             } else {
    540                 icon = resolveInfo.loadIcon(getPackageManager());
    541             }
    542 
    543             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
    544             final String packageName = serviceInfo.packageName;
    545             final ComponentName componentName = new ComponentName(packageName, serviceInfo.name);
    546 
    547             preference.setKey(componentName.flattenToString());
    548 
    549             preference.setTitle(title);
    550             Utils.setSafeIcon(preference, icon);
    551             final boolean serviceEnabled = enabledServices.contains(componentName);
    552             String description = info.loadDescription(getPackageManager());
    553             if (TextUtils.isEmpty(description)) {
    554                 description = getString(R.string.accessibility_service_default_description);
    555             }
    556 
    557             if (serviceEnabled && AccessibilityUtils.hasServiceCrashed(
    558                     packageName, serviceInfo.name, enabledServiceInfos)) {
    559                 // Update the summaries for services that have crashed.
    560                 preference.setSummary(R.string.accessibility_summary_state_stopped);
    561                 description = getString(R.string.accessibility_description_state_stopped);
    562             } else {
    563                 final CharSequence serviceSummary = getServiceSummary(getContext(), info,
    564                         serviceEnabled);
    565                 preference.setSummary(serviceSummary);
    566             }
    567 
    568             // Disable all accessibility services that are not permitted.
    569             final boolean serviceAllowed =
    570                     permittedServices == null || permittedServices.contains(packageName);
    571             if (!serviceAllowed && !serviceEnabled) {
    572                 final EnforcedAdmin admin =
    573                         RestrictedLockUtils.checkIfAccessibilityServiceDisallowed(
    574                                 getActivity(), packageName, UserHandle.myUserId());
    575                 if (admin != null) {
    576                     preference.setDisabledByAdmin(admin);
    577                 } else {
    578                     preference.setEnabled(false);
    579                 }
    580             } else {
    581                 preference.setEnabled(true);
    582             }
    583 
    584             preference.setFragment(ToggleAccessibilityServicePreferenceFragment.class.getName());
    585             preference.setPersistent(true);
    586 
    587             final Bundle extras = preference.getExtras();
    588             extras.putString(EXTRA_PREFERENCE_KEY, preference.getKey());
    589             extras.putBoolean(EXTRA_CHECKED, serviceEnabled);
    590             extras.putString(EXTRA_TITLE, title);
    591             extras.putParcelable(EXTRA_RESOLVE_INFO, resolveInfo);
    592             extras.putString(EXTRA_SUMMARY, description);
    593 
    594             final String settingsClassName = info.getSettingsActivityName();
    595             if (!TextUtils.isEmpty(settingsClassName)) {
    596                 extras.putString(EXTRA_SETTINGS_TITLE,
    597                         getString(R.string.accessibility_menu_item_settings));
    598                 extras.putString(EXTRA_SETTINGS_COMPONENT_NAME,
    599                         new ComponentName(packageName, settingsClassName).flattenToString());
    600             }
    601             extras.putParcelable(EXTRA_COMPONENT_NAME, componentName);
    602 
    603             PreferenceCategory prefCategory = downloadedServicesCategory;
    604             // Set the appropriate category if the service comes pre-installed.
    605             if (mPreBundledServiceComponentToCategoryMap.containsKey(componentName)) {
    606                 prefCategory = mPreBundledServiceComponentToCategoryMap.get(componentName);
    607             }
    608             preference.setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX);
    609             prefCategory.addPreference(preference);
    610             mServicePreferenceToPreferenceCategoryMap.put(preference, prefCategory);
    611         }
    612 
    613         // If the user has not installed any additional services, hide the category.
    614         if (downloadedServicesCategory.getPreferenceCount() == 0) {
    615             final PreferenceScreen screen = getPreferenceScreen();
    616             screen.removePreference(downloadedServicesCategory);
    617         }
    618     }
    619 
    620     private void initializePreBundledServicesMapFromArray(String categoryKey, int key) {
    621         String[] services = getResources().getStringArray(key);
    622         PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey);
    623         for (int i = 0; i < services.length; i++) {
    624             ComponentName component = ComponentName.unflattenFromString(services[i]);
    625             mPreBundledServiceComponentToCategoryMap.put(component, category);
    626         }
    627     }
    628 
    629     protected void updateSystemPreferences() {
    630         // Move color inversion and color correction preferences to Display category if device
    631         // supports HWC hardware-accelerated color transform.
    632         if (isColorTransformAccelerated(getContext())) {
    633             PreferenceCategory experimentalCategory =
    634                     mCategoryToPrefCategoryMap.get(CATEGORY_EXPERIMENTAL);
    635             PreferenceCategory displayCategory =
    636                     mCategoryToPrefCategoryMap.get(CATEGORY_DISPLAY);
    637             experimentalCategory.removePreference(mToggleInversionPreference);
    638             experimentalCategory.removePreference(mDisplayDaltonizerPreferenceScreen);
    639             mToggleInversionPreference.setOrder(mToggleLargePointerIconPreference.getOrder());
    640             mDisplayDaltonizerPreferenceScreen.setOrder(mToggleInversionPreference.getOrder());
    641             mToggleInversionPreference.setSummary(R.string.summary_empty);
    642             displayCategory.addPreference(mToggleInversionPreference);
    643             displayCategory.addPreference(mDisplayDaltonizerPreferenceScreen);
    644         }
    645 
    646         // Text contrast.
    647         mToggleHighTextContrastPreference.setChecked(
    648                 Settings.Secure.getInt(getContentResolver(),
    649                         Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0) == 1);
    650 
    651         // If the quick setting is enabled, the preference MUST be enabled.
    652         mToggleInversionPreference.setChecked(Settings.Secure.getInt(getContentResolver(),
    653                 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0) == 1);
    654 
    655         // Power button ends calls.
    656         if (KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER)
    657                 && Utils.isVoiceCapable(getActivity())) {
    658             final int incallPowerBehavior = Settings.Secure.getInt(getContentResolver(),
    659                     Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
    660                     Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT);
    661             final boolean powerButtonEndsCall =
    662                     (incallPowerBehavior == Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP);
    663             mTogglePowerButtonEndsCallPreference.setChecked(powerButtonEndsCall);
    664         }
    665 
    666         // Auto-rotate screen
    667         updateLockScreenRotationCheckbox();
    668 
    669         // Large pointer icon.
    670         mToggleLargePointerIconPreference.setChecked(Settings.Secure.getInt(getContentResolver(),
    671                 Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, 0) != 0);
    672 
    673         updateDisableAnimationsToggle();
    674 
    675         // Master mono
    676         updateMasterMono();
    677 
    678         // Long press timeout.
    679         final int longPressTimeout = Settings.Secure.getInt(getContentResolver(),
    680                 Settings.Secure.LONG_PRESS_TIMEOUT, mLongPressTimeoutDefault);
    681         String value = String.valueOf(longPressTimeout);
    682         mSelectLongPressTimeoutPreference.setValue(value);
    683         mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValueToTitleMap.get(value));
    684 
    685         updateVibrationSummary(mVibrationPreferenceScreen);
    686 
    687         updateFeatureSummary(Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED,
    688                 mCaptioningPreferenceScreen);
    689         updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
    690                 mDisplayDaltonizerPreferenceScreen);
    691 
    692         updateMagnificationSummary(mDisplayMagnificationPreferenceScreen);
    693 
    694         updateFontSizeSummary(mFontSizePreferenceScreen);
    695 
    696         updateAutoclickSummary(mAutoclickPreferenceScreen);
    697 
    698         updateAccessibilityShortcut(mAccessibilityShortcutPreferenceScreen);
    699     }
    700 
    701     private void updateMagnificationSummary(Preference pref) {
    702         final boolean tripleTapEnabled = Settings.Secure.getInt(getContentResolver(),
    703                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1;
    704         final boolean buttonEnabled = Settings.Secure.getInt(getContentResolver(),
    705                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1;
    706 
    707         int summaryResId = 0;
    708         if (!tripleTapEnabled && !buttonEnabled) {
    709             summaryResId = R.string.accessibility_feature_state_off;
    710         } else if (!tripleTapEnabled && buttonEnabled) {
    711             summaryResId = R.string.accessibility_screen_magnification_navbar_title;
    712         } else if (tripleTapEnabled && !buttonEnabled) {
    713             summaryResId = R.string.accessibility_screen_magnification_gestures_title;
    714         } else {
    715             summaryResId = R.string.accessibility_screen_magnification_state_navbar_gesture;
    716         }
    717         pref.setSummary(summaryResId);
    718     }
    719 
    720     private void updateFeatureSummary(String prefKey, Preference pref) {
    721         final boolean enabled = Settings.Secure.getInt(getContentResolver(), prefKey, 0) == 1;
    722         pref.setSummary(enabled ? R.string.accessibility_feature_state_on
    723                 : R.string.accessibility_feature_state_off);
    724     }
    725 
    726     private void updateAutoclickSummary(Preference pref) {
    727         final boolean enabled = Settings.Secure.getInt(
    728                 getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 0) == 1;
    729         if (!enabled) {
    730             pref.setSummary(R.string.accessibility_feature_state_off);
    731             return;
    732         }
    733         int delay = Settings.Secure.getInt(
    734                 getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
    735                 AccessibilityManager.AUTOCLICK_DELAY_DEFAULT);
    736         pref.setSummary(ToggleAutoclickPreferenceFragment.getAutoclickPreferenceSummary(
    737                 getResources(), delay));
    738     }
    739 
    740     private void updateFontSizeSummary(Preference pref) {
    741         final float currentScale = Settings.System.getFloat(getContext().getContentResolver(),
    742                 Settings.System.FONT_SCALE, 1.0f);
    743         final Resources res = getContext().getResources();
    744         final String[] entries = res.getStringArray(R.array.entries_font_size);
    745         final String[] strEntryValues = res.getStringArray(R.array.entryvalues_font_size);
    746         final int index = ToggleFontSizePreferenceFragment.fontSizeValueToIndex(currentScale,
    747                 strEntryValues);
    748         pref.setSummary(entries[index]);
    749     }
    750 
    751     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    752     void updateVibrationSummary(Preference pref) {
    753         final Context context = getContext();
    754         final Vibrator vibrator = context.getSystemService(Vibrator.class);
    755 
    756         final int ringIntensity = Settings.System.getInt(context.getContentResolver(),
    757                 Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
    758                 vibrator.getDefaultNotificationVibrationIntensity());
    759         CharSequence ringIntensityString =
    760                 VibrationIntensityPreferenceController.getIntensityString(context, ringIntensity);
    761 
    762         final int touchIntensity = Settings.System.getInt(context.getContentResolver(),
    763                 Settings.System.HAPTIC_FEEDBACK_INTENSITY,
    764                 vibrator.getDefaultHapticFeedbackIntensity());
    765         CharSequence touchIntensityString =
    766                 VibrationIntensityPreferenceController.getIntensityString(context, touchIntensity);
    767 
    768         if (mVibrationPreferenceScreen == null) {
    769             mVibrationPreferenceScreen = findPreference(VIBRATION_PREFERENCE_SCREEN);
    770         }
    771 
    772         if (ringIntensity == touchIntensity) {
    773             mVibrationPreferenceScreen.setSummary(ringIntensityString);
    774         } else {
    775             mVibrationPreferenceScreen.setSummary(
    776                     getString(R.string.accessibility_vibration_summary,
    777                             ringIntensityString, touchIntensityString));
    778         }
    779     }
    780 
    781     private String getVibrationSummary(Context context, @VibrationIntensity int intensity) {
    782         final boolean supportsMultipleIntensities = context.getResources().getBoolean(
    783                 R.bool.config_vibration_supports_multiple_intensities);
    784         if (supportsMultipleIntensities) {
    785             switch (intensity) {
    786                 case Vibrator.VIBRATION_INTENSITY_OFF:
    787                     return context.getString(R.string.accessibility_vibration_summary_off);
    788                 case Vibrator.VIBRATION_INTENSITY_LOW:
    789                     return context.getString(R.string.accessibility_vibration_summary_low);
    790                 case Vibrator.VIBRATION_INTENSITY_MEDIUM:
    791                     return context.getString(R.string.accessibility_vibration_summary_medium);
    792                 case Vibrator.VIBRATION_INTENSITY_HIGH:
    793                     return context.getString(R.string.accessibility_vibration_summary_high);
    794                 default:
    795                     return "";
    796             }
    797         } else {
    798             if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
    799                 return context.getString(R.string.switch_on_text);
    800             } else {
    801                 return context.getString(R.string.switch_off_text);
    802             }
    803         }
    804     }
    805 
    806     private void updateLockScreenRotationCheckbox() {
    807         Context context = getActivity();
    808         if (context != null) {
    809             mToggleLockScreenRotationPreference.setChecked(
    810                     !RotationPolicy.isRotationLocked(context));
    811         }
    812     }
    813 
    814     private void updateDisableAnimationsToggle() {
    815         boolean allAnimationsDisabled = true;
    816         for (String animationSetting : TOGGLE_ANIMATION_TARGETS) {
    817             if (!TextUtils.equals(
    818                     Settings.Global.getString(getContentResolver(), animationSetting),
    819                     ANIMATION_OFF_VALUE)) {
    820                 allAnimationsDisabled = false;
    821                 break;
    822             }
    823         }
    824         mToggleDisableAnimationsPreference.setChecked(allAnimationsDisabled);
    825     }
    826 
    827     private void updateMasterMono() {
    828         final boolean masterMono = Settings.System.getIntForUser(
    829                 getContentResolver(), Settings.System.MASTER_MONO,
    830                 0 /* default */, UserHandle.USER_CURRENT) == 1;
    831         mToggleMasterMonoPreference.setChecked(masterMono);
    832     }
    833 
    834     private void updateAccessibilityShortcut(Preference preference) {
    835         if (AccessibilityManager.getInstance(getActivity())
    836                 .getInstalledAccessibilityServiceList().isEmpty()) {
    837             mAccessibilityShortcutPreferenceScreen
    838                     .setSummary(getString(R.string.accessibility_no_services_installed));
    839             mAccessibilityShortcutPreferenceScreen.setEnabled(false);
    840         } else {
    841             mAccessibilityShortcutPreferenceScreen.setEnabled(true);
    842             boolean shortcutEnabled =
    843                     AccessibilityUtils.isShortcutEnabled(getContext(), UserHandle.myUserId());
    844             CharSequence summary = shortcutEnabled
    845                     ? AccessibilityShortcutPreferenceFragment.getServiceName(getContext())
    846                     : getString(R.string.accessibility_feature_state_off);
    847             mAccessibilityShortcutPreferenceScreen.setSummary(summary);
    848         }
    849     }
    850 
    851     private static void configureMagnificationPreferenceIfNeeded(Preference preference) {
    852         // Some devices support only a single magnification mode. In these cases, we redirect to
    853         // the magnification mode's UI directly, rather than showing a PreferenceScreen with a
    854         // single list item.
    855         final Context context = preference.getContext();
    856         if (!MagnificationPreferenceFragment.isApplicable(context.getResources())) {
    857             preference.setFragment(ToggleScreenMagnificationPreferenceFragment.class.getName());
    858             final Bundle extras = preference.getExtras();
    859             MagnificationGesturesPreferenceController
    860                     .populateMagnificationGesturesPreferenceExtras(extras, context);
    861         }
    862     }
    863 
    864     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
    865             new BaseSearchIndexProvider() {
    866 
    867                 public static final String KEY_DISPLAY_SIZE = "accessibility_settings_screen_zoom";
    868 
    869                 @Override
    870                 public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
    871                         boolean enabled) {
    872                     List<SearchIndexableResource> indexables = new ArrayList<>();
    873                     SearchIndexableResource indexable = new SearchIndexableResource(context);
    874                     indexable.xmlResId = R.xml.accessibility_settings;
    875                     indexables.add(indexable);
    876                     return indexables;
    877                 }
    878 
    879                 @Override
    880                 public List<String> getNonIndexableKeys(Context context) {
    881                     List<String> keys = super.getNonIndexableKeys(context);
    882                     // Duplicates in Display
    883                     keys.add(FONT_SIZE_PREFERENCE_SCREEN);
    884                     keys.add(KEY_DISPLAY_SIZE);
    885 
    886                     // Duplicates in Language & Input
    887                     keys.add(TTS_SETTINGS_PREFERENCE);
    888 
    889                     return keys;
    890                 }
    891             };
    892 }
    893