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