Accessibility feature - settings (replacing 704 and merging with the latest Donut)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6e3bfde..76bb702 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -249,8 +249,15 @@
             </intent-filter>
         </activity>
 
-
-
+        <activity android:name="AccessibilitySettings" android:label="@string/accessibility_settings_title"
+                android:theme="@android:style/Theme.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <action android:name="ACCESSIBILITY_FEEDBACK_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE_LAUNCH" />
+            </intent-filter>
+        </activity>
 
         <!-- Second and third-level settings -->
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f4a6846..5b04e51 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1572,6 +1572,33 @@
     <string name="launch_count_label">Count</string>
     <!-- label for usage time -->
     <string name="usage_time_label">Usage time</string>
+
+    <!-- Accessibility settings -->
+
+    <!-- Settings title for accessibility settings -->
+    <string name="accessibility_settings_title">Accessibility</string>
+    <!-- Settings summary for accessibility settings -->
+    <string name="accessibility_settings_summary">Manage accessibility options</string>
+    <!-- Setting Checkbox title for enabling accessibility -->
+    <string name="toggle_accessibility_title">Accessibility</string>
+    <!-- Setting Checkbox summary for enabling accessiblity - enable -->
+    <string name="enable_accessibility_service_summary">Enable accessibility</string>
+    <!-- Setting Checkbox summary for enabling accessibility - disable -->
+    <string name="disable_accessibility_service_summary">Disable accessibility</string>
+    <!-- Setting accessibility services category -->
+    <string name="accessibility_services_category">Accessibility services</string>
+    <!-- Message for announcing the lack of installed accessibility services. -->
+    <string name="no_accessibility_services_summary">No installed accessibility services.</string>
+    <!-- Warning message about security implications of enabling an accessibility service,
+         displayed as a dialog message when the user selects to enable an accessibility service. -->
+    <string name="accessibility_service_security_warning">This accessibility service may be able to collect
+        all the text you type, including personal data credit card numbers except passwords.
+        It may also log your user interface interactions. It comes from the application
+        <xliff:g id="accessibility_service_name">%1$s</xliff:g>. Enable this accessibility service?</string>
+    <!-- Warning about disabling accessibility displayed as a dialog message when the user
+         selects to disable accessibility. This avoids accidental disabling. -->
+    <string name="accessibility_service_disable_warning">Disable accessibility?</string>
+
 </resources>
 
 
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
new file mode 100644
index 0000000..d419540
--- /dev/null
+++ b/res/xml/accessibility_settings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+    <CheckBoxPreference
+            android:key="toggle_accessibility_service_checkbox"
+            android:title="@string/toggle_accessibility_title"
+            android:summaryOn="@string/disable_accessibility_service_summary"
+            android:summaryOff="@string/enable_accessibility_service_summary"
+            android:persistent="false"/>
+
+    <PreferenceCategory android:key="accessibility_services_category"
+            android:title="@string/accessibility_services_category" />
+
+</PreferenceScreen>
diff --git a/res/xml/settings.xml b/res/xml/settings.xml
index 56feed8..10712eb 100644
--- a/res/xml/settings.xml
+++ b/res/xml/settings.xml
@@ -115,7 +115,6 @@
                 android:targetClass="com.android.settings.LanguageSettings" />
         </PreferenceScreen>
 
-
         <!-- Search. 
              The settings activity will ensure that this is resolved to an
              activity on the system image, otherwise it will remove this
@@ -131,6 +130,17 @@
             <intent android:action="android.search.action.SEARCH_SETTINGS" />
         </PreferenceScreen>
 
+        <!-- Accessibility feedback -->
+
+        <PreferenceScreen
+            android:title="@string/accessibility_settings_title"
+            android:summary="@string/accessibility_settings_summary">
+            <intent
+                android:action="android.intent.action.MAIN"
+                android:targetPackage="com.android.settings"
+                android:targetClass="com.android.settings.AccessibilitySettings" />
+        </PreferenceScreen>
+
         <!-- About Device -->
  
         <PreferenceScreen
diff --git a/src/com/android/settings/AccessibilitySettings.java b/src/com/android/settings/AccessibilitySettings.java
new file mode 100644
index 0000000..45602bc
--- /dev/null
+++ b/src/com/android/settings/AccessibilitySettings.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import android.app.AlertDialog;
+import android.app.Service;
+import android.content.DialogInterface;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.view.accessibility.AccessibilityManager;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Activity with the accessibility settings.
+ */
+public class AccessibilitySettings extends PreferenceActivity {
+    private final String TOGGLE_ACCESSIBILITY_SERVICE_CHECKBOX =
+        "toggle_accessibility_service_checkbox";
+
+    private static final String ACCESSIBILITY_SERVICES_CATEGORY =
+        "accessibility_services_category";
+
+    private CheckBoxPreference mToggleCheckBox;
+
+    private Map<String, ServiceInfo> mAccessibilityServices =
+        new LinkedHashMap<String, ServiceInfo>();
+
+    private TextUtils.SimpleStringSplitter mStringColonSplitter =
+        new TextUtils.SimpleStringSplitter(':');
+
+    private PreferenceGroup mAccessibilityServicesCategory;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        addPreferencesFromResource(R.xml.accessibility_settings);
+
+        mToggleCheckBox = (CheckBoxPreference) findPreference(
+            TOGGLE_ACCESSIBILITY_SERVICE_CHECKBOX);
+
+        addAccessibilitServicePreferences();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        final HashSet<String> enabled = new HashSet<String>();
+        String settingValue = Settings.Secure.getString(getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+        if (settingValue != null) {
+            TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+            splitter.setString(settingValue);
+            while (splitter.hasNext()) {
+                enabled.add(splitter.next());
+            }
+        }
+
+        Map<String, ServiceInfo> accessibilityServices = mAccessibilityServices;
+
+        for (String key : accessibilityServices.keySet()) {
+            CheckBoxPreference preference = (CheckBoxPreference) findPreference(key);
+            if (preference != null) {
+                preference.setChecked(enabled.contains(key));
+            }
+        }
+
+        int serviceState = Settings.Secure.getInt(getContentResolver(),
+            Settings.Secure.ACCESSIBILITY_ENABLED, 0);
+
+        if (!accessibilityServices.isEmpty()) {
+            if (serviceState == 1) {
+                mToggleCheckBox.setChecked(true);
+                mToggleCheckBox.setSummaryOn(R.string.disable_accessibility_service_summary);
+            } else {
+                mToggleCheckBox.setSummaryOff(R.string.enable_accessibility_service_summary);
+                setAccessibilityServicePreferencesState(false);
+            }
+            mToggleCheckBox.setEnabled(true);
+        } else {
+            if (serviceState == 1) {
+                // no service and accessibility is enabled => disable
+                Settings.Secure.putInt(getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_ENABLED, 0);
+                setAccessibilityServicePreferencesState(false);
+            }
+            mToggleCheckBox.setSummaryOff(R.string.no_accessibility_services_summary);
+            mToggleCheckBox.setEnabled(false);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        persistEnabledAccessibilityServices();
+    }
+
+    /**
+     * Sets the state of the preferences for enabling/disabling AccessibilityServices.
+     *
+     * @param isEnabled If to enable or disable the preferences.
+     */
+    private void setAccessibilityServicePreferencesState(boolean isEnabled) {
+        if (mAccessibilityServicesCategory == null) {
+            return;
+        }
+
+        int count = mAccessibilityServicesCategory.getPreferenceCount();
+        for (int i = 0; i < count; i++) {
+            mAccessibilityServicesCategory.getPreference(i).setEnabled(isEnabled);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+        final String key = preference.getKey();
+
+        if (TOGGLE_ACCESSIBILITY_SERVICE_CHECKBOX.equals(key)) {
+            boolean isChecked = ((CheckBoxPreference) preference).isChecked();
+            handleEnableAccessibilityStateChange((CheckBoxPreference) preference);
+        } else if (preference instanceof CheckBoxPreference) {
+            handleEnableAccessibilityServiceStateChange((CheckBoxPreference) preference);
+        }
+
+        return super.onPreferenceTreeClick(preferenceScreen, preference);
+    }
+
+    /**
+     * Handles the change of the accessibility enabled setting state.
+     *
+     * @param preference The preference for enabling/disabling accessibility.
+     */
+    private void handleEnableAccessibilityStateChange(CheckBoxPreference preference) {
+        if (preference.isChecked()) {
+            Settings.Secure.putInt(getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_ENABLED, 1);
+            setAccessibilityServicePreferencesState(true);
+        } else {
+            final CheckBoxPreference checkBoxPreference = preference;
+            AlertDialog dialog = (new AlertDialog.Builder(this))
+                .setTitle(android.R.string.dialog_alert_title)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setMessage(getString(R.string.accessibility_service_disable_warning))
+                .setCancelable(true)
+                .setPositiveButton(android.R.string.ok,
+                    new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                            Settings.Secure.putInt(getContentResolver(),
+                                Settings.Secure.ACCESSIBILITY_ENABLED, 0);
+                            setAccessibilityServicePreferencesState(false);
+                        }
+                })
+                .setNegativeButton(android.R.string.cancel,
+                    new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                            checkBoxPreference.setChecked(true);
+                        }
+                })
+                .create();
+            dialog.show();
+        }
+    }
+
+    /**
+     * Handles the change of the preference for enabling/disabling an AccessibilityService.
+     *
+     * @param preference The preference.
+     */
+    private void handleEnableAccessibilityServiceStateChange(CheckBoxPreference preference) {
+        if (preference.isChecked()) {
+            final CheckBoxPreference checkBoxPreference = preference;
+            AlertDialog dialog = (new AlertDialog.Builder(this))
+                .setTitle(android.R.string.dialog_alert_title)
+                .setIcon(android.R.drawable.ic_dialog_alert)
+                .setMessage(getString(R.string.accessibility_service_security_warning,
+                    mAccessibilityServices.get(preference.getKey())
+                    .applicationInfo.loadLabel(getPackageManager())))
+                .setCancelable(true)
+                .setPositiveButton(android.R.string.ok,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) {
+                                checkBoxPreference.setChecked(true);
+                                persistEnabledAccessibilityServices();
+                            }
+                })
+                .setNegativeButton(android.R.string.cancel,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) {
+                                checkBoxPreference.setChecked(false);
+                            }
+                })
+                .create();
+            dialog.show();
+        } else {
+            persistEnabledAccessibilityServices();
+        }
+    }
+
+    /**
+     * Persists the Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES setting.
+     * The AccessibilityManagerService watches this property and manages the
+     * AccessibilityServices.
+     */
+    private void persistEnabledAccessibilityServices() {
+        StringBuilder builder = new StringBuilder(256);
+
+        int firstEnabled = -1;
+        for (String key : mAccessibilityServices.keySet()) {
+            CheckBoxPreference preference = (CheckBoxPreference) findPreference(key);
+            if (preference.isChecked()) {
+                 builder.append(key);
+                 builder.append(':');
+            }
+        }
+
+        Settings.Secure.putString(getContentResolver(),
+            Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, builder.toString());
+    }
+
+    /**
+     * Adds {@link CheckBoxPreference} for enabling or disabling an accessibility services.
+     */
+    private void addAccessibilitServicePreferences() {
+        AccessibilityManager accessibilityManager =
+            (AccessibilityManager) getSystemService(Service.ACCESSIBILITY_SERVICE);
+
+        List<ServiceInfo> installedServices = accessibilityManager.getAccessibilityServiceList();
+
+        mAccessibilityServicesCategory =
+            (PreferenceGroup) findPreference(ACCESSIBILITY_SERVICES_CATEGORY);
+
+        if (installedServices.isEmpty()) {
+            getPreferenceScreen().removePreference(mAccessibilityServicesCategory);
+            mAccessibilityServicesCategory = null;
+            return;
+        }
+
+        for (int i = 0, count = installedServices.size(); i < count; ++i) {
+            ServiceInfo serviceInfo = installedServices.get(i);
+            String key = serviceInfo.packageName + "/" + serviceInfo.name;
+
+            mAccessibilityServices.put(key, serviceInfo);
+
+            CheckBoxPreference preference = new CheckBoxPreference(this);
+            preference.setKey(key);
+            preference.setTitle(serviceInfo.loadLabel(getPackageManager()));
+            mAccessibilityServicesCategory.addPreference(preference);
+        }
+    }
+}