• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
isColorTransformAccelerated(Context context)235     public static boolean isColorTransformAccelerated(Context context) {
236         return context.getResources()
237                 .getBoolean(com.android.internal.R.bool.config_setColorTransformAccelerated);
238     }
239 
AccessibilitySettings()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
getMetricsCategory()257     public int getMetricsCategory() {
258         return MetricsEvent.ACCESSIBILITY;
259     }
260 
261     @Override
getHelpResource()262     public int getHelpResource() {
263         return R.string.help_uri_accessibility;
264     }
265 
266     @Override
onCreate(Bundle icicle)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
onResume()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
onPause()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
onPreferenceChange(Preference preference, Object newValue)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 
handleLongPressTimeoutPreferenceChange(String stringValue)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 
handleToggleInversionPreferenceChange(boolean checked)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
onPreferenceTreeClick(Preference preference)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 
getServiceSummary(Context context, AccessibilityServiceInfo info, boolean serviceEnabled)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 
handleToggleTextContrastPreferenceClick()362     private void handleToggleTextContrastPreferenceClick() {
363         Settings.Secure.putInt(getContentResolver(),
364                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
365                 (mToggleHighTextContrastPreference.isChecked() ? 1 : 0));
366     }
367 
handleTogglePowerButtonEndsCallPreferenceClick()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 
handleLockScreenRotationPreferenceClick()376     private void handleLockScreenRotationPreferenceClick() {
377         RotationPolicy.setRotationLockForAccessibility(getActivity(),
378                 !mToggleLockScreenRotationPreference.isChecked());
379     }
380 
handleToggleLargePointerIconPreferenceClick()381     private void handleToggleLargePointerIconPreferenceClick() {
382         Settings.Secure.putInt(getContentResolver(),
383                 Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
384                 mToggleLargePointerIconPreference.isChecked() ? 1 : 0);
385     }
386 
handleToggleDisableAnimations()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 
handleToggleMasterMonoPreferenceClick()395     private void handleToggleMasterMonoPreferenceClick() {
396         Settings.System.putIntForUser(getContentResolver(), Settings.System.MASTER_MONO,
397                 mToggleMasterMonoPreference.isChecked() ? 1 : 0, UserHandle.USER_CURRENT);
398     }
399 
initializeAllPreferences()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 
updateAllPreferences()482     private void updateAllPreferences() {
483         updateSystemPreferences();
484         updateServicePreferences();
485     }
486 
updateServicePreferences()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 
initializePreBundledServicesMapFromArray(String categoryKey, int key)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 
updateSystemPreferences()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 
updateMagnificationSummary(Preference pref)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 
updateFeatureSummary(String prefKey, Preference pref)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 
updateAutoclickSummary(Preference pref)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 
updateFontSizeSummary(Preference pref)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)
updateVibrationSummary(Preference pref)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 
getVibrationSummary(Context context, @VibrationIntensity int intensity)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 
updateLockScreenRotationCheckbox()806     private void updateLockScreenRotationCheckbox() {
807         Context context = getActivity();
808         if (context != null) {
809             mToggleLockScreenRotationPreference.setChecked(
810                     !RotationPolicy.isRotationLocked(context));
811         }
812     }
813 
updateDisableAnimationsToggle()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 
updateMasterMono()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 
updateAccessibilityShortcut(Preference preference)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 
configureMagnificationPreferenceIfNeeded(Preference preference)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