OSDN Git Service

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