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
17package com.android.settings.accessibility;
18
19import android.accessibilityservice.AccessibilityServiceInfo;
20import android.app.admin.DevicePolicyManager;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.pm.PackageManager;
24import android.content.pm.ServiceInfo;
25import android.content.res.Resources;
26import android.graphics.drawable.Drawable;
27import android.net.Uri;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.UserHandle;
31import android.provider.SearchIndexableResource;
32import android.provider.Settings;
33import android.support.v14.preference.SwitchPreference;
34import android.support.v4.content.ContextCompat;
35import android.support.v7.preference.ListPreference;
36import android.support.v7.preference.Preference;
37import android.support.v7.preference.PreferenceCategory;
38import android.support.v7.preference.PreferenceScreen;
39import android.text.TextUtils;
40import android.util.ArrayMap;
41import android.view.KeyCharacterMap;
42import android.view.KeyEvent;
43import android.view.accessibility.AccessibilityManager;
44
45import com.android.internal.content.PackageMonitor;
46import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
47import com.android.internal.view.RotationPolicy;
48import com.android.internal.view.RotationPolicy.RotationPolicyListener;
49import com.android.settings.DisplaySettings;
50import com.android.settings.R;
51import com.android.settings.SettingsPreferenceFragment;
52import com.android.settings.Utils;
53import com.android.settings.search.BaseSearchIndexProvider;
54import com.android.settings.search.Indexable;
55import com.android.settings.search.SearchIndexableRaw;
56import com.android.settingslib.RestrictedLockUtils;
57import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
58import com.android.settingslib.RestrictedPreference;
59import com.android.settingslib.accessibility.AccessibilityUtils;
60
61import java.util.ArrayList;
62import java.util.HashMap;
63import java.util.List;
64import java.util.Map;
65import java.util.Set;
66
67/**
68 * Activity with the accessibility settings.
69 */
70public 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
215    public int getMetricsCategory() {
216        return MetricsEvent.ACCESSIBILITY;
217    }
218
219    @Override
220    protected int getHelpResource() {
221        return R.string.help_uri_accessibility;
222    }
223
224    @Override
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
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
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
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
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
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
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
302    private void handleToggleTextContrastPreferenceClick() {
303        Settings.Secure.putInt(getContentResolver(),
304                Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
305                (mToggleHighTextContrastPreference.isChecked() ? 1 : 0));
306    }
307
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
316    private void handleLockScreenRotationPreferenceClick() {
317        RotationPolicy.setRotationLockForAccessibility(getActivity(),
318                !mToggleLockScreenRotationPreference.isChecked());
319    }
320
321    private void handleToggleLargePointerIconPreferenceClick() {
322        Settings.Secure.putInt(getContentResolver(),
323                Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
324                mToggleLargePointerIconPreference.isChecked() ? 1 : 0);
325    }
326
327    private void handleToggleMasterMonoPreferenceClick() {
328        Settings.System.putIntForUser(getContentResolver(), Settings.System.MASTER_MONO,
329                mToggleMasterMonoPreference.isChecked() ? 1 : 0, UserHandle.USER_CURRENT);
330    }
331
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
408    private void updateAllPreferences() {
409        updateSystemPreferences();
410        updateServicePreferences();
411    }
412
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
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
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
621    private boolean isColorTransformAccelerated(Context context) {
622        return context.getResources()
623                .getBoolean(com.android.internal.R.bool.config_setColorTransformAccelerated);
624    }
625
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
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
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
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
676    private void updateLockScreenRotationCheckbox() {
677        Context context = getActivity();
678        if (context != null) {
679            mToggleLockScreenRotationPreference.setChecked(
680                    !RotationPolicy.isRotationLocked(context));
681        }
682    }
683
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
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
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