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.ActivityManagerNative;
21import android.app.admin.DevicePolicyManager;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.content.pm.ServiceInfo;
27import android.content.res.Configuration;
28import android.net.Uri;
29import android.os.Bundle;
30import android.os.Handler;
31import android.os.RemoteException;
32import android.os.UserHandle;
33import android.preference.CheckBoxPreference;
34import android.preference.ListPreference;
35import android.preference.Preference;
36import android.preference.PreferenceCategory;
37import android.preference.PreferenceScreen;
38import android.preference.SwitchPreference;
39import android.provider.SearchIndexableResource;
40import android.provider.Settings;
41import android.text.TextUtils;
42import android.text.TextUtils.SimpleStringSplitter;
43import android.view.KeyCharacterMap;
44import android.view.KeyEvent;
45import android.view.View;
46import android.view.accessibility.AccessibilityManager;
47import android.widget.TextView;
48
49import com.android.internal.content.PackageMonitor;
50import com.android.internal.view.RotationPolicy;
51import com.android.internal.view.RotationPolicy.RotationPolicyListener;
52import com.android.settings.DialogCreatable;
53import com.android.settings.R;
54import com.android.settings.SettingsPreferenceFragment;
55import com.android.settings.Utils;
56import com.android.settings.search.BaseSearchIndexProvider;
57import com.android.settings.search.Indexable;
58import com.android.settings.search.SearchIndexableRaw;
59
60import java.util.ArrayList;
61import java.util.HashMap;
62import java.util.HashSet;
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 DialogCreatable,
71        Preference.OnPreferenceChangeListener, Indexable {
72
73    private static final float LARGE_FONT_SCALE = 1.3f;
74
75    static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':';
76
77    // Preference categories
78    private static final String SERVICES_CATEGORY = "services_category";
79    private static final String SYSTEM_CATEGORY = "system_category";
80
81    // Preferences
82    private static final String TOGGLE_LARGE_TEXT_PREFERENCE =
83            "toggle_large_text_preference";
84    private static final String TOGGLE_HIGH_TEXT_CONTRAST_PREFERENCE =
85            "toggle_high_text_contrast_preference";
86    private static final String TOGGLE_INVERSION_PREFERENCE =
87            "toggle_inversion_preference";
88    private static final String TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE =
89            "toggle_power_button_ends_call_preference";
90    private static final String TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE =
91            "toggle_lock_screen_rotation_preference";
92    private static final String TOGGLE_SPEAK_PASSWORD_PREFERENCE =
93            "toggle_speak_password_preference";
94    private static final String SELECT_LONG_PRESS_TIMEOUT_PREFERENCE =
95            "select_long_press_timeout_preference";
96    private static final String ENABLE_ACCESSIBILITY_GESTURE_PREFERENCE_SCREEN =
97            "enable_global_gesture_preference_screen";
98    private static final String CAPTIONING_PREFERENCE_SCREEN =
99            "captioning_preference_screen";
100    private static final String DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN =
101            "screen_magnification_preference_screen";
102    private static final String DISPLAY_DALTONIZER_PREFERENCE_SCREEN =
103            "daltonizer_preference_screen";
104
105    // Extras passed to sub-fragments.
106    static final String EXTRA_PREFERENCE_KEY = "preference_key";
107    static final String EXTRA_CHECKED = "checked";
108    static final String EXTRA_TITLE = "title";
109    static final String EXTRA_SUMMARY = "summary";
110    static final String EXTRA_SETTINGS_TITLE = "settings_title";
111    static final String EXTRA_COMPONENT_NAME = "component_name";
112    static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name";
113
114    // Timeout before we update the services if packages are added/removed
115    // since the AccessibilityManagerService has to do that processing first
116    // to generate the AccessibilityServiceInfo we need for proper
117    // presentation.
118    private static final long DELAY_UPDATE_SERVICES_MILLIS = 1000;
119
120    // Auxiliary members.
121    final static SimpleStringSplitter sStringColonSplitter =
122            new SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
123
124    static final Set<ComponentName> sInstalledServices = new HashSet<ComponentName>();
125
126    private final Map<String, String> mLongPressTimeoutValuetoTitleMap =
127            new HashMap<String, String>();
128
129    private final Configuration mCurConfig = new Configuration();
130
131    private final Handler mHandler = new Handler();
132
133    private final Runnable mUpdateRunnable = new Runnable() {
134        @Override
135        public void run() {
136            loadInstalledServices();
137            updateServicesPreferences();
138        }
139    };
140
141    private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() {
142        @Override
143        public void onPackageAdded(String packageName, int uid) {
144            sendUpdate();
145        }
146
147        @Override
148        public void onPackageAppeared(String packageName, int reason) {
149            sendUpdate();
150        }
151
152        @Override
153        public void onPackageDisappeared(String packageName, int reason) {
154            sendUpdate();
155        }
156
157        @Override
158        public void onPackageRemoved(String packageName, int uid) {
159            sendUpdate();
160        }
161
162        private void sendUpdate() {
163            mHandler.postDelayed(mUpdateRunnable, DELAY_UPDATE_SERVICES_MILLIS);
164        }
165    };
166
167    private final SettingsContentObserver mSettingsContentObserver =
168            new SettingsContentObserver(mHandler) {
169                @Override
170                public void onChange(boolean selfChange, Uri uri) {
171                    loadInstalledServices();
172                    updateServicesPreferences();
173                }
174            };
175
176    private final RotationPolicyListener mRotationPolicyListener = new RotationPolicyListener() {
177        @Override
178        public void onChange() {
179            updateLockScreenRotationCheckbox();
180        }
181    };
182
183    // Preference controls.
184    private PreferenceCategory mServicesCategory;
185    private PreferenceCategory mSystemsCategory;
186
187    private CheckBoxPreference mToggleLargeTextPreference;
188    private CheckBoxPreference mToggleHighTextContrastPreference;
189    private CheckBoxPreference mTogglePowerButtonEndsCallPreference;
190    private CheckBoxPreference mToggleLockScreenRotationPreference;
191    private CheckBoxPreference mToggleSpeakPasswordPreference;
192    private ListPreference mSelectLongPressTimeoutPreference;
193    private Preference mNoServicesMessagePreference;
194    private PreferenceScreen mCaptioningPreferenceScreen;
195    private PreferenceScreen mDisplayMagnificationPreferenceScreen;
196    private PreferenceScreen mGlobalGesturePreferenceScreen;
197    private PreferenceScreen mDisplayDaltonizerPreferenceScreen;
198    private SwitchPreference mToggleInversionPreference;
199
200    private int mLongPressTimeoutDefault;
201
202    private DevicePolicyManager mDpm;
203
204    @Override
205    public void onCreate(Bundle icicle) {
206        super.onCreate(icicle);
207        addPreferencesFromResource(R.xml.accessibility_settings);
208        initializeAllPreferences();
209        mDpm = (DevicePolicyManager) (getActivity()
210                .getSystemService(Context.DEVICE_POLICY_SERVICE));
211    }
212
213    @Override
214    public void onResume() {
215        super.onResume();
216        loadInstalledServices();
217        updateAllPreferences();
218
219        mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false);
220        mSettingsContentObserver.register(getContentResolver());
221        if (RotationPolicy.isRotationSupported(getActivity())) {
222            RotationPolicy.registerRotationPolicyListener(getActivity(),
223                    mRotationPolicyListener);
224        }
225    }
226
227    @Override
228    public void onPause() {
229        mSettingsPackageMonitor.unregister();
230        mSettingsContentObserver.unregister(getContentResolver());
231        if (RotationPolicy.isRotationSupported(getActivity())) {
232            RotationPolicy.unregisterRotationPolicyListener(getActivity(),
233                    mRotationPolicyListener);
234        }
235        super.onPause();
236    }
237
238    @Override
239    public boolean onPreferenceChange(Preference preference, Object newValue) {
240        if (mSelectLongPressTimeoutPreference == preference) {
241            handleLongPressTimeoutPreferenceChange((String) newValue);
242            return true;
243        } else if (mToggleInversionPreference == preference) {
244            handleToggleInversionPreferenceChange((Boolean) newValue);
245            return true;
246        }
247        return false;
248    }
249
250    private void handleLongPressTimeoutPreferenceChange(String stringValue) {
251        Settings.Secure.putInt(getContentResolver(),
252                Settings.Secure.LONG_PRESS_TIMEOUT, Integer.parseInt(stringValue));
253        mSelectLongPressTimeoutPreference.setSummary(
254                mLongPressTimeoutValuetoTitleMap.get(stringValue));
255    }
256
257    private void handleToggleInversionPreferenceChange(boolean checked) {
258        Settings.Secure.putInt(getContentResolver(),
259                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, (checked ? 1 : 0));
260    }
261
262    @Override
263    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
264        if (mToggleLargeTextPreference == preference) {
265            handleToggleLargeTextPreferenceClick();
266            return true;
267        } else if (mToggleHighTextContrastPreference == preference) {
268            handleToggleTextContrastPreferenceClick();
269            return true;
270        } else if (mTogglePowerButtonEndsCallPreference == preference) {
271            handleTogglePowerButtonEndsCallPreferenceClick();
272            return true;
273        } else if (mToggleLockScreenRotationPreference == preference) {
274            handleLockScreenRotationPreferenceClick();
275            return true;
276        } else if (mToggleSpeakPasswordPreference == preference) {
277            handleToggleSpeakPasswordPreferenceClick();
278            return true;
279        } else if (mGlobalGesturePreferenceScreen == preference) {
280            handleToggleEnableAccessibilityGesturePreferenceClick();
281            return true;
282        } else if (mDisplayMagnificationPreferenceScreen == preference) {
283            handleDisplayMagnificationPreferenceScreenClick();
284            return true;
285        }
286        return super.onPreferenceTreeClick(preferenceScreen, preference);
287    }
288
289    private void handleToggleLargeTextPreferenceClick() {
290        try {
291            mCurConfig.fontScale = mToggleLargeTextPreference.isChecked() ? LARGE_FONT_SCALE : 1;
292            ActivityManagerNative.getDefault().updatePersistentConfiguration(mCurConfig);
293        } catch (RemoteException re) {
294            /* ignore */
295        }
296    }
297
298    private void handleToggleTextContrastPreferenceClick() {
299        Settings.Secure.putInt(getContentResolver(),
300                Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
301                (mToggleHighTextContrastPreference.isChecked() ? 1 : 0));
302    }
303
304    private void handleTogglePowerButtonEndsCallPreferenceClick() {
305        Settings.Secure.putInt(getContentResolver(),
306                Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
307                (mTogglePowerButtonEndsCallPreference.isChecked()
308                        ? Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP
309                        : Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF));
310    }
311
312    private void handleLockScreenRotationPreferenceClick() {
313        RotationPolicy.setRotationLockForAccessibility(getActivity(),
314                !mToggleLockScreenRotationPreference.isChecked());
315    }
316
317    private void handleToggleSpeakPasswordPreferenceClick() {
318        Settings.Secure.putInt(getContentResolver(),
319                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD,
320                mToggleSpeakPasswordPreference.isChecked() ? 1 : 0);
321    }
322
323    private void handleToggleEnableAccessibilityGesturePreferenceClick() {
324        Bundle extras = mGlobalGesturePreferenceScreen.getExtras();
325        extras.putString(EXTRA_TITLE, getString(
326                R.string.accessibility_global_gesture_preference_title));
327        extras.putString(EXTRA_SUMMARY, getString(
328                R.string.accessibility_global_gesture_preference_description));
329        extras.putBoolean(EXTRA_CHECKED, Settings.Global.getInt(getContentResolver(),
330                Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1);
331        super.onPreferenceTreeClick(mGlobalGesturePreferenceScreen,
332                mGlobalGesturePreferenceScreen);
333    }
334
335    private void handleDisplayMagnificationPreferenceScreenClick() {
336        Bundle extras = mDisplayMagnificationPreferenceScreen.getExtras();
337        extras.putString(EXTRA_TITLE, getString(
338                R.string.accessibility_screen_magnification_title));
339        extras.putCharSequence(EXTRA_SUMMARY, getActivity().getResources().getText(
340                R.string.accessibility_screen_magnification_summary));
341        extras.putBoolean(EXTRA_CHECKED, Settings.Secure.getInt(getContentResolver(),
342                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1);
343        super.onPreferenceTreeClick(mDisplayMagnificationPreferenceScreen,
344                mDisplayMagnificationPreferenceScreen);
345    }
346
347    private void initializeAllPreferences() {
348        mServicesCategory = (PreferenceCategory) findPreference(SERVICES_CATEGORY);
349        mSystemsCategory = (PreferenceCategory) findPreference(SYSTEM_CATEGORY);
350
351        // Large text.
352        mToggleLargeTextPreference =
353                (CheckBoxPreference) findPreference(TOGGLE_LARGE_TEXT_PREFERENCE);
354
355        // Text contrast.
356        mToggleHighTextContrastPreference =
357                (CheckBoxPreference) findPreference(TOGGLE_HIGH_TEXT_CONTRAST_PREFERENCE);
358
359        // Display inversion.
360        mToggleInversionPreference = (SwitchPreference) findPreference(TOGGLE_INVERSION_PREFERENCE);
361        mToggleInversionPreference.setOnPreferenceChangeListener(this);
362
363        // Power button ends calls.
364        mTogglePowerButtonEndsCallPreference =
365                (CheckBoxPreference) findPreference(TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE);
366        if (!KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER)
367                || !Utils.isVoiceCapable(getActivity())) {
368            mSystemsCategory.removePreference(mTogglePowerButtonEndsCallPreference);
369        }
370
371        // Lock screen rotation.
372        mToggleLockScreenRotationPreference =
373                (CheckBoxPreference) findPreference(TOGGLE_LOCK_SCREEN_ROTATION_PREFERENCE);
374        if (!RotationPolicy.isRotationSupported(getActivity())) {
375            mSystemsCategory.removePreference(mToggleLockScreenRotationPreference);
376        }
377
378        // Speak passwords.
379        mToggleSpeakPasswordPreference =
380                (CheckBoxPreference) findPreference(TOGGLE_SPEAK_PASSWORD_PREFERENCE);
381
382        // Long press timeout.
383        mSelectLongPressTimeoutPreference =
384                (ListPreference) findPreference(SELECT_LONG_PRESS_TIMEOUT_PREFERENCE);
385        mSelectLongPressTimeoutPreference.setOnPreferenceChangeListener(this);
386        if (mLongPressTimeoutValuetoTitleMap.size() == 0) {
387            String[] timeoutValues = getResources().getStringArray(
388                    R.array.long_press_timeout_selector_values);
389            mLongPressTimeoutDefault = Integer.parseInt(timeoutValues[0]);
390            String[] timeoutTitles = getResources().getStringArray(
391                    R.array.long_press_timeout_selector_titles);
392            final int timeoutValueCount = timeoutValues.length;
393            for (int i = 0; i < timeoutValueCount; i++) {
394                mLongPressTimeoutValuetoTitleMap.put(timeoutValues[i], timeoutTitles[i]);
395            }
396        }
397
398        // Captioning.
399        mCaptioningPreferenceScreen = (PreferenceScreen) findPreference(
400                CAPTIONING_PREFERENCE_SCREEN);
401
402        // Display magnification.
403        mDisplayMagnificationPreferenceScreen = (PreferenceScreen) findPreference(
404                DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN);
405
406        // Display color adjustments.
407        mDisplayDaltonizerPreferenceScreen = (PreferenceScreen) findPreference(
408                DISPLAY_DALTONIZER_PREFERENCE_SCREEN);
409
410        // Global gesture.
411        mGlobalGesturePreferenceScreen =
412                (PreferenceScreen) findPreference(ENABLE_ACCESSIBILITY_GESTURE_PREFERENCE_SCREEN);
413        final int longPressOnPowerBehavior = getActivity().getResources().getInteger(
414                com.android.internal.R.integer.config_longPressOnPowerBehavior);
415        final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
416        if (!KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER)
417                || longPressOnPowerBehavior != LONG_PRESS_POWER_GLOBAL_ACTIONS) {
418            // Remove accessibility shortcut if power key is not present
419            // nor long press power does not show global actions menu.
420            mSystemsCategory.removePreference(mGlobalGesturePreferenceScreen);
421        }
422    }
423
424    private void updateAllPreferences() {
425        updateServicesPreferences();
426        updateSystemPreferences();
427    }
428
429    private void updateServicesPreferences() {
430        // Since services category is auto generated we have to do a pass
431        // to generate it since services can come and go and then based on
432        // the global accessibility state to decided whether it is enabled.
433
434        // Generate.
435        mServicesCategory.removeAll();
436
437        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getActivity());
438
439        List<AccessibilityServiceInfo> installedServices =
440                accessibilityManager.getInstalledAccessibilityServiceList();
441        Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(
442                getActivity());
443        List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
444                UserHandle.myUserId());
445        final boolean accessibilityEnabled = Settings.Secure.getInt(getContentResolver(),
446                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
447
448        for (int i = 0, count = installedServices.size(); i < count; ++i) {
449            AccessibilityServiceInfo info = installedServices.get(i);
450
451            PreferenceScreen preference = getPreferenceManager().createPreferenceScreen(
452                    getActivity());
453            String title = info.getResolveInfo().loadLabel(getPackageManager()).toString();
454
455            ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
456            ComponentName componentName = new ComponentName(serviceInfo.packageName,
457                    serviceInfo.name);
458
459            preference.setKey(componentName.flattenToString());
460
461            preference.setTitle(title);
462            final boolean serviceEnabled = accessibilityEnabled
463                    && enabledServices.contains(componentName);
464            String serviceEnabledString;
465            if (serviceEnabled) {
466                serviceEnabledString = getString(R.string.accessibility_feature_state_on);
467            } else {
468                serviceEnabledString = getString(R.string.accessibility_feature_state_off);
469            }
470
471            // Disable all accessibility services that are not permitted.
472            String packageName = serviceInfo.packageName;
473            boolean serviceAllowed =
474                    permittedServices == null || permittedServices.contains(packageName);
475            preference.setEnabled(serviceAllowed || serviceEnabled);
476
477            String summaryString;
478            if (serviceAllowed) {
479                summaryString = serviceEnabledString;
480            } else  {
481                summaryString = getString(R.string.accessibility_feature_or_input_method_not_allowed);
482            }
483            preference.setSummary(summaryString);
484
485            preference.setOrder(i);
486            preference.setFragment(ToggleAccessibilityServicePreferenceFragment.class.getName());
487            preference.setPersistent(true);
488
489            Bundle extras = preference.getExtras();
490            extras.putString(EXTRA_PREFERENCE_KEY, preference.getKey());
491            extras.putBoolean(EXTRA_CHECKED, serviceEnabled);
492            extras.putString(EXTRA_TITLE, title);
493
494            String description = info.loadDescription(getPackageManager());
495            if (TextUtils.isEmpty(description)) {
496                description = getString(R.string.accessibility_service_default_description);
497            }
498            extras.putString(EXTRA_SUMMARY, description);
499
500            String settingsClassName = info.getSettingsActivityName();
501            if (!TextUtils.isEmpty(settingsClassName)) {
502                extras.putString(EXTRA_SETTINGS_TITLE,
503                        getString(R.string.accessibility_menu_item_settings));
504                extras.putString(EXTRA_SETTINGS_COMPONENT_NAME,
505                        new ComponentName(info.getResolveInfo().serviceInfo.packageName,
506                                settingsClassName).flattenToString());
507            }
508
509            extras.putParcelable(EXTRA_COMPONENT_NAME, componentName);
510
511            mServicesCategory.addPreference(preference);
512        }
513
514        if (mServicesCategory.getPreferenceCount() == 0) {
515            if (mNoServicesMessagePreference == null) {
516                mNoServicesMessagePreference = new Preference(getActivity());
517                mNoServicesMessagePreference.setPersistent(false);
518                mNoServicesMessagePreference.setLayoutResource(
519                        R.layout.text_description_preference);
520                mNoServicesMessagePreference.setSelectable(false);
521                mNoServicesMessagePreference.setSummary(
522                        getString(R.string.accessibility_no_services_installed));
523            }
524            mServicesCategory.addPreference(mNoServicesMessagePreference);
525        }
526    }
527
528    private void updateSystemPreferences() {
529        // Large text.
530        try {
531            mCurConfig.updateFrom(ActivityManagerNative.getDefault().getConfiguration());
532        } catch (RemoteException re) {
533            /* ignore */
534        }
535        mToggleLargeTextPreference.setChecked(mCurConfig.fontScale == LARGE_FONT_SCALE);
536
537        mToggleHighTextContrastPreference.setChecked(
538                Settings.Secure.getInt(getContentResolver(),
539                        Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0) == 1);
540
541        // If the quick setting is enabled, the preference MUST be enabled.
542        mToggleInversionPreference.setChecked(Settings.Secure.getInt(getContentResolver(),
543                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0) == 1);
544
545        // Power button ends calls.
546        if (KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER)
547                && Utils.isVoiceCapable(getActivity())) {
548            final int incallPowerBehavior = Settings.Secure.getInt(getContentResolver(),
549                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
550                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT);
551            final boolean powerButtonEndsCall =
552                    (incallPowerBehavior == Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP);
553            mTogglePowerButtonEndsCallPreference.setChecked(powerButtonEndsCall);
554        }
555
556        // Auto-rotate screen
557        updateLockScreenRotationCheckbox();
558
559        // Speak passwords.
560        final boolean speakPasswordEnabled = Settings.Secure.getInt(getContentResolver(),
561                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0;
562        mToggleSpeakPasswordPreference.setChecked(speakPasswordEnabled);
563
564        // Long press timeout.
565        final int longPressTimeout = Settings.Secure.getInt(getContentResolver(),
566                Settings.Secure.LONG_PRESS_TIMEOUT, mLongPressTimeoutDefault);
567        String value = String.valueOf(longPressTimeout);
568        mSelectLongPressTimeoutPreference.setValue(value);
569        mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValuetoTitleMap.get(value));
570
571        updateFeatureSummary(Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED,
572                mCaptioningPreferenceScreen);
573        updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
574                mDisplayMagnificationPreferenceScreen);
575        updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
576                mDisplayDaltonizerPreferenceScreen);
577
578        // Global gesture
579        final boolean globalGestureEnabled = Settings.Global.getInt(getContentResolver(),
580                Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED, 0) == 1;
581        if (globalGestureEnabled) {
582            mGlobalGesturePreferenceScreen.setSummary(
583                    R.string.accessibility_global_gesture_preference_summary_on);
584        } else {
585            mGlobalGesturePreferenceScreen.setSummary(
586                    R.string.accessibility_global_gesture_preference_summary_off);
587        }
588    }
589
590    private void updateFeatureSummary(String prefKey, Preference pref) {
591        final boolean enabled = Settings.Secure.getInt(getContentResolver(), prefKey, 0) == 1;
592        pref.setSummary(enabled ? R.string.accessibility_feature_state_on
593                : R.string.accessibility_feature_state_off);
594    }
595
596    private void updateLockScreenRotationCheckbox() {
597        Context context = getActivity();
598        if (context != null) {
599            mToggleLockScreenRotationPreference.setChecked(
600                    !RotationPolicy.isRotationLocked(context));
601        }
602    }
603
604    private void loadInstalledServices() {
605        Set<ComponentName> installedServices = sInstalledServices;
606        installedServices.clear();
607
608        List<AccessibilityServiceInfo> installedServiceInfos =
609                AccessibilityManager.getInstance(getActivity())
610                        .getInstalledAccessibilityServiceList();
611        if (installedServiceInfos == null) {
612            return;
613        }
614
615        final int installedServiceInfoCount = installedServiceInfos.size();
616        for (int i = 0; i < installedServiceInfoCount; i++) {
617            ResolveInfo resolveInfo = installedServiceInfos.get(i).getResolveInfo();
618            ComponentName installedService = new ComponentName(
619                    resolveInfo.serviceInfo.packageName,
620                    resolveInfo.serviceInfo.name);
621            installedServices.add(installedService);
622        }
623    }
624
625    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
626            new BaseSearchIndexProvider() {
627        @Override
628        public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
629            List<SearchIndexableRaw> indexables = new ArrayList<SearchIndexableRaw>();
630
631            PackageManager packageManager = context.getPackageManager();
632            AccessibilityManager accessibilityManager = (AccessibilityManager)
633                    context.getSystemService(Context.ACCESSIBILITY_SERVICE);
634
635            String screenTitle = context.getResources().getString(
636                    R.string.accessibility_services_title);
637
638            // Indexing all services, regardless if enabled.
639            List<AccessibilityServiceInfo> services = accessibilityManager
640                    .getInstalledAccessibilityServiceList();
641            final int serviceCount = services.size();
642            for (int i = 0; i < serviceCount; i++) {
643                AccessibilityServiceInfo service = services.get(i);
644                if (service == null || service.getResolveInfo() == null) {
645                    continue;
646                }
647
648                ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
649                ComponentName componentName = new ComponentName(serviceInfo.packageName,
650                        serviceInfo.name);
651
652                SearchIndexableRaw indexable = new SearchIndexableRaw(context);
653                indexable.key = componentName.flattenToString();
654                indexable.title = service.getResolveInfo().loadLabel(packageManager).toString();
655                indexable.summaryOn = context.getString(R.string.accessibility_feature_state_on);
656                indexable.summaryOff = context.getString(R.string.accessibility_feature_state_off);
657                indexable.screenTitle = screenTitle;
658                indexables.add(indexable);
659            }
660
661            return indexables;
662        }
663
664        @Override
665        public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
666               boolean enabled) {
667            List<SearchIndexableResource> indexables = new ArrayList<SearchIndexableResource>();
668            SearchIndexableResource indexable = new SearchIndexableResource(context);
669            indexable.xmlResId = R.xml.accessibility_settings;
670            indexables.add(indexable);
671            return indexables;
672        }
673    };
674}
675