Merge "Bug 5003361: No Android Dreams in Settings / Display"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5302eb0..4118f32 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -639,6 +639,11 @@
                 android:resource="@id/privacy_settings" />
         </activity>
 
+        <activity android:name="SetFullBackupPassword"
+                android:theme="@android:style/Theme.Holo.DialogWhenLarge"
+                android:exported="false">
+        </activity>
+
         <activity android:name="CredentialStorage"
                 android:theme="@style/Transparent"
                 android:configChanges="orientation|keyboardHidden|screenSize">
diff --git a/res/layout/preference_inputmethod.xml b/res/layout/preference_inputmethod.xml
index 227b165..0edc060 100644
--- a/res/layout/preference_inputmethod.xml
+++ b/res/layout/preference_inputmethod.xml
@@ -78,6 +78,7 @@
         android:paddingLeft="15dip"
         android:paddingRight="?android:attr/scrollbarSize"
         android:src="@drawable/ic_sysbar_quicksettings"
+        android:contentDescription="@string/input_method_settings_button"
         android:layout_gravity="center"
         android:clickable="true"
         android:focusable="true"
diff --git a/res/layout/set_backup_pw.xml b/res/layout/set_backup_pw.xml
new file mode 100644
index 0000000..3743c81
--- /dev/null
+++ b/res/layout/set_backup_pw.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="16dip"
+    >
+
+    <TextView
+            android:id="@+id/prompt1"
+            android:text="@string/current_backup_pw_prompt"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+    <EditText
+            android:id="@+id/current_backup_pw"
+            android:layout_below="@id/prompt1"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:singleLine="true"
+            android:password="true" />
+
+    <TextView
+            android:id="@+id/prompt2"
+            android:layout_below="@id/current_backup_pw"
+            android:text="@string/new_backup_pw_prompt"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+    <EditText
+            android:id="@+id/new_backup_pw"
+            android:layout_below="@id/prompt2"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:singleLine="true"
+            android:password="true" />
+
+    <TextView
+            android:id="@+id/prompt3"
+            android:layout_below="@id/new_backup_pw"
+            android:text="@string/confirm_new_backup_pw_prompt"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+    <EditText
+            android:id="@+id/confirm_new_backup_pw"
+            android:layout_below="@id/prompt3"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:singleLine="true"
+            android:password="true" />
+
+    <Button
+            android:id="@+id/backup_pw_cancel_button"
+            android:layout_below="@id/confirm_new_backup_pw"
+            android:text="@string/backup_pw_cancel_button_text"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+    <Button
+            android:id="@+id/backup_pw_set_button"
+            android:layout_below="@id/confirm_new_backup_pw"
+            android:layout_toRightOf="@id/backup_pw_cancel_button"
+            android:text="@string/backup_pw_set_button_text"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+
+</RelativeLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index eef3792..f812010 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2587,6 +2587,8 @@
     <string name="configure_input_method">Configure input methods</string>
     <!-- Title for settings of each IME [CHAR LIMIT=25] -->
     <string name="input_method_settings">Settings</string>
+    <!-- Spoken description for IME settings image button [CHAR LIMIT=NONE] -->
+    <string name="input_method_settings_button">Settings</string>
     <!-- Title for settings of active input methods in each IME [CHAR LIMIT=35] -->
     <string name="active_input_method_subtypes">Active input methods</string>
     <!-- Title for settings whether or not the framework will select input methods in an IME based on the current system locale. [CHAR LIMIT=35] -->
@@ -2679,9 +2681,8 @@
     <!-- Title for accessibility preference to enable touch exploration mode. [CHAR LIMIT=35] -->
     <string name="accessibility_touch_exploration_title">Explore by touch</string>
     <!-- Summary for accessibility of the touch exploration mode. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_touch_exploration_summary" >Allows exploring screen content and interacting with the device.\n\n
-        Touch the screen to receive feedback of the content under your finger.\n\n Tap on the last explored location to activate.\n\n
-        Use two fingers to drag. </string>
+    <string name="accessibility_touch_exploration_summary" >When Explore by Touch is turned on,
+    you can hear or see descriptions of what\'s under your finger.\n\n This feature is for low-vision users.</string>
     <!-- Title for accessibility preference to choose long-press delay i.e. timeout before it is detected. [CHAR LIMIT=35] -->
     <string name="accessibility_long_press_timeout_title">Touch &amp; hold delay</string>
     <!-- Title for accessibility preference to install accessibility scripts from Google. [CHAR LIMIT=35] -->
@@ -2708,31 +2709,37 @@
     <string name="accessibility_script_injection_button_disallow">Don\'t Allow</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. [CHAR LIMIT=NONE] -->
-    <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>. Use this accessibility service?</string>
-    <!-- Warning about disabling accessibility displayed as a dialog message when the user
+         displayed as a dialog message when the user selects to enable an accessibility service (tablet). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_service_security_warning">
+            <xliff:g id="accessibility_service_name">%1$s</xliff:g> can
+            collect all of the text you type, except passwords. This includes personal data such as credit card
+            numbers. It can also collect data about your interactions with the device.</string>
+    <!-- Title for a warning about disabling accessibility displayed as a dialog message when the user
+         selects to disable accessibility. This avoids accidental disabling. [CHAR LIMIT=30] -->
+    <string name="accessibility_disable_warning_title">Turn accessibility off?</string>
+    <!-- Summary for a warning about disabling accessibility displayed as a dialog message when the user
          selects to disable accessibility. This avoids accidental disabling. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_service_disable_warning">Disable accessibility?</string>
+    <string name="accessibility_disable_warning_summary">Touching OK will stop spoken
+        descriptions and all other accessibility features you\'ve been using.</string>
     <!-- Title for the prompt that lets users know that they have no accessibility related apps
          installed and that they can install TalkBack from Market. [CHAR LIMIT=50] -->
-    <string name="accessibility_service_no_apps_title">No accessibility related applications found
+    <string name="accessibility_service_no_apps_title">No accessibility applications
         </string>
     <!-- Message for the prompt that lets users know that they have no accessibility related apps
          installed and that they can install TalkBack from Market. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_service_no_apps_message">You do not have any accessibility-related
-        applications installed.\n\nYou can download a screen reader for your device from Android
-        Market.\n\nClick "OK" to install the screen reader.</string>
+    <string name="accessibility_service_no_apps_message">You don\'t have accessibility
+        applications installed. Do you want to download a screen reader from the Android Market?</string>
     <!-- Warning message about security implications of downloading accessibility scripts,
          displayed as a dialog message when the user selects to enable script downloading. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_script_injection_security_warning">Some applications can ask Google
-        to download scripts to your device that make their content more accessible. Are you sure you
-        want to allow Google to install accessibility scripts on your device?</string>
+    <string name="accessibility_script_injection_security_warning">Do you want applications to install
+        scripts from Google that will make their content more accessible?</string>
     <!-- Warning message that the interaction model changes on enabling touch exploration. [CHAR LIMIT=NONE] -->
     <string name="accessibility_touch_exploration_warning">Enabling explore by touch
-        changes the interation model. Enable explore by touch?</string>
+        changes how the devices respons to touch. Enable explore by touch?</string>
+    <!-- Default description for an accessiiblity serivice if the latter doesn't provide one. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_service_default_description">This accessibility service has no
+        description.\n\nAccessibility services provide various types of feedback when you interact
+        with the device. </string>
 
     <!-- App Fuel Gauge strings -->
     <skip />
@@ -3080,6 +3087,14 @@
     <string name="auto_restore_title">Automatic restore</string>
     <!-- Summary text of the "automatic restore" setting -->
     <string name="auto_restore_summary">If I reinstall an application, restore backed up settings or other data</string>
+
+    <!-- Local backup password menu title [CHAR LIMIT=25] -->
+    <string name="local_backup_password_title">Local backup password</string>
+    <!-- Summary text of the "local backup password" setting when the user has not supplied a password -->
+    <string name="local_backup_password_summary_none">Local full backups are not currently protected.</string>
+    <!-- Summary text of the "local backup password" setting when the user has already supplied a password -->
+    <string name="local_backup_password_summary_change">Select to change or remove the password for local full backups</string>
+
     <!-- Dialog title for confirmation to erase backup data from server -->
     <string name="backup_erase_dialog_title">Backup</string>
     <!-- Dialog title for confirmation to erase backup data from server -->
@@ -3593,4 +3608,17 @@
 
     <!--  Title for spelling correction settings -->
     <string name="spellcheckers_settings_title">Spelling correction</string>
+
+    <!-- Prompt for the user to enter their current full-backup password -->
+    <string name="current_backup_pw_prompt">Enter your current full backup password here</string>
+    <!-- Prompt for the user to enter a new full-backup password -->
+    <string name="new_backup_pw_prompt">Enter a new password for full backups here</string>
+    <!-- Prompt for the user to confirm the new full-backup password by re-entering it -->
+    <string name="confirm_new_backup_pw_prompt">Please re-enter your new full backup password here</string>
+
+    <!-- Button label for setting the user's new full-backup password -->
+    <string name="backup_pw_set_button_text">Set backup password</string>
+    <!-- Button label for cancelling the new-password operation and retaining the user's previous full-backup password -->
+    <string name="backup_pw_cancel_button_text">Cancel</string>
+
 </resources>
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
index 78a857b..4dbca6d 100644
--- a/res/xml/accessibility_settings.xml
+++ b/res/xml/accessibility_settings.xml
@@ -30,12 +30,12 @@
         <CheckBoxPreference
                 android:key="toggle_large_text_preference"
                 android:title="@string/accessibility_toggle_large_text_title"
-                android:persistent="false"/>
+                android:persistent="true"/>
 
         <CheckBoxPreference
                 android:key="toggle_power_button_ends_call_preference"
                 android:title="@string/accessibility_power_button_ends_call_title"
-                android:persistent="false">
+                android:persistent="true">
         </CheckBoxPreference>
 
         <PreferenceScreen
@@ -55,10 +55,15 @@
                 android:entryValues="@array/long_press_timeout_selector_values"
                 android:persistent="true" />
 
-        <Preference
+        <com.android.settings.AccessibilityEnableScriptInjectionPreference
                 android:key="toggle_script_injection_preference"
                 android:title="@string/accessibility_script_injection_title"
-                android:persistent="false" />
+                android:dialogTitle="@android:string/dialog_alert_title"
+                android:dialogIcon="@android:drawable/ic_dialog_alert"
+                android:dialogMessage="@string/accessibility_script_injection_security_warning"
+                android:positiveButtonText="@string/accessibility_script_injection_button_allow"
+                android:negativeButtonText="@string/accessibility_script_injection_button_disallow"
+                android:persistent="true" />
 
     </PreferenceCategory>
 
diff --git a/res/xml/privacy_settings.xml b/res/xml/privacy_settings.xml
index 54d88f7..60efef6 100644
--- a/res/xml/privacy_settings.xml
+++ b/res/xml/privacy_settings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- Copyright (C) 2009-2011 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.
@@ -38,6 +38,16 @@
                 android:title="@string/auto_restore_title"
                 android:summary="@string/auto_restore_summary"
                 android:persistent="false" />
+        <PreferenceScreen
+                android:key="local_backup_password"
+                android:title="@string/local_backup_password_title"
+                android:summary="@string/local_backup_password_summary_none"
+                android:persistent="false" >
+            <intent
+                    android:action="android.settings.privacy.SET_FULL_BACKUP_PASSWORD"
+                    android:targetPackage="com.android.settings"
+                    android:targetClass="com.android.settings.SetFullBackupPassword" />
+        </PreferenceScreen>
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/src/com/android/settings/AccessibilityEnableScriptInjectionPreference.java b/src/com/android/settings/AccessibilityEnableScriptInjectionPreference.java
new file mode 100644
index 0000000..a9338ed
--- /dev/null
+++ b/src/com/android/settings/AccessibilityEnableScriptInjectionPreference.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2011 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.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.preference.DialogPreference;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+/**
+ * Preference for enabling accessibility script injection. It displays a warning
+ * dialog before enabling the preference.
+ */
+public class AccessibilityEnableScriptInjectionPreference extends DialogPreference {
+
+    private boolean mInjectionAllowed;
+    private boolean mSendClickAccessibilityEvent;
+
+    public AccessibilityEnableScriptInjectionPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        updateSummary();
+    }
+
+    public void setInjectionAllowed(boolean injectionAllowed) {
+        if (mInjectionAllowed != injectionAllowed) {
+            mInjectionAllowed = injectionAllowed;
+            persistBoolean(injectionAllowed);
+            updateSummary();
+        }
+    }
+
+    public boolean isInjectionAllowed() {
+        return mInjectionAllowed;
+    }
+
+    @Override
+    protected void onBindView(View view) {
+        super.onBindView(view);
+        View summaryView = view.findViewById(com.android.internal.R.id.summary);
+        sendAccessibilityEvent(summaryView);
+    }
+
+    private void sendAccessibilityEvent(View view) {
+        // Since the view is still not attached we create, populate,
+        // and send the event directly since we do not know when it
+        // will be attached and posting commands is not as clean.
+        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getContext());
+        if (mSendClickAccessibilityEvent && accessibilityManager.isEnabled()) {
+            AccessibilityEvent event = AccessibilityEvent.obtain();
+            event.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED);
+            view.onInitializeAccessibilityEvent(event);
+            view.dispatchPopulateAccessibilityEvent(event);
+            accessibilityManager.sendAccessibilityEvent(event);
+        }
+        mSendClickAccessibilityEvent = false;
+    }
+
+    @Override
+    protected void onClick() {
+        if (isInjectionAllowed()) {
+            setInjectionAllowed(false);
+            // Update the system setting only upon user action.
+            setSystemSetting(false);
+            mSendClickAccessibilityEvent = true;
+        } else {
+            super.onClick();
+            mSendClickAccessibilityEvent = false;
+        }
+    }
+
+    @Override
+    protected Object onGetDefaultValue(TypedArray a, int index) {
+        return a.getBoolean(index, false);
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setInjectionAllowed(restoreValue
+                ? getPersistedBoolean(mInjectionAllowed)
+                : (Boolean) defaultValue);
+    }
+
+    @Override
+    protected void onDialogClosed(boolean result) {
+        setInjectionAllowed(result);
+        if (result) {
+            // Update the system setting only upon user action.
+            setSystemSetting(true);
+        }
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        if (isPersistent()) {
+            return superState;
+        }
+        SavedState myState = new SavedState(superState);
+        myState.mInjectionAllowed = mInjectionAllowed;
+        return myState;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        if (state == null || !state.getClass().equals(SavedState.class)) {
+            super.onRestoreInstanceState(state);
+            return;
+        }
+        SavedState myState = (SavedState) state;
+        super.onRestoreInstanceState(myState.getSuperState());
+        setInjectionAllowed(myState.mInjectionAllowed);
+    }
+
+    private void updateSummary() {
+        setSummary(mInjectionAllowed
+                ? getContext().getString(R.string.accessibility_script_injection_allowed)
+                : getContext().getString(R.string.accessibility_script_injection_disallowed));
+    }
+
+    private void setSystemSetting(boolean enabled) {
+        Settings.Secure.putInt(getContext().getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, enabled ? 1 : 0);
+    }
+
+    private static class SavedState extends BaseSavedState {
+        private boolean mInjectionAllowed;
+
+        public SavedState(Parcel source) {
+            super(source);
+            mInjectionAllowed = (source.readInt() == 1);
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            super.writeToParcel(parcel, flags);
+            parcel.writeInt(mInjectionAllowed ? 1 : 0);
+        }
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        @SuppressWarnings("all")
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+}
diff --git a/src/com/android/settings/AccessibilitySettings.java b/src/com/android/settings/AccessibilitySettings.java
index beef902..a4b7975 100644
--- a/src/com/android/settings/AccessibilitySettings.java
+++ b/src/com/android/settings/AccessibilitySettings.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.pm.ServiceInfo;
 import android.content.res.Configuration;
 import android.net.Uri;
@@ -37,6 +38,7 @@
 import android.preference.CheckBoxPreference;
 import android.preference.ListPreference;
 import android.preference.Preference;
+import android.preference.PreferenceActivity;
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
@@ -49,6 +51,7 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.Switch;
 import android.widget.TextView;
@@ -73,6 +76,8 @@
 
     private static final float LARGE_FONT_SCALE = 1.3f;
 
+    private static final String SYSTEM_PROPERTY_MARKET_URL = "ro.screenreader.market";
+
     // Timeout before we update the services if packages are added/removed since
     // the AccessibilityManagerService has to do that processing first to generate
     // the AccessibilityServiceInfo we need for proper presentation.
@@ -80,8 +85,15 @@
 
     private static final char ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ':';
 
+    private static final String KEY_ACCESSIBILITY_TUTORIAL_LAUNCHED_ONCE =
+        "key_accessibility_tutorial_launched_once";
+
+    private static final String KEY_INSTALL_ACCESSIBILITY_SERVICE_OFFERED_ONCE =
+        "key_install_accessibility_service_offered_once";
+
     // Preference categories
     private static final String SERVICES_CATEGORY = "services_category";
+    private static final String SYSTEM_CATEGORY = "system_category";
 
     // Preferences
     private static final String TOGGLE_LARGE_TEXT_PREFERENCE = "toggle_large_text_preference";
@@ -95,18 +107,17 @@
         "toggle_script_injection_preference";
 
     // Extras passed to sub-fragments.
-    static final String EXTRA_PREFERENCE_KEY = "preference_key";
-    static final String EXTRA_CHECKED = "checked";
-    static final String EXTRA_TITLE = "title";
-    static final String EXTRA_SUMMARY = "summary";
-    static final String EXTRA_WARNING_MESSAGE = "warning_message";
-    static final String EXTRA_SETTINGS_TITLE = "settings_title";
-    static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name";
+    private static final String EXTRA_PREFERENCE_KEY = "preference_key";
+    private static final String EXTRA_CHECKED = "checked";
+    private static final String EXTRA_TITLE = "title";
+    private static final String EXTRA_SUMMARY = "summary";
+    private static final String EXTRA_WARNING_MESSAGE = "warning_message";
+    private static final String EXTRA_SETTINGS_TITLE = "settings_title";
+    private static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name";
 
     // Dialog IDs.
     private static final int DIALOG_ID_DISABLE_ACCESSIBILITY = 1;
-    private static final int DIALOG_ID_ENABLE_SCRIPT_INJECTION = 2;
-    private static final int DIALOG_ID_NO_ACCESSIBILITY_SERVICES = 3;
+    private static final int DIALOG_ID_NO_ACCESSIBILITY_SERVICES = 2;
 
     // Auxiliary members.
     private final SimpleStringSplitter mStringColonSplitter =
@@ -123,7 +134,7 @@
         @Override
         public void dispatchMessage(Message msg) {
             super.dispatchMessage(msg);
-            updateServicesPreferences();
+            updateServicesPreferences(mToggleAccessibilitySwitch.isChecked());
         }
     };
 
@@ -131,103 +142,96 @@
     private ToggleSwitch mToggleAccessibilitySwitch;
 
     private PreferenceCategory mServicesCategory;
+    private PreferenceCategory mSystemsCategory;
 
     private CheckBoxPreference mToggleLargeTextPreference;
     private CheckBoxPreference mTogglePowerButtonEndsCallPreference;
-    private Preference mTouchExplorationEnabledPreference;
+    private Preference mToggleTouchExplorationPreference;
     private ListPreference mSelectLongPressTimeoutPreference;
-    private Preference mToggleScriptInjectionPreference;
+    private AccessibilityEnableScriptInjectionPreference mToggleScriptInjectionPreference;
+
+    private int mLongPressTimeoutDefault;
 
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
         addPreferencesFromResource(R.xml.accessibility_settings);
-        installToggleAccessibilitySwitch();
-        findPreferences();
+        initializeAllPreferences();
     }
 
     @Override
     public void onResume() {
         super.onResume();
-        updateServicesPreferences();
-        updateSystemPreferences();
-        updatePreferencesForAccessibilityState();
+        final boolean accessibilityEnabled = mToggleAccessibilitySwitch.isChecked();
+        updateAllPreferences(accessibilityEnabled);
+        if (accessibilityEnabled) {
+            offerInstallAccessibilitySerivceOnce();
+        }
         mSettingsPackageMonitor.register(getActivity(), false);
     }
 
     @Override
     public void onPause() {
-        super.onPause();
         mSettingsPackageMonitor.unregister();
+        super.onPause();
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        addToggleAccessibilitySwitch();
+        super.onViewCreated(view, savedInstanceState);
+    }
+
+    @Override
+    public void onDestroyView() {
+        removeToggleAccessibilitySwitch();
+        super.onDestroyView();
     }
 
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         if (preference == mSelectLongPressTimeoutPreference) {
-            final int intValue = Integer.parseInt((String) newValue);
+            String stringValue = (String) newValue;
             Settings.Secure.putInt(getContentResolver(),
-                    Settings.Secure.LONG_PRESS_TIMEOUT, intValue);
+                    Settings.Secure.LONG_PRESS_TIMEOUT, Integer.parseInt(stringValue));
             mSelectLongPressTimeoutPreference.setSummary(
-                    mLongPressTimeoutValuetoTitleMap.get(String.valueOf(intValue)));
+                    mLongPressTimeoutValuetoTitleMap.get(stringValue));
             return true;
         }
         return false;
     }
 
-    private void updatePreferencesForAccessibilityState() {
-        final boolean accessibilityEnabled = (Settings.Secure.getInt(getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1);
-        mServicesCategory.setEnabled(accessibilityEnabled);
-        mTouchExplorationEnabledPreference.setEnabled(accessibilityEnabled);
-        mToggleScriptInjectionPreference.setEnabled(accessibilityEnabled);
-    }
-
     @Override
     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
         final String key = preference.getKey();
         if (mToggleLargeTextPreference == preference) {
-            handleToggleLargeTextPreference((CheckBoxPreference) preference);
+            handleToggleLargeTextPreferenceClick();
             return true;
         } else if (mTogglePowerButtonEndsCallPreference == preference) {
-            handleTogglePowerButtonEndsCallPreference((CheckBoxPreference) preference);
-            return true;
-        } else if (mToggleScriptInjectionPreference == preference) {
-            handleToggleAccessibilityScriptInjectionPreference(preference);
+            handleTogglePowerButtonEndsCallPreferenceClick();
             return true;
         }
         return super.onPreferenceTreeClick(preferenceScreen, preference);
     }
 
-    private void handleTogglePowerButtonEndsCallPreference(CheckBoxPreference preference) {
-        Settings.Secure.putInt(getContentResolver(),
-                Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
-                (preference.isChecked() ? Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP
-                        : Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF));
-    }
-
-    private void handleToggleLargeTextPreference(CheckBoxPreference preference) {
+    private void handleToggleLargeTextPreferenceClick() {
         try {
-            mCurConfig.fontScale = preference.isChecked() ? LARGE_FONT_SCALE : 1;
+            mCurConfig.fontScale = mToggleLargeTextPreference.isChecked() ? LARGE_FONT_SCALE : 1;
             ActivityManagerNative.getDefault().updatePersistentConfiguration(mCurConfig);
-        } catch (RemoteException e) {
+        } catch (RemoteException re) {
             /* ignore */
         }
     }
 
-    private void handleToggleAccessibilityScriptInjectionPreference(Preference preference) {
-        String allowed = getString(R.string.accessibility_script_injection_disallowed);
-        if (preference.getSummary().equals(allowed)) {
-            // set right enabled state since the user may press back.
-            showDialog(DIALOG_ID_ENABLE_SCRIPT_INJECTION);
-        } else {
-            Settings.Secure.putInt(getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0);
-            mToggleScriptInjectionPreference.setSummary(
-                    getString(R.string.accessibility_script_injection_disallowed));
-        }
+    private void handleTogglePowerButtonEndsCallPreferenceClick() {
+        Settings.Secure.putInt(getContentResolver(),
+                Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
+                (mTogglePowerButtonEndsCallPreference.isChecked()
+                        ? Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP
+                        : Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF));
     }
 
-    private void installToggleAccessibilitySwitch() {
-        mToggleAccessibilitySwitch = createActionBarToggleSwitch(getActivity());
+    private void addToggleAccessibilitySwitch() {
+        mToggleAccessibilitySwitch = createAndAddActionBarToggleSwitch(getActivity());
         final boolean checked = (Settings.Secure.getInt(getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1);
         mToggleAccessibilitySwitch.setChecked(checked);
@@ -236,34 +240,126 @@
             @Override
             public boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked) {
                 if (!checked) {
-                    toggleSwitch.setCheckedNoBeforeCheckedChangeListener(true);
+                    toggleSwitch.setCheckedInternal(true);
                     showDialog(DIALOG_ID_DISABLE_ACCESSIBILITY);
                     return true;
                 }
                 Settings.Secure.putInt(getContentResolver(),
                         Settings.Secure.ACCESSIBILITY_ENABLED, 1);
-                updatePreferencesForAccessibilityState();
+                updateAllPreferences(true);
+                offerInstallAccessibilitySerivceOnce();
                 return false;
             }
         });
     }
 
-    private void findPreferences() {
-        mServicesCategory = (PreferenceCategory) findPreference(SERVICES_CATEGORY);
-
-        mToggleLargeTextPreference = (CheckBoxPreference) findPreference(
-                TOGGLE_LARGE_TEXT_PREFERENCE);
-        mTogglePowerButtonEndsCallPreference = (CheckBoxPreference) findPreference(
-                TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE);
-        mTouchExplorationEnabledPreference = findPreference(TOGGLE_TOUCH_EXPLORATION_PREFERENCE);
-        mSelectLongPressTimeoutPreference = (ListPreference) findPreference(
-                SELECT_LONG_PRESS_TIMEOUT_PREFERENCE);
-        mSelectLongPressTimeoutPreference.setOnPreferenceChangeListener(this);
-        mToggleScriptInjectionPreference = findPreference(TOGGLE_SCRIPT_INJECTION_PREFERENCE);
-        mToggleScriptInjectionPreference.setOnPreferenceChangeListener(this);
+    public void removeToggleAccessibilitySwitch() {
+        getActivity().getActionBar().setCustomView(null);
     }
 
-    private void updateServicesPreferences() {
+    private void initializeAllPreferences() {
+        // The basic logic here is if accessibility is not enabled all accessibility
+        // settings will have no effect but still their selected state should be kept
+        // unchanged, so the user can see what settings will be enabled when turning
+        // on accessibility.
+
+        final boolean accessibilityEnabled = (Settings.Secure.getInt(getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1);
+
+        mServicesCategory = (PreferenceCategory) findPreference(SERVICES_CATEGORY);
+        mSystemsCategory = (PreferenceCategory) findPreference(SYSTEM_CATEGORY);
+
+        // Large text.
+        mToggleLargeTextPreference =
+            (CheckBoxPreference) findPreference(TOGGLE_LARGE_TEXT_PREFERENCE);
+        if (accessibilityEnabled) {
+            try {
+                mCurConfig.updateFrom(ActivityManagerNative.getDefault().getConfiguration());
+            } catch (RemoteException re) {
+                /* ignore */
+            }
+            mToggleLargeTextPreference.setChecked(mCurConfig.fontScale == LARGE_FONT_SCALE);
+        }
+
+        // Power button ends calls.
+        mTogglePowerButtonEndsCallPreference =
+            (CheckBoxPreference) findPreference(TOGGLE_POWER_BUTTON_ENDS_CALL_PREFERENCE);
+        if (KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER)
+                && Utils.isVoiceCapable(getActivity())) {
+            if (accessibilityEnabled) {
+                final int incallPowerBehavior = Settings.Secure.getInt(getContentResolver(),
+                        Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
+                        Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT);
+                final boolean powerButtonEndsCall =
+                    (incallPowerBehavior == Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP);
+                mTogglePowerButtonEndsCallPreference.setChecked(powerButtonEndsCall);
+            }
+        } else {
+            mSystemsCategory.removePreference(mTogglePowerButtonEndsCallPreference);
+        }
+
+        // Touch exploration enabled.
+        mToggleTouchExplorationPreference = findPreference(TOGGLE_TOUCH_EXPLORATION_PREFERENCE);
+        final boolean touchExplorationEnabled = (Settings.Secure.getInt(getContentResolver(),
+                Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1);
+        if (touchExplorationEnabled) {
+            mToggleTouchExplorationPreference.setSummary(
+                    getString(R.string.accessibility_service_state_on));
+            mToggleTouchExplorationPreference.getExtras().putBoolean(EXTRA_CHECKED, true);
+        } else {
+            mToggleTouchExplorationPreference.setSummary(
+                    getString(R.string.accessibility_service_state_off));
+            mToggleTouchExplorationPreference.getExtras().putBoolean(EXTRA_CHECKED, false);
+        }
+
+        // Long press timeout.
+        mSelectLongPressTimeoutPreference =
+            (ListPreference) findPreference(SELECT_LONG_PRESS_TIMEOUT_PREFERENCE);
+        mSelectLongPressTimeoutPreference.setOnPreferenceChangeListener(this);
+        if (mLongPressTimeoutValuetoTitleMap.size() == 0) {
+            String[] timeoutValues = getResources().getStringArray(
+                    R.array.long_press_timeout_selector_values);
+            mLongPressTimeoutDefault = Integer.parseInt(timeoutValues[0]);
+            String[] timeoutTitles = getResources().getStringArray(
+                    R.array.long_press_timeout_selector_titles);
+            final int timeoutValueCount = timeoutValues.length;
+            for (int i = 0; i < timeoutValueCount; i++) {
+               mLongPressTimeoutValuetoTitleMap.put(timeoutValues[i], timeoutTitles[i]);
+            }
+        }
+        if (accessibilityEnabled) {
+            final int longPressTimeout = Settings.Secure.getInt(getContentResolver(),
+                    Settings.Secure.LONG_PRESS_TIMEOUT, mLongPressTimeoutDefault);
+            String value = String.valueOf(longPressTimeout);
+            mSelectLongPressTimeoutPreference.setValue(value);
+            mSelectLongPressTimeoutPreference.setSummary(
+                    mLongPressTimeoutValuetoTitleMap.get(value));
+        } else {
+            Settings.Secure.putInt(getContentResolver(), Settings.Secure.LONG_PRESS_TIMEOUT,
+                    mLongPressTimeoutDefault);
+        }
+
+        // Script injection.
+        mToggleScriptInjectionPreference = (AccessibilityEnableScriptInjectionPreference)
+            findPreference(TOGGLE_SCRIPT_INJECTION_PREFERENCE);
+        if (accessibilityEnabled) {
+            final boolean  scriptInjectionAllowed = (Settings.Secure.getInt(getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0) == 1);
+            mToggleScriptInjectionPreference.setInjectionAllowed(scriptInjectionAllowed);
+        }
+    }
+
+    private void updateAllPreferences(boolean accessibilityEnabled) {
+        updateServicesPreferences(accessibilityEnabled);
+        updateSystemPreferences(accessibilityEnabled);
+    }
+
+    private void updateServicesPreferences(boolean accessibilityEnabled) {
+        // Since services category is auto generated we have to do a pass
+        // to generate it since services can come and go and then based on
+        // the global accessibility state to decided whether it is enabled.
+
+        // Generate.
         mServicesCategory.removeAll();
 
         AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(getActivity());
@@ -271,17 +367,6 @@
         List<AccessibilityServiceInfo> installedServices =
             accessibilityManager.getInstalledAccessibilityServiceList();
 
-        if (installedServices.isEmpty() && accessibilityManager.isEnabled()) {
-            // no service and accessibility is enabled => disable
-            Settings.Secure.putInt(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0);
-            mToggleAccessibilitySwitch.setChecked(false);
-            mToggleAccessibilitySwitch.setEnabled(false);
-            // Notify user that they do not have any accessibility
-            // services installed and direct them to Market to get TalkBack.
-            showDialog(DIALOG_ID_NO_ACCESSIBILITY_SERVICES);
-            return;
-        }
-
         Set<ComponentName> enabledComponentNames = new HashSet<ComponentName>();
         String settingValue = Settings.Secure.getString(getContentResolver(),
                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
@@ -317,12 +402,19 @@
 
             preference.setOrder(i);
             preference.setFragment(ToggleAccessibilityServiceFragment.class.getName());
+            preference.setPersistent(true);
 
             Bundle extras = preference.getExtras();
             extras.putString(EXTRA_PREFERENCE_KEY, preference.getKey());
             extras.putBoolean(EXTRA_CHECKED, enabled);
             extras.putString(EXTRA_TITLE, title);
-            extras.putString(EXTRA_SUMMARY, info.getDescription());
+
+            String description = info.getDescription();
+            if (TextUtils.isEmpty(description)) {
+                description = getString(R.string.accessibility_service_default_description);
+            }
+            extras.putString(EXTRA_SUMMARY, description);
+
             extras.putString(EXTRA_WARNING_MESSAGE, getString(
                     R.string.accessibility_service_security_warning,
                     info.getResolveInfo().loadLabel(getPackageManager())));
@@ -338,69 +430,99 @@
 
             mServicesCategory.addPreference(preference);
         }
+
+        // Update enabled state.
+        mServicesCategory.setEnabled(accessibilityEnabled);
     }
 
-    public void updateSystemPreferences() {
+    private void updateSystemPreferences(boolean accessibilityEnabled) {
+        // The basic logic here is if accessibility is not enabled all accessibility
+        // settings will have no effect but still their selected state should be kept
+        // unchanged, so the user can see what settings will be enabled when turning
+        // on accessibility.
+
         // Large text.
+        mToggleLargeTextPreference.setEnabled(accessibilityEnabled);
+        if (accessibilityEnabled) {
+            mCurConfig.fontScale =
+                mToggleLargeTextPreference.isChecked() ? LARGE_FONT_SCALE : 1;
+        } else {
+            mCurConfig.fontScale = 1;
+        }
         try {
-            mCurConfig.updateFrom(ActivityManagerNative.getDefault().getConfiguration());
+            ActivityManagerNative.getDefault().updatePersistentConfiguration(mCurConfig);
         } catch (RemoteException re) {
             /* ignore */
         }
-        mToggleLargeTextPreference.setChecked(Float.compare(mCurConfig.fontScale,
-                LARGE_FONT_SCALE) == 0);
 
-        // Power button ends call.
-        if (KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER)
-                && Utils.isVoiceCapable(getActivity())) {
-              final int incallPowerBehavior = Settings.Secure.getInt(getContentResolver(),
-                      Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
-                      Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT);
-              final boolean powerButtonEndsCall =
-                  (incallPowerBehavior == Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP);
-              mTogglePowerButtonEndsCallPreference.setChecked(powerButtonEndsCall);
-        } else {
-              getPreferenceScreen().removePreference(mTogglePowerButtonEndsCallPreference);
+        // Power button ends calls.
+        if (mTogglePowerButtonEndsCallPreference != null) {
+            mTogglePowerButtonEndsCallPreference.setEnabled(accessibilityEnabled);
+            final int powerButtonEndsCall;
+            if (accessibilityEnabled) {
+                powerButtonEndsCall = mTogglePowerButtonEndsCallPreference.isChecked()
+                    ? Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP
+                    : Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF;
+            } else {
+                powerButtonEndsCall = Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF;
+            }
+            Settings.Secure.putInt(getContentResolver(),
+                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
+                    powerButtonEndsCall);
         }
 
         // Touch exploration enabled.
+        mToggleTouchExplorationPreference.setEnabled(accessibilityEnabled);
         final boolean touchExplorationEnabled = (Settings.Secure.getInt(getContentResolver(),
                 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1);
         if (touchExplorationEnabled) {
-            mTouchExplorationEnabledPreference.setSummary(
+            mToggleTouchExplorationPreference.setSummary(
                     getString(R.string.accessibility_service_state_on));
-            mTouchExplorationEnabledPreference.getExtras().putBoolean(EXTRA_CHECKED, true);
+            mToggleTouchExplorationPreference.getExtras().putBoolean(EXTRA_CHECKED, true);
         } else {
-            mTouchExplorationEnabledPreference.setSummary(
+            mToggleTouchExplorationPreference.setSummary(
                     getString(R.string.accessibility_service_state_off));
-            mTouchExplorationEnabledPreference.getExtras().putBoolean(EXTRA_CHECKED, false);
+            mToggleTouchExplorationPreference.getExtras().putBoolean(EXTRA_CHECKED, false);
         }
 
         // Long press timeout.
-        if (mLongPressTimeoutValuetoTitleMap.isEmpty()) {
-            String[] timeoutValues = getResources().getStringArray(
-                    R.array.long_press_timeout_selector_values);
-            String[] timeoutTitles = getResources().getStringArray(
-                    R.array.long_press_timeout_selector_titles);
-            final int timeoutValueCount = timeoutValues.length;
-            for (int i = 0;i < timeoutValueCount; i++) {
-                mLongPressTimeoutValuetoTitleMap.put(timeoutValues[i], timeoutTitles[i]);
-            }
+        mSelectLongPressTimeoutPreference.setEnabled(accessibilityEnabled);
+        final int longPressTimeout;
+        if (accessibilityEnabled) {
+            longPressTimeout = Integer.parseInt(mSelectLongPressTimeoutPreference.getValue());
+        } else {
+            longPressTimeout = mLongPressTimeoutDefault;
         }
-        String longPressTimeout = String.valueOf(Settings.Secure.getInt(getContentResolver(),
-                Settings.Secure.LONG_PRESS_TIMEOUT, 0));
-        mSelectLongPressTimeoutPreference.setSummary(
-                mLongPressTimeoutValuetoTitleMap.get(longPressTimeout));
+        Settings.Secure.putInt(getContentResolver(), Settings.Secure.LONG_PRESS_TIMEOUT,
+                longPressTimeout);
+        String value = mSelectLongPressTimeoutPreference.getValue();
+        mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValuetoTitleMap.get(value));
 
         // Script injection.
-        final boolean scriptInjectionAllowed = (Settings.Secure.getInt(getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0) == 1);
-        if (scriptInjectionAllowed) {
-            mToggleScriptInjectionPreference.setSummary(
-                    getString(R.string.accessibility_script_injection_allowed));
+        mToggleScriptInjectionPreference.setEnabled(accessibilityEnabled);
+        final boolean scriptInjectionAllowed;
+        if (accessibilityEnabled) {
+            scriptInjectionAllowed = mToggleScriptInjectionPreference.isInjectionAllowed();
         } else {
-            mToggleScriptInjectionPreference.setSummary(
-                    getString(R.string.accessibility_script_injection_disallowed));
+            scriptInjectionAllowed = false;
+        }
+        Settings.Secure.putInt(getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION,
+                scriptInjectionAllowed ? 1 : 0);
+    }
+
+    private void offerInstallAccessibilitySerivceOnce() {
+        if (mServicesCategory.getPreferenceCount() > 0) {
+            return;
+        }
+        SharedPreferences preferences = getActivity().getPreferences(Context.MODE_PRIVATE);
+        final boolean offerInstallService = !preferences.getBoolean(
+                KEY_INSTALL_ACCESSIBILITY_SERVICE_OFFERED_ONCE, false);
+        if (offerInstallService) {
+            preferences.edit().putBoolean(KEY_INSTALL_ACCESSIBILITY_SERVICE_OFFERED_ONCE,
+                    true).commit();
+            // Notify user that they do not have any accessibility
+            // services installed and direct them to Market to get TalkBack.
+            showDialog(DIALOG_ID_NO_ACCESSIBILITY_SERVICES);
         }
     }
 
@@ -409,48 +531,29 @@
         switch (dialogId) {
             case DIALOG_ID_DISABLE_ACCESSIBILITY:
                 return (new AlertDialog.Builder(getActivity()))
-                    .setTitle(android.R.string.dialog_alert_title)
+                    .setTitle(R.string.accessibility_disable_warning_title)
                     .setIcon(android.R.drawable.ic_dialog_alert)
                     .setMessage(getResources().
-                            getString(R.string.accessibility_service_disable_warning))
+                            getString(R.string.accessibility_disable_warning_summary))
                     .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);
-                                mToggleAccessibilitySwitch.setCheckedNoBeforeCheckedChangeListener(
+                                mToggleAccessibilitySwitch.setCheckedInternal(
                                         false);
-                                updatePreferencesForAccessibilityState();
+                                updateAllPreferences(false);
                             }
                     })
                     .setNegativeButton(android.R.string.cancel,
                         new DialogInterface.OnClickListener() {
                             public void onClick(DialogInterface dialog, int which) {
-                                mToggleAccessibilitySwitch.setCheckedNoBeforeCheckedChangeListener(
+                                mToggleAccessibilitySwitch.setCheckedInternal(
                                         true);
                             }
                         })
                     .create();
-            case DIALOG_ID_ENABLE_SCRIPT_INJECTION:
-                return new AlertDialog.Builder(getActivity())
-                .setTitle(android.R.string.dialog_alert_title)
-                .setIcon(android.R.drawable.ic_dialog_alert)
-                .setMessage(getActivity().getString(
-                        R.string.accessibility_script_injection_security_warning))
-                .setCancelable(true)
-                .setPositiveButton(R.string.accessibility_script_injection_button_allow,
-                    new DialogInterface.OnClickListener() {
-                        public void onClick(DialogInterface dialog, int which) {
-                            Settings.Secure.putInt(getContentResolver(),
-                            Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 1);
-                            mToggleScriptInjectionPreference.setSummary(
-                                    getString(R.string.accessibility_script_injection_allowed));
-                        }
-                })
-
-                .setNegativeButton(R.string.accessibility_script_injection_button_disallow, null)
-                .create();
             case DIALOG_ID_NO_ACCESSIBILITY_SERVICES:
                 return new AlertDialog.Builder(getActivity())
                     .setTitle(R.string.accessibility_service_no_apps_title)
@@ -461,9 +564,10 @@
                                 // dismiss the dialog before launching the activity otherwise
                                 // the dialog removal occurs after onSaveInstanceState which
                                 // triggers an exception
-                                dialog.dismiss();
+                                removeDialog(DIALOG_ID_NO_ACCESSIBILITY_SERVICES);
                                 String screenreaderMarketLink = SystemProperties.get(
-                                    "ro.screenreader.market", DEFAULT_SCREENREADER_MARKET_LINK);
+                                        SYSTEM_PROPERTY_MARKET_URL,
+                                        DEFAULT_SCREENREADER_MARKET_LINK);
                                 Uri marketUri = Uri.parse(screenreaderMarketLink);
                                 Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri);
                                 startActivity(marketIntent);
@@ -503,7 +607,7 @@
         }
     }
 
-    private static ToggleSwitch createActionBarToggleSwitch(Activity activity) {
+    private static ToggleSwitch createAndAddActionBarToggleSwitch(Activity activity) {
         ToggleSwitch toggleSwitch = new ToggleSwitch(activity);
         final int padding = activity.getResources().getDimensionPixelSize(
                 R.dimen.action_bar_switch_padding);
@@ -542,7 +646,7 @@
             super.setChecked(checked);
         }
 
-        public void setCheckedNoBeforeCheckedChangeListener(boolean checked) {
+        public void setCheckedInternal(boolean checked) {
             super.setChecked(checked);
         }
     }
@@ -590,6 +694,17 @@
         public void onPreferenceToggled(String preferenceKey, boolean enabled) {
             Settings.Secure.putInt(getContentResolver(),
                     Settings.Secure.TOUCH_EXPLORATION_ENABLED, enabled ? 1 : 0);
+            if (enabled) {
+                SharedPreferences preferences = getActivity().getPreferences(Context.MODE_PRIVATE);
+                final boolean launchAccessibilityTutorial = !preferences.getBoolean(
+                        KEY_ACCESSIBILITY_TUTORIAL_LAUNCHED_ONCE, false);
+                if (launchAccessibilityTutorial) {
+                    preferences.edit().putBoolean(KEY_ACCESSIBILITY_TUTORIAL_LAUNCHED_ONCE,
+                            true).commit();
+                    Intent intent = new Intent(AccessibilityTutorialActivity.ACTION);
+                    getActivity().startActivity(intent);
+                }
+            }
         }
     }
 
@@ -608,14 +723,9 @@
         private CharSequence mSettingsTitle;
         private Intent mSettingsIntent;
 
-        @Override
-        public void onActivityCreated(Bundle savedInstanceState) {
-            installActionBarToggleSwitch();
-            processArguments();
-            getListView().setDivider(null);
-            getListView().setEnabled(false);
-            super.onActivityCreated(savedInstanceState);
-        }
+        // TODO: Showing sub-sub fragment does not handle the activity title
+        //       so we do it but this is wrong. Do a real fix when there is time.
+        private CharSequence mOldActivityTitle;
 
         @Override
         public void onCreate(Bundle savedInstanceState) {
@@ -629,6 +739,22 @@
                     super.onBindView(view);
                     TextView summaryView = (TextView) view.findViewById(R.id.summary);
                     summaryView.setText(getSummary());
+                    sendAccessibilityEvent(summaryView);
+                }
+
+                private void sendAccessibilityEvent(View view) {
+                    // Since the view is still not attached we create, populate,
+                    // and send the event directly since we do not know when it
+                    // will be attached and posting commands is not as clean.
+                    AccessibilityManager accessibilityManager =
+                        AccessibilityManager.getInstance(getActivity());
+                    if (accessibilityManager.isEnabled()) {
+                        AccessibilityEvent event = AccessibilityEvent.obtain();
+                        event.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+                        view.onInitializeAccessibilityEvent(event);
+                        view.dispatchPopulateAccessibilityEvent(event);
+                        accessibilityManager.sendAccessibilityEvent(event);
+                    }
                 }
             };
             mSummaryPreference.setPersistent(false);
@@ -636,13 +762,31 @@
             preferenceScreen.addPreference(mSummaryPreference);
         }
 
+        @Override
+        public void onViewCreated(View view, Bundle savedInstanceState) {
+            super.onViewCreated(view, savedInstanceState);
+            installActionBarToggleSwitch();
+            processArguments();
+            getListView().setDivider(null);
+            getListView().setEnabled(false);
+        }
+
+        @Override
+        public void onDestroyView() {
+            getActivity().getActionBar().setCustomView(null);
+            if (mOldActivityTitle != null) {
+                getActivity().getActionBar().setTitle(mOldActivityTitle);
+            }
+            super.onDestroyView();
+        }
+
         public abstract void onPreferenceToggled(String preferenceKey, boolean value);
 
         @Override
         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
             super.onCreateOptionsMenu(menu, inflater);
             MenuItem menuItem = menu.add(mSettingsTitle);
-            menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+            menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
             menuItem.setIntent(mSettingsIntent);
         }
 
@@ -668,7 +812,7 @@
             switch (which) {
                 case DialogInterface.BUTTON_POSITIVE:
                     // OK, we got the user consent so set checked.
-                    mToggleSwitch.setCheckedNoBeforeCheckedChangeListener(true);
+                    mToggleSwitch.setCheckedInternal(true);
                     onPreferenceToggled(mPreferenceKey, true);
                     break;
                 case DialogInterface.BUTTON_NEGATIVE:
@@ -680,13 +824,13 @@
         }
 
         private void installActionBarToggleSwitch() {
-            mToggleSwitch = createActionBarToggleSwitch(getActivity());
+            mToggleSwitch = createAndAddActionBarToggleSwitch(getActivity());
             mToggleSwitch.setOnBeforeCheckedChangeListener(new OnBeforeCheckedChangeListener() {
                 @Override
                 public boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked) {
                     if (checked) {
                         if (!TextUtils.isEmpty(mWarningMessage)) {
-                            toggleSwitch.setCheckedNoBeforeCheckedChangeListener(false);
+                            toggleSwitch.setCheckedInternal(false);
                             showDialog(DIALOG_ID_WARNING);
                             return true;
                         }
@@ -707,11 +851,15 @@
 
             // Enabled.
             final boolean enabled = arguments.getBoolean(EXTRA_CHECKED);
-            mToggleSwitch.setCheckedNoBeforeCheckedChangeListener(enabled);
+            mToggleSwitch.setCheckedInternal(enabled);
 
             // Title.
-            String title = arguments.getString(EXTRA_TITLE);
-            getActivity().getActionBar().setTitle(arguments.getCharSequence(EXTRA_TITLE));
+            PreferenceActivity activity = (PreferenceActivity) getActivity();
+            if (!activity.onIsMultiPane() || activity.onIsHidingHeaders()) {
+                mOldActivityTitle = getActivity().getTitle();
+                String title = arguments.getString(EXTRA_TITLE);
+                getActivity().getActionBar().setTitle(arguments.getCharSequence(EXTRA_TITLE));
+            }
 
             // Summary.
             String summary = arguments.getString(EXTRA_SUMMARY);
diff --git a/src/com/android/settings/AccessibilityTutorialActivity.java b/src/com/android/settings/AccessibilityTutorialActivity.java
index 9ea9917..da8350c 100644
--- a/src/com/android/settings/AccessibilityTutorialActivity.java
+++ b/src/com/android/settings/AccessibilityTutorialActivity.java
@@ -47,6 +47,8 @@
 import android.widget.TextView;
 import android.widget.ViewAnimator;
 
+import com.android.settings.R;
+
 import java.util.List;
 
 /**
@@ -54,6 +56,10 @@
  * available in Touch Exploration.
  */
 public class AccessibilityTutorialActivity extends Activity {
+
+    /** Intent action for launching this activity. */
+    public static final String ACTION = "android.settings.ACCESSIBILITY_TUTORIAL";
+
     /** Instance state saving constant for the active module. */
     private static final String KEY_ACTIVE_MODULE = "active_module";
 
diff --git a/src/com/android/settings/DateTimeSettings.java b/src/com/android/settings/DateTimeSettings.java
index 31e6a33..3935d59 100644
--- a/src/com/android/settings/DateTimeSettings.java
+++ b/src/com/android/settings/DateTimeSettings.java
@@ -354,6 +354,8 @@
 
         c.set(Calendar.HOUR_OF_DAY, hourOfDay);
         c.set(Calendar.MINUTE, minute);
+        c.set(Calendar.SECOND, 0);
+        c.set(Calendar.MILLISECOND, 0);
         long when = c.getTimeInMillis();
 
         if (when / 1000 < Integer.MAX_VALUE) {
diff --git a/src/com/android/settings/PrivacySettings.java b/src/com/android/settings/PrivacySettings.java
index 28dc93a..6759500 100644
--- a/src/com/android/settings/PrivacySettings.java
+++ b/src/com/android/settings/PrivacySettings.java
@@ -43,11 +43,13 @@
     private static final String BACKUP_DATA = "backup_data";
     private static final String AUTO_RESTORE = "auto_restore";
     private static final String CONFIGURE_ACCOUNT = "configure_account";
+    private static final String LOCAL_BACKUP_PASSWORD = "local_backup_password";
     private IBackupManager mBackupManager;
     private CheckBoxPreference mBackup;
     private CheckBoxPreference mAutoRestore;
     private Dialog mConfirmDialog;
     private PreferenceScreen mConfigure;
+    private PreferenceScreen mPassword;
 
     private static final int DIALOG_ERASE_BACKUP = 2;
     private int mDialogType;
@@ -64,6 +66,7 @@
         mBackup = (CheckBoxPreference) screen.findPreference(BACKUP_DATA);
         mAutoRestore = (CheckBoxPreference) screen.findPreference(AUTO_RESTORE);
         mConfigure = (PreferenceScreen) screen.findPreference(CONFIGURE_ACCOUNT);
+        mPassword = (PreferenceScreen) screen.findPreference(LOCAL_BACKUP_PASSWORD);
 
         // Vendor specific
         if (getActivity().getPackageManager().
@@ -158,7 +161,9 @@
         mConfigure.setEnabled(configureEnabled);
         mConfigure.setIntent(configIntent);
         setConfigureSummary(configSummary);
-    }
+
+        updatePasswordSummary();
+}
 
     private void setConfigureSummary(String summary) {
         if (summary != null) {
@@ -178,6 +183,18 @@
         }
     }
 
+    private void updatePasswordSummary() {
+        try {
+            if (mBackupManager.hasBackupPassword()) {
+                mPassword.setSummary(R.string.local_backup_password_summary_change);
+            } else {
+                mPassword.setSummary(R.string.local_backup_password_summary_none);
+            }
+        } catch (RemoteException e) {
+            // Not much we can do here
+        }
+    }
+
     public void onClick(DialogInterface dialog, int which) {
         if (which == DialogInterface.BUTTON_POSITIVE) {
             //updateProviders();
diff --git a/src/com/android/settings/SetFullBackupPassword.java b/src/com/android/settings/SetFullBackupPassword.java
new file mode 100644
index 0000000..9f3f29f
--- /dev/null
+++ b/src/com/android/settings/SetFullBackupPassword.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 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.Activity;
+import android.app.backup.IBackupManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class SetFullBackupPassword extends Activity {
+    static final String TAG = "SetFullBackupPassword";
+
+    IBackupManager mBackupManager;
+    TextView mCurrentPw, mNewPw, mConfirmNewPw;
+    Button mCancel, mSet;
+
+    OnClickListener mButtonListener = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            if (v == mSet) {
+                final String curPw = mCurrentPw.getText().toString();
+                final String newPw = mNewPw.getText().toString();
+                final String confirmPw = mConfirmNewPw.getText().toString();
+
+                if (!newPw.equals(confirmPw)) {
+                    // Mismatch between new pw and its confirmation re-entry
+Log.i(TAG, "password mismatch");
+                    Toast.makeText(SetFullBackupPassword.this,
+                            "!!! New password and confirmation don't match !!!",
+                            Toast.LENGTH_LONG).show();
+                    return;
+                }
+
+                // TODO: should we distinguish cases of has/hasn't set a pw before?
+
+                if (setBackupPassword(curPw, newPw)) {
+                    // success
+Log.i(TAG, "password set successfully");
+                    Toast.makeText(SetFullBackupPassword.this,
+                            "!!! New backup password set !!!",
+                            Toast.LENGTH_LONG).show();
+                    finish();
+                } else {
+                    // failure -- bad existing pw, usually
+Log.i(TAG, "failure; password mismatch?");
+                    Toast.makeText(SetFullBackupPassword.this,
+                            "!!! Failure setting backup password !!!",
+                            Toast.LENGTH_LONG).show();
+                }
+            } else if (v == mCancel) {
+                finish();
+            } else {
+                Log.w(TAG, "Click on unknown view");
+            }
+        }
+    };
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mBackupManager = IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
+
+        setContentView(R.layout.set_backup_pw);
+
+        mCurrentPw = (TextView) findViewById(R.id.current_backup_pw);
+        mNewPw = (TextView) findViewById(R.id.new_backup_pw);
+        mConfirmNewPw = (TextView) findViewById(R.id.confirm_new_backup_pw);
+
+        mCancel = (Button) findViewById(R.id.backup_pw_cancel_button);
+        mSet = (Button) findViewById(R.id.backup_pw_set_button);
+
+        mCancel.setOnClickListener(mButtonListener);
+        mSet.setOnClickListener(mButtonListener);
+    }
+
+    private boolean setBackupPassword(String currentPw, String newPw) {
+        try {
+            return mBackupManager.setBackupPassword(currentPw, newPw);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to communicate with backup manager");
+            return false;
+        }
+    }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java
index de96d71..838e7b1 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java
@@ -82,7 +82,7 @@
 
                 String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
                 if (TextUtils.isEmpty(name)) {
-                    name = device != null ? device.getName() :
+                    name = device != null ? device.getAliasName() :
                             context.getString(android.R.string.unknownName);
                 }
 
diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java
index 1a0965c..4d96140 100644
--- a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java
+++ b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java
@@ -130,7 +130,7 @@
     }
 
     private String createConnectionDisplayText() {
-        String mRemoteName = mDevice != null ? mDevice.getName() : null;
+        String mRemoteName = mDevice != null ? mDevice.getAliasName() : null;
 
         if (mRemoteName == null) mRemoteName = getString(R.string.unknown);
         String mMessage1 = getString(R.string.bluetooth_connection_dialog_text,
@@ -139,7 +139,7 @@
     }
 
     private String createPbapDisplayText() {
-        String mRemoteName = mDevice != null ? mDevice.getName() : null;
+        String mRemoteName = mDevice != null ? mDevice.getAliasName() : null;
 
         if (mRemoteName == null) mRemoteName = getString(R.string.unknown);
         String mMessage1 = getString(R.string.bluetooth_pb_acceptance_dialog_text,
diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
index c769ba6..51055af 100644
--- a/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
+++ b/src/com/android/settings/bluetooth/BluetoothPermissionRequest.java
@@ -83,7 +83,7 @@
                 Notification notification = new Notification(android.R.drawable.stat_sys_data_bluetooth,
                     context.getString(R.string.bluetooth_connection_permission_request),
                     System.currentTimeMillis());
-                String deviceName = device != null ? device.getName() : null;
+                String deviceName = device != null ? device.getAliasName() : null;
                 notification.setLatestEventInfo(context,
                     context.getString(R.string.bluetooth_connection_permission_request),
                     context.getString(R.string.bluetooth_connection_notif_message, deviceName),
diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
index 846c379..8082314 100644
--- a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
+++ b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
@@ -326,8 +326,8 @@
                 mName = mDevice.getAddress();
             } else {
                 mName = name;
+                mDevice.setAlias(name);
             }
-            // TODO: save custom device name in preferences
             dispatchAttributesChanged();
         }
     }
@@ -338,7 +338,7 @@
     }
 
     private void fetchName() {
-        mName = mDevice.getName();
+        mName = mDevice.getAliasName();
 
         if (TextUtils.isEmpty(mName)) {
             mName = mDevice.getAddress();
@@ -422,7 +422,7 @@
         mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles);
 
         if (DEBUG) {
-            Log.e(TAG, "updating profiles for " + mDevice.getName());
+            Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
             BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
 
             if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java b/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java
index ab71ece..f293f50 100644
--- a/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java
+++ b/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java
@@ -104,7 +104,7 @@
             return cachedDevice.getName();
         }
 
-        String name = device.getName();
+        String name = device.getAliasName();
         if (name != null) {
             return name;
         }
diff --git a/src/com/android/settings/bluetooth/DockEventReceiver.java b/src/com/android/settings/bluetooth/DockEventReceiver.java
index 6ecbef6..d18348f 100644
--- a/src/com/android/settings/bluetooth/DockEventReceiver.java
+++ b/src/com/android/settings/bluetooth/DockEventReceiver.java
@@ -54,7 +54,7 @@
 
         if (DEBUG) {
             Log.d(TAG, "Action: " + intent.getAction() + " State:" + state + " Device: "
-                    + (device == null ? "null" : device.getName()));
+                    + (device == null ? "null" : device.getAliasName()));
         }
 
         if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())
diff --git a/src/com/android/settings/bluetooth/DockService.java b/src/com/android/settings/bluetooth/DockService.java
index b457706..f0644c3 100644
--- a/src/com/android/settings/bluetooth/DockService.java
+++ b/src/com/android/settings/bluetooth/DockService.java
@@ -424,7 +424,7 @@
 
         if (DEBUG) {
             Log.d(TAG, "Action: " + intent.getAction() + " State:" + state
-                    + " Device: " + (device == null ? "null" : device.getName()));
+                    + " Device: " + (device == null ? "null" : device.getAliasName()));
         }
 
         if (device == null) {