Merge "Let extra apps launch channel settings."
diff --git a/res/layout/preference_tts_engine.xml b/res/layout/preference_tts_engine.xml
index 8f4036b..19401f0 100644
--- a/res/layout/preference_tts_engine.xml
+++ b/res/layout/preference_tts_engine.xml
@@ -31,17 +31,4 @@
         android:maxLines="2"
         android:textAppearance="?android:attr/textAppearanceListItem"
         android:ellipsize="marquee"/>
-
-    <ImageView
-        android:id="@+id/tts_engine_settings"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-        android:src="@drawable/ic_settings"
-        android:contentDescription="@string/tts_engine_settings_button"
-        android:layout_centerVertical="true"
-        android:clickable="true"
-        android:focusable="true"
-        android:background="?android:attr/selectableItemBackground"/>
 </LinearLayout>
diff --git a/res/layout/search_inline_switch_item.xml b/res/layout/search_inline_switch_item.xml
index 40639b0..75943c4 100644
--- a/res/layout/search_inline_switch_item.xml
+++ b/res/layout/search_inline_switch_item.xml
@@ -48,7 +48,6 @@
             android:layout_height="wrap_content"
             android:textAppearance="?android:attr/textAppearanceListItemSecondary"
             android:textColor="?android:attr/textColorSecondary"
-            android:maxLength="60"
             android:maxLines="10"/>
 
         <include layout="@layout/search_breadcrumb_view"/>
@@ -60,4 +59,4 @@
         android:layout_height="match_parent"
         android:gravity="top"
         android:paddingStart="16dp"/>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7a3be0c..4b93d8c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1634,6 +1634,8 @@
     <string name="wifi_cellular_data_fallback_summary">Use cellular data when Wi\u2011Fi has no Internet access. Data usage may apply.</string>
     <!-- Action bar text message to manually add a wifi network [CHAR LIMIT=20]-->
     <string name="wifi_add_network">Add network</string>
+    <!-- Action bar title to open additional Wi-Fi settings-->
+    <string name="wifi_configure_settings_preference_title">Wi\u2011Fi preferences</string>
     <!-- Header for the list of wifi networks-->
     <string name="wifi_access_points">Wi\u2011Fi networks</string>
     <!-- Menu option to do WPS Push Button [CHAR LIMIT=25]-->
@@ -4622,9 +4624,20 @@
         behalf.  It comes from the <xliff:g id="voice_input_service_app_name">%s</xliff:g>
         application.  Enable the use of this service?</string>
 
-    <!-- On main TTS Settings screen, in default settings section, reset speech rate for synthesized voice to 1x speech rate.-->
+    <!-- [CHAR LIMIT=50] The text for the settings section that is used to set a preferred text to speech engine -->
+    <string name="tts_engine_preference_title">Preferred engine</string>
+    <!-- [CHAR LIMIT=50] The text for a settings screen of the currently set text to speech engine -->
+    <string name="tts_engine_settings_title">Engine settings</string>
+    <!-- [CHAR LIMIT=50] The text for a button that goes to the speech rate and pitch settings for text to speech. -->
+    <string name="tts_sliders_title">Speech rate &amp; pitch</string>
+    <!-- [CHAR LIMIT=50] Name for the general text to speech settings section. -->
+    <string name="tts_engine_section_title">Engine</string>
+    <!-- [CHAR LIMIT=50] Name for the button that goes to the voice selection screen. -->
+    <string name="tts_install_voice_title">Voices</string>
+
+    <!-- Reset speech rate for synthesized voice to 1x speech rate in the text to speech settings.-->
     <string name="tts_reset_speech_rate_title">Reset speech rate</string>
-    <!-- On main TTS Settings screen, summary for reset speech rate for synthesized voice -->
+    <!-- Summary for reset speech rate for synthesized voice in the text to speech settings.-->
     <string name="tts_reset_speech_rate_summary">Reset the speed at which the text is spoken to normal.</string>
 
     <!-- Power Control Widget -->
@@ -8070,6 +8083,11 @@
         <item quantity="one"><xliff:g id="count">%d</xliff:g> app allowed access to your camera by your admin</item>
         <item quantity="other"><xliff:g id="count">%d</xliff:g> apps allowed access to your camera by your admin</item>
     </plurals>
+    <!-- Label indicating how many apps were set as default defaults for common actions (e.g. open browser, send e-mail) by the admin. [CHAR LIMIT=NONE] -->
+    <plurals name="enterprise_privacy_number_enterprise_set_default_apps">
+        <item quantity="one"><xliff:g id="count">%d</xliff:g> default app set by your admin</item>
+        <item quantity="other"><xliff:g id="count">%d</xliff:g> default apps set by your admin</item>
+    </plurals>
     <!-- Label explaining that an always-on VPN was set by the admin for the entire device. [CHAR LIMIT=NONE] -->
     <string name="enterprise_privacy_always_on_vpn_device">Always-on VPN turned on</string>
     <!-- Label explaining that an always-on VPN was set by the admin in the personal profile. [CHAR LIMIT=NONE] -->
@@ -8078,6 +8096,10 @@
     <string name="enterprise_privacy_always_on_vpn_work">Always-on VPN turned on in your work profile</string>
     <!-- Label explaining that a global HTTP proxy was set by the admin. [CHAR LIMIT=NONE] -->
     <string name="enterprise_privacy_global_http_proxy">Global HTTP proxy set</string>
+    <!-- Label explaining that the admin can lock the device and change the user's password. [CHAR LIMIT=NONE] -->
+    <string name="enterprise_privacy_lock_device">Admin can lock device and reset password</string>
+    <!-- Label explaining that the admin can wipe the device remotely. [CHAR LIMIT=NONE] -->
+    <string name="enterprise_privacy_wipe_device">Admin can delete all device data</string>
     <!-- Message indicating that the device is enterprise-managed by a Device Owner [CHAR LIMIT=NONE] -->
     <string name="do_disclosure_generic">This device is managed.</string>
     <!-- Message indicating that the device is enterprise-managed by a Device Owner [CHAR LIMIT=NONE] -->
@@ -8121,9 +8143,29 @@
     <!-- On status for the automatic storage manager. [CHAR_LIMIT=10] -->
     <string name="storage_manager_indicator_on">On</string>
 
+    <!-- Title of games app storage screen [CHAR LIMIT=30] -->
+    <string name="game_storage_settings">Games</string>
+
     <!-- UI webview setting: WebView uninstalled-for-user explanatory text [CHAR LIMIT=30] -->
     <string name="webview_uninstalled_for_user">Uninstalled for user <xliff:g id="user" example="John Doe">%s</xliff:g>\n</string>
     <!-- UI webview setting: WebView disabled-for-user explanatory text [CHAR LIMIT=30] -->
     <string name="webview_disabled_for_user">Disabled for user <xliff:g id="user" example="John Doe">%s</xliff:g>\n</string>
 
+    <!-- AutoFill strings -->
+    <!-- Preference label for the auto-fill app. [CHAR LIMIT=60] -->
+    <string name="autofill_app">Autofill app</string>
+    <!-- Keywords for the auto-fill feature. [CHAR LIMIT=NONE] -->
+    <string name="autofill_keywords">auto, fill, autofill</string>
+    <!-- Title of the warning dialog for setting the auto-fill app. [CHAR_LIMIT=NONE] -->
+    <string name="autofill_confirmation_message">
+        Make <xliff:g id="app_name">%1$s</xliff:g> your autofill app? <xliff:g id="app_name">%1$s</xliff:g> will be able to read your screen and fill fields in other apps.
+    </string>
+
+    <!-- Name of setting for switching device theme [CHAR LIMIT=60] -->
+    <string name="device_theme">Device theme</string>
+    <!-- Name of default device theme [CHAR LIMIT=60] -->
+    <string name="default_theme">Default</string>
+    <!-- Temporary reboot string, will be removed -->
+    <string name="change_theme_reboot" translatable="false">Changing the theme requires a restart.</string>
+
 </resources>
diff --git a/res/xml/app_default_settings.xml b/res/xml/app_default_settings.xml
index 9b585c1..27200e7 100644
--- a/res/xml/app_default_settings.xml
+++ b/res/xml/app_default_settings.xml
@@ -26,12 +26,19 @@
         android:fragment="com.android.settings.applications.ManageAssist"
         android:order="-20"/>
 
+    <com.android.settings.applications.DefaultAutoFillPreference
+        android:key="default_autofill"
+        android:title="@string/autofill_app"
+        android:summary="@string/app_list_preference_none"
+        settings:keywords="@string/autofill_keywords"
+        android:order="-19"/>
+
     <Preference
         android:key="default_browser"
         android:title="@string/default_browser_title"
         android:summary="@string/default_browser_title_none"
         android:fragment="com.android.settings.applications.defaultapps.DefaultBrowserPicker"
-        android:order="-19">
+        android:order="-18">
         <extra android:name="for_work" android:value="false"/>
     </Preference>
 
@@ -40,35 +47,32 @@
         android:title="@string/home_app"
         android:summary="@string/no_default_home"
         settings:keywords="@string/keywords_home"
-        android:fragment="com.android.settings.applications.defaultapps.DefaultHomePicker"
-        android:order="-18"/>
+        android:order="-17"/>
 
     <Preference
         android:key="default_phone_app"
         android:title="@string/default_phone_title"
         android:fragment="com.android.settings.applications.defaultapps.DefaultPhonePicker"
         settings:keywords="@string/keywords_default_phone_app"
-        android:order="-17"/>
+        android:order="-16"/>
 
     <Preference
         android:key="default_sms_app"
         android:title="@string/sms_application_title"
         android:fragment="com.android.settings.applications.defaultapps.DefaultSmsPicker"
         settings:keywords="@string/keywords_more_default_sms_app"
-        android:order="-16"/>
+        android:order="-15"/>
 
     <Preference
         android:key="default_emergency_app"
         android:title="@string/default_emergency_app"
         settings:keywords="@string/keywords_emergency_app"
-        android:fragment="com.android.settings.applications.defaultapps.DefaultEmergencyPicker"
-        android:order="-15"/>
+        android:order="-14"/>
 
     <Preference
         android:key="default_notification_asst_app"
         android:title="@string/default_notification_assistant"
-        android:fragment="com.android.settings.applications.defaultapps.DefaultNotificationAssistantPicker"
-        android:order="-14"/>
+        android:order="-13"/>
 
     <Preference
         android:key="domain_urls"
@@ -102,4 +106,4 @@
         android:fragment="com.android.settings.applications.SpecialAccessSettings"
         android:title="@string/special_access"/>
 
-</PreferenceScreen>
\ No newline at end of file
+</PreferenceScreen>
diff --git a/res/xml/enterprise_privacy_settings.xml b/res/xml/enterprise_privacy_settings.xml
index 4bb2a8c..cc63d5f 100644
--- a/res/xml/enterprise_privacy_settings.xml
+++ b/res/xml/enterprise_privacy_settings.xml
@@ -76,6 +76,10 @@
                 settings:allowDividerBelow="true"
                 settings:multiLine="true"/>
         <com.android.settings.DividerPreference
+                android:key="number_enterprise_set_default_apps"
+                settings:allowDividerBelow="true"
+                settings:multiLine="true"/>
+        <com.android.settings.DividerPreference
                 android:key="always_on_vpn_primary_user"
                 settings:allowDividerBelow="true"
                 settings:multiLine="true"/>
@@ -92,5 +96,15 @@
     </PreferenceCategory>
 
     <PreferenceCategory android:title="@string/enterprise_privacy_device_access_category">
+        <com.android.settings.DividerPreference
+                android:key="lock_device"
+                android:title="@string/enterprise_privacy_lock_device"
+                settings:allowDividerBelow="true"
+                settings:multiLine="true"/>
+        <com.android.settings.DividerPreference
+                android:key="wipe_device"
+                android:title="@string/enterprise_privacy_wipe_device"
+                settings:allowDividerBelow="true"
+                settings:multiLine="true"/>
     </PreferenceCategory>
 </PreferenceScreen>
diff --git a/res/xml/ia_display_settings.xml b/res/xml/ia_display_settings.xml
index 946466b..ed026f1 100644
--- a/res/xml/ia_display_settings.xml
+++ b/res/xml/ia_display_settings.xml
@@ -105,6 +105,11 @@
                 android:title="@string/tap_to_wake"
                 android:summary="@string/tap_to_wake_summary" />
 
+        <ListPreference
+                android:key="theme"
+                android:title="@string/device_theme"
+                android:summary="%s" />
+
         <Preference
                 android:key="wifi_display"
                 android:title="@string/wifi_display_settings_title"
diff --git a/res/xml/tts_engine_picker.xml b/res/xml/tts_engine_picker.xml
new file mode 100644
index 0000000..61e3e7e
--- /dev/null
+++ b/res/xml/tts_engine_picker.xml
@@ -0,0 +1,23 @@
+<?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"
+        android:title="@string/tts_settings_title">
+
+    <PreferenceCategory android:key="tts_engine_preference_category"
+        android:title="@string/tts_engine_preference_title"/>
+
+</PreferenceScreen>
diff --git a/res/xml/tts_engine_settings.xml b/res/xml/tts_engine_settings.xml
deleted file mode 100644
index d00c102..0000000
--- a/res/xml/tts_engine_settings.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="">
-    <ListPreference
-        android:key="tts_default_lang"
-        android:title="@string/tts_default_lang_title"
-        android:summary="@string/tts_default_lang_summary"
-        android:order="100" />
-
-    <Preference
-        android:key="tts_engine_settings"
-        android:persistent="false"
-        android:title="@string/tts_engine_settings_title"
-        android:order="200" />
-
-    <Preference
-        android:key="tts_install_data"
-        android:persistent="false"
-        android:title="@string/tts_install_data_title"
-        android:summary="@string/tts_install_data_summary"
-        android:order="300" />
-</PreferenceScreen>
diff --git a/res/xml/tts_settings.xml b/res/xml/tts_settings.xml
index 31d2b17..b02a749 100644
--- a/res/xml/tts_settings.xml
+++ b/res/xml/tts_settings.xml
@@ -17,38 +17,40 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
         android:title="@string/tts_settings_title">
 
-    <!-- The contents of this category are filled in by the Java code
-         based on the list of available engines. -->
-    <PreferenceCategory android:key="tts_engine_preference_section"
-        android:title="@string/tts_engine_preference_section_title" />
+    <PreferenceCategory android:key="tts_engine_section"
+         android:title="@string/tts_engine_section_title">
+
+    <com.android.settingslib.RestrictedPreference android:key="tts_engine_preference"
+        android:title="@string/tts_engine_preference_title"
+        android:fragment="com.android.settings.tts.TtsEnginePreferenceFragment"/>
+
+    <Preference
+        android:key="tts_engine_settings"
+        android:persistent="false"
+        android:title="@string/tts_engine_settings_title"
+        android:order="200" />
+
+    <Preference
+        android:key="tts_install_data"
+        android:persistent="false"
+        android:title="@string/tts_install_voice_title"
+        android:order="300" />
+
+    </PreferenceCategory>
 
     <PreferenceCategory android:key="tts_general_section"
          android:title="@string/tts_general_section_title">
-        <!-- The max value for seek bars here should be kept in sync
-             with the max value specified in TextToSpeechSettings class. -->
-        <com.android.settings.SeekBarPreference
-            android:key="tts_default_rate"
-            android:title="@string/tts_default_rate_title"
-            android:summary="@string/tts_default_rate_summary"
-            android:defaultValue="50"
-            android:max="600"/>
 
-        <com.android.settings.SeekBarPreference
-            android:key="tts_default_pitch"
-            android:title="@string/tts_default_pitch_title"
-            android:summary="@string/tts_default_pitch_summary"
-            android:defaultValue="100"
-            android:max="400"/>
+    <com.android.settingslib.RestrictedPreference
+        android:key="tts_sliders"
+        android:title="@string/tts_sliders_title"
+        android:fragment="com.android.settings.tts.TtsSlidersFragment"/>
 
-        <Preference android:key="reset_speech_rate"
-            android:persistent="false"
-            android:title="@string/tts_reset_speech_rate_title"
-            android:summary="@string/tts_reset_speech_rate_summary" />
-
-        <Preference android:key="reset_speech_pitch"
-            android:persistent="false"
-            android:title="@string/tts_reset_speech_pitch_title"
-            android:summary="@string/tts_reset_speech_pitch_summary" />
+        <ListPreference
+            android:key="tts_default_lang"
+            android:title="@string/tts_default_lang_title"
+            android:summary="@string/tts_default_lang_summary"
+            android:persistent="false" />
 
         <Preference android:key="tts_play_example"
             android:persistent="false"
diff --git a/res/xml/tts_sliders.xml b/res/xml/tts_sliders.xml
new file mode 100644
index 0000000..3c767b0
--- /dev/null
+++ b/res/xml/tts_sliders.xml
@@ -0,0 +1,46 @@
+<?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"
+        android:title="@string/tts_settings_title">
+
+        <!-- The max value for seek bars here should be kept in sync
+             with the max value specified in TextToSpeechSettings class. -->
+        <com.android.settings.SeekBarPreference
+            android:key="tts_default_rate"
+            android:title="@string/tts_default_rate_title"
+            android:summary="@string/tts_default_rate_summary"
+            android:defaultValue="50"
+            android:max="600"/>
+
+        <com.android.settings.SeekBarPreference
+            android:key="tts_default_pitch"
+            android:title="@string/tts_default_pitch_title"
+            android:summary="@string/tts_default_pitch_summary"
+            android:defaultValue="100"
+            android:max="400"/>
+
+        <Preference android:key="reset_speech_rate"
+            android:persistent="false"
+            android:title="@string/tts_reset_speech_rate_title"
+            android:summary="@string/tts_reset_speech_rate_summary" />
+
+        <Preference android:key="reset_speech_pitch"
+            android:persistent="false"
+            android:title="@string/tts_reset_speech_pitch_title"
+            android:summary="@string/tts_reset_speech_pitch_summary" />
+
+</PreferenceScreen>
diff --git a/res/xml/wifi_configure_settings.xml b/res/xml/wifi_configure_settings.xml
index a43ea87..1e04749 100644
--- a/res/xml/wifi_configure_settings.xml
+++ b/res/xml/wifi_configure_settings.xml
@@ -17,11 +17,6 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
         android:title="@string/wifi_configure_titlebar">
 
-    <Preference
-        android:key="saved_networks"
-        android:title="@string/wifi_saved_access_points_label"
-        android:fragment="com.android.settings.wifi.SavedAccessPointsWifiSettings" />
-
     <!-- android:dependency="enable_wifi" -->
     <ListPreference
         android:key="sleep_policy"
diff --git a/res/xml/wifi_settings.xml b/res/xml/wifi_settings.xml
index b4ab126..15743ea 100644
--- a/res/xml/wifi_settings.xml
+++ b/res/xml/wifi_settings.xml
@@ -14,14 +14,23 @@
      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"
+<PreferenceScreen
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
         android:title="@string/wifi_settings"
         settings:keywords="@string/keywords_wifi">
 
-    <!-- Needed so PreferenceGroupAdapter allows AccessPointPreference to be
-         recycled. Removed in onResume -->
-    <com.android.settings.wifi.LongPressAccessPointPreference
-        android:key="dummy" />
+    <PreferenceCategory android:key="access_points"/>
 
+    <PreferenceCategory android:key="additional_settings">
+        <Preference
+                android:key="configure_settings"
+                android:title="@string/wifi_configure_settings_preference_title"
+                android:fragment="com.android.settings.wifi.ConfigureWifiSettings" />
+
+        <Preference
+                android:key="saved_networks"
+                android:title="@string/wifi_saved_access_points_label"
+                android:fragment="com.android.settings.wifi.SavedAccessPointsWifiSettings" />
+    </PreferenceCategory>
 </PreferenceScreen>
diff --git a/src/com/android/settings/AppListPreference.java b/src/com/android/settings/AppListPreference.java
index 4cf4996..8585454 100644
--- a/src/com/android/settings/AppListPreference.java
+++ b/src/com/android/settings/AppListPreference.java
@@ -61,6 +61,8 @@
     protected final boolean mForWork;
     protected final int mUserId;
 
+
+    private boolean mSavesState = true;
     private Drawable[] mEntryDrawables;
     private boolean mShowItemNone = false;
     private CharSequence[] mSummaries;
@@ -130,6 +132,10 @@
                 : UserHandle.myUserId();
     }
 
+    public void setSavesState(boolean savesState) {
+        mSavesState = savesState;
+    }
+
     public void setShowItemNone(boolean showItemNone) {
         mShowItemNone = showItemNone;
     }
@@ -261,12 +267,16 @@
     @Override
     protected Parcelable onSaveInstanceState() {
         Parcelable superState = super.onSaveInstanceState();
-        return new SavedState(getEntryValues(), getValue(), mSummaries, mShowItemNone, superState);
+        if (mSavesState) {
+            return new SavedState(getEntryValues(), getValue(), mSummaries, mShowItemNone, superState);
+        } else {
+            return superState;
+        }
     }
 
     @Override
     protected void onRestoreInstanceState(Parcelable state) {
-        if (state instanceof SavedState) {
+        if (mSavesState || state instanceof SavedState) {
             SavedState savedState = (SavedState) state;
             mShowItemNone = savedState.showItemNone;
             setPackageNames(savedState.entryValues, savedState.value);
diff --git a/src/com/android/settings/DateTimeSettings.java b/src/com/android/settings/DateTimeSettings.java
index fc47fef..326f200 100644
--- a/src/com/android/settings/DateTimeSettings.java
+++ b/src/com/android/settings/DateTimeSettings.java
@@ -56,11 +56,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index 91b193d..320cb83 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -35,6 +35,7 @@
 import com.android.settings.display.NightModePreferenceController;
 import com.android.settings.display.ScreenSaverPreferenceController;
 import com.android.settings.display.TapToWakePreferenceController;
+import com.android.settings.display.ThemePreferenceController;
 import com.android.settings.display.TimeoutPreferenceController;
 import com.android.settings.display.VrDisplayPreferenceController;
 import com.android.settings.display.WallpaperPreferenceController;
@@ -68,11 +69,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_DISPLAY;
-    }
-
-    @Override
     protected int getPreferenceScreenResId() {
         if (mDashboardFeatureProvider.isEnabled()) {
             return R.xml.ia_display_settings;
@@ -97,6 +93,7 @@
         controllers.add(new TimeoutPreferenceController(context));
         controllers.add(new VrDisplayPreferenceController(context));
         controllers.add(new WallpaperPreferenceController(context));
+        controllers.add(new ThemePreferenceController(context));
         return controllers;
     }
 
@@ -182,6 +179,7 @@
                     new TimeoutPreferenceController(context).updateNonIndexableKeys(result);
                     new VrDisplayPreferenceController(context).updateNonIndexableKeys(result);
                     new WallpaperPreferenceController(context).updateNonIndexableKeys(result);
+                    new ThemePreferenceController(context).updateNonIndexableKeys(result);
 
                     return result;
                 }
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index ab61fdd..360d258 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -128,6 +128,7 @@
     public static class OtherSoundSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ManageDomainUrlsActivity extends SettingsActivity { /* empty */ }
     public static class AutomaticStorageManagerSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class GamesStorageActivity extends SettingsActivity { /* empty */ }
 
     public static class TopLevelSettings extends SettingsActivity { /* empty */ }
     public static class ApnSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/accounts/AccountDetailDashboardFragment.java b/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
index 12f78ff..8143e96 100644
--- a/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
@@ -22,15 +22,14 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.support.annotation.VisibleForTesting;
-
 import android.support.v7.preference.Preference;
+
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.accounts.AuthenticatorHelper;
-import com.android.settingslib.drawer.CategoryKey;
 import com.android.settingslib.drawer.Tile;
 
 import java.util.ArrayList;
@@ -62,8 +61,8 @@
         Bundle args = getArguments();
         final Activity activity = getActivity();
         UserHandle userHandle = Utils.getSecureTargetUser(activity.getActivityToken(),
-            (UserManager) getSystemService(Context.USER_SERVICE), args,
-            activity.getIntent().getExtras());
+                (UserManager) getSystemService(Context.USER_SERVICE), args,
+                activity.getIntent().getExtras());
         if (args != null) {
             if (args.containsKey(KEY_ACCOUNT)) {
                 mAccount = args.getParcelable(KEY_ACCOUNT);
@@ -94,11 +93,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_ACCOUNT;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
@@ -142,6 +136,6 @@
         }
         final AuthenticatorHelper helper = new AuthenticatorHelper(context, userHandle, null);
         headerPreference.setIcon(helper.getDrawableForType(context, mAccountType));
-   }
+    }
 
 }
\ No newline at end of file
diff --git a/src/com/android/settings/accounts/UserAndAccountDashboardFragment.java b/src/com/android/settings/accounts/UserAndAccountDashboardFragment.java
index 4113152..dbc0292 100644
--- a/src/com/android/settings/accounts/UserAndAccountDashboardFragment.java
+++ b/src/com/android/settings/accounts/UserAndAccountDashboardFragment.java
@@ -25,7 +25,6 @@
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.drawer.CategoryKey;
 import com.android.settingslib.drawer.Tile;
 
 import java.util.ArrayList;
@@ -45,11 +44,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_ACCOUNT;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/applications/ActivityInfoWrapper.java b/src/com/android/settings/applications/ActivityInfoWrapper.java
index c6920ca..4cb6e68 100644
--- a/src/com/android/settings/applications/ActivityInfoWrapper.java
+++ b/src/com/android/settings/applications/ActivityInfoWrapper.java
@@ -24,7 +24,7 @@
 public interface ActivityInfoWrapper {
 
     /**
-     * Returns the resizeMode of the activity.
+     * Returns whether this activity supports picture-in-picture.
      */
-    int getResizeMode();
+    boolean supportsPictureInPicture();
 }
diff --git a/src/com/android/settings/applications/ActivityInfoWrapperImpl.java b/src/com/android/settings/applications/ActivityInfoWrapperImpl.java
index e7a20bc..b70a1e8 100644
--- a/src/com/android/settings/applications/ActivityInfoWrapperImpl.java
+++ b/src/com/android/settings/applications/ActivityInfoWrapperImpl.java
@@ -27,7 +27,7 @@
     }
 
     @Override
-    public int getResizeMode() {
-        return mInfo.resizeMode;
+    public boolean supportsPictureInPicture() {
+        return mInfo.supportsPictureInPicture();
     }
 }
diff --git a/src/com/android/settings/applications/AdvancedAppSettings.java b/src/com/android/settings/applications/AdvancedAppSettings.java
index e8b621a..e59f94b 100644
--- a/src/com/android/settings/applications/AdvancedAppSettings.java
+++ b/src/com/android/settings/applications/AdvancedAppSettings.java
@@ -31,7 +31,6 @@
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settingslib.drawer.CategoryKey;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -42,11 +41,6 @@
     static final String TAG = "AdvancedAppSettings";
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_APPS_DEFAULT;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
index 5942897..5dda9c1 100644
--- a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
+++ b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
@@ -25,7 +25,6 @@
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.drawer.CategoryKey;
 
 import java.util.Arrays;
 import java.util.List;
@@ -40,11 +39,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_APPS;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java
index 331d384..52f1da5 100644
--- a/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java
+++ b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java
@@ -17,7 +17,6 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.RemoteException;
@@ -33,11 +32,11 @@
 
     private final String[] mPermissions;
     private final PackageManagerWrapper mPackageManager;
-    private final IPackageManager mPackageManagerService;
+    private final IPackageManagerWrapper mPackageManagerService;
     private final DevicePolicyManagerWrapper mDevicePolicyManager;
 
     public AppWithAdminGrantedPermissionsCounter(Context context, String[] permissions,
-            PackageManagerWrapper packageManager, IPackageManager packageManagerService,
+            PackageManagerWrapper packageManager, IPackageManagerWrapper packageManagerService,
             DevicePolicyManagerWrapper devicePolicyManager) {
         super(context, packageManager);
         mPermissions = permissions;
diff --git a/src/com/android/settings/applications/ApplicationFeatureProvider.java b/src/com/android/settings/applications/ApplicationFeatureProvider.java
index 101ae91..fccbbd3 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProvider.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProvider.java
@@ -17,8 +17,11 @@
 package com.android.settings.applications;
 
 import android.app.Fragment;
+import android.content.Intent;
 import android.view.View;
 
+import java.util.Set;
+
 public interface ApplicationFeatureProvider {
 
     /**
@@ -55,9 +58,47 @@
             NumberOfAppsCallback callback);
 
     /**
+     * Return the persistent preferred activities configured by the admin for the current user and
+     * all its managed profiles. A persistent preferred activity is an activity that the admin
+     * configured to always handle a given intent (e.g. open browser), even if the user has other
+     * apps installed that would also be able to handle the intent.
+     *
+     * @param intent The intents for which to find persistent preferred activities
+     *
+     * @return the persistent preferred activites for the given intent
+     */
+    Set<PersistentPreferredActivityInfo> findPersistentPreferredActivities(Intent[] intents);
+
+    /**
      * Callback that receives the number of packages installed on the device.
      */
     interface NumberOfAppsCallback {
         void onNumberOfAppsResult(int num);
     }
+
+    public static class PersistentPreferredActivityInfo {
+        public final String packageName;
+        public final int userId;
+
+        public PersistentPreferredActivityInfo(String packageName, int userId) {
+            this.packageName = packageName;
+            this.userId = userId;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (!(other instanceof PersistentPreferredActivityInfo)) {
+                return false;
+            }
+            final PersistentPreferredActivityInfo otherActivityInfo
+                    = (PersistentPreferredActivityInfo) other;
+            return otherActivityInfo.packageName.equals(packageName)
+                    && otherActivityInfo.userId == userId;
+        }
+
+        @Override
+        public int hashCode() {
+            return packageName.hashCode() ^ userId;
+        }
+    }
 }
diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
index ff7f0f0..2d2bce0 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
@@ -18,25 +18,34 @@
 
 import android.app.Fragment;
 import android.content.Context;
-import android.content.pm.IPackageManager;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.ArraySet;
 import android.view.View;
 
 import com.android.settings.enterprise.DevicePolicyManagerWrapper;
 
 import java.util.List;
+import java.util.Set;
 
 public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvider {
 
     private final Context mContext;
     private final PackageManagerWrapper mPm;
-    private final IPackageManager mPms;
+    private final IPackageManagerWrapper mPms;
     private final DevicePolicyManagerWrapper mDpm;
     private final UserManager mUm;
 
     public ApplicationFeatureProviderImpl(Context context, PackageManagerWrapper pm,
-            IPackageManager pms, DevicePolicyManagerWrapper dpm) {
+            IPackageManagerWrapper pms, DevicePolicyManagerWrapper dpm) {
         mContext = context.getApplicationContext();
         mPm = pm;
         mPms = pms;
@@ -61,6 +70,39 @@
                 callback).execute();
     }
 
+    @Override
+    public Set<PersistentPreferredActivityInfo> findPersistentPreferredActivities(
+            Intent[] intents) {
+        final Set<PersistentPreferredActivityInfo> activities = new ArraySet<>();
+        final List<UserHandle> users = mUm.getUserProfiles();
+        for (final Intent intent : intents) {
+            for (final UserHandle user : users) {
+                final int userId = user.getIdentifier();
+                try {
+                    final ResolveInfo resolveInfo = mPms.findPersistentPreferredActivity(intent,
+                            userId);
+                    if (resolveInfo != null) {
+                        ComponentInfo componentInfo = null;
+                        if (resolveInfo.activityInfo != null) {
+                            componentInfo = resolveInfo.activityInfo;
+                        } else if (resolveInfo.serviceInfo != null) {
+                            componentInfo = resolveInfo.serviceInfo;
+                        } else if (resolveInfo.providerInfo != null) {
+                            componentInfo = resolveInfo.providerInfo;
+                        }
+                        if (componentInfo != null) {
+                            activities.add(new PersistentPreferredActivityInfo(
+                                    componentInfo.packageName, userId));
+                        }
+                    }
+                } catch (RemoteException exception) {
+                }
+            }
+
+        }
+        return activities;
+    }
+
     private static class AllUserInstalledAppCounter extends InstalledAppCounter {
         private NumberOfAppsCallback mCallback;
 
@@ -86,7 +128,7 @@
         private NumberOfAppsCallback mCallback;
 
         AllUserAppWithAdminGrantedPermissionsCounter(Context context, String[] permissions,
-                PackageManagerWrapper packageManager, IPackageManager packageManagerService,
+                PackageManagerWrapper packageManager, IPackageManagerWrapper packageManagerService,
                 DevicePolicyManagerWrapper devicePolicyManager, NumberOfAppsCallback callback) {
             super(context, permissions, packageManager, packageManagerService, devicePolicyManager);
             mCallback = callback;
diff --git a/src/com/android/settings/applications/DefaultAutoFillPreference.java b/src/com/android/settings/applications/DefaultAutoFillPreference.java
new file mode 100644
index 0000000..9ed2e19
--- /dev/null
+++ b/src/com/android/settings/applications/DefaultAutoFillPreference.java
@@ -0,0 +1,141 @@
+/**
+ * Copyright (C) 2017 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.applications;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Parcelable;
+import android.provider.Settings;
+import android.service.autofill.AutoFillService;
+import android.service.autofill.AutoFillServiceInfo;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settings.AppListPreferenceWithSettings;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultAutoFillPreference extends AppListPreferenceWithSettings {
+    private static final String TAG = "DefaultAutoFill";
+
+    private static final String SETTING = Settings.Secure.AUTO_FILL_SERVICE;
+
+    public DefaultAutoFillPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        setSavesState(false);
+        setShowItemNone(true);
+
+        refreshData();
+    }
+
+    @Override
+    protected CharSequence getConfirmationMessage(String value) {
+        if (value == null || value.isEmpty()) {
+            return null;
+        }
+
+        int index = findIndexOfValue(value);
+        CharSequence[] entries = getEntries();
+        if (index < 0 || index >= entries.length) {
+            return null;
+        }
+
+        CharSequence entry = entries[index];
+        return getContext().getString(R.string.autofill_confirmation_message, entry);
+    }
+
+    @Override
+    protected boolean persistString(String value) {
+        Settings.Secure.putString(getContext().getContentResolver(), SETTING, value);
+        refreshData();
+        return true;
+    }
+
+    private void refreshData() {
+        ComponentName selectedComponent = getSelectedComponentName();
+        List<AutoFillServiceInfo> infos = getInfos();
+
+        AutoFillServiceInfo selectedInfo = null;
+        int numberOfComponents = infos.size();
+        ComponentName[] components = new ComponentName[numberOfComponents];
+        for (int i = 0; i < numberOfComponents; ++i) {
+            AutoFillServiceInfo info = infos.get(i);
+            ServiceInfo serviceInfo = info.getServiceInfo();
+            ComponentName component =
+                    new ComponentName(serviceInfo.packageName, serviceInfo.name);
+            components[i] = component;
+
+            if (component.equals(selectedComponent)) {
+                selectedInfo = info;
+            }
+        }
+
+        ComponentName selectedComponentSettings = null;
+        if (selectedInfo != null) {
+            String settingsActivity = selectedInfo.getSettingsActivity();
+            selectedComponentSettings = settingsActivity != null
+                    ? new ComponentName(selectedComponent.getPackageName(), settingsActivity)
+                    : null;
+        } else { // selected component not found
+            Log.w(TAG, "Selected AutoFillService not found " + selectedComponent);
+            selectedComponent = null;
+            selectedComponentSettings = null;
+        }
+
+        setComponentNames(components, selectedComponent);
+        setSettingsComponent(selectedComponentSettings);
+        setSummary(getEntry());
+    }
+
+    @Nullable
+    private ComponentName getSelectedComponentName() {
+        String componentString =
+                Settings.Secure.getString(getContext().getContentResolver(), SETTING);
+        if (componentString == null) {
+            return null;
+        }
+
+        return ComponentName.unflattenFromString(componentString);
+    }
+
+    private List<AutoFillServiceInfo> getInfos() {
+        PackageManager pm = getContext().getPackageManager();
+        List<ResolveInfo> resolveInfos = pm.queryIntentServices(
+                new Intent(AutoFillService.SERVICE_INTERFACE),
+                PackageManager.GET_META_DATA);
+        List<AutoFillServiceInfo> infos = new ArrayList<>(resolveInfos.size());
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+            AutoFillServiceInfo info = new AutoFillServiceInfo(pm, serviceInfo);
+            if (info.getParseError() == null) {
+                infos.add(info);
+            } else {
+                Log.i(TAG, "Invalid AutoFillService " + serviceInfo + ": " + info.getParseError());
+            }
+        }
+        return infos;
+    }
+}
diff --git a/src/com/android/settings/applications/IPackageManagerWrapper.java b/src/com/android/settings/applications/IPackageManagerWrapper.java
new file mode 100644
index 0000000..f885985
--- /dev/null
+++ b/src/com/android/settings/applications/IPackageManagerWrapper.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.applications;
+
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
+
+/**
+ * This interface replicates a subset of the android.content.pm.IPackageManager (PMS). The interface
+ * exists so that we can use a thin wrapper around the PMS in production code and a mock in tests.
+ * We cannot directly mock or shadow the PMS, because some of the methods we rely on are newer than
+ * the API version supported by Robolectric.
+ */
+public interface IPackageManagerWrapper {
+
+    /**
+     * Calls {@code IPackageManager.checkUidPermission()}.
+     *
+     * @see android.content.pm.IPackageManager#checkUidPermission
+     */
+    int checkUidPermission(String permName, int uid) throws RemoteException;
+
+    /**
+     * Calls {@code IPackageManager.findPersistentPreferredActivity()}.
+     *
+     * @see android.content.pm.IPackageManager#findPersistentPreferredActivity
+     */
+    ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) throws RemoteException;
+}
diff --git a/src/com/android/settings/applications/IPackageManagerWrapperImpl.java b/src/com/android/settings/applications/IPackageManagerWrapperImpl.java
new file mode 100644
index 0000000..5ea15b9
--- /dev/null
+++ b/src/com/android/settings/applications/IPackageManagerWrapperImpl.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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.applications;
+
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
+
+public class IPackageManagerWrapperImpl implements IPackageManagerWrapper {
+
+    private final IPackageManager mPms;
+
+    public IPackageManagerWrapperImpl(IPackageManager pms) {
+        mPms = pms;
+    }
+
+    @Override
+    public int checkUidPermission(String permName, int uid) throws RemoteException {
+        return mPms.checkUidPermission(permName, uid);
+    }
+
+    @Override
+    public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId)
+            throws RemoteException {
+        return mPms.findPersistentPreferredActivity(intent, userId);
+    }
+}
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index a87ba53..f6b303c 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -31,7 +31,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.preference.PreferenceFrameLayout;
-import android.support.v7.preference.Preference;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -57,6 +56,7 @@
 import com.android.settings.AppHeader;
 import com.android.settings.R;
 import com.android.settings.Settings.AllApplicationsActivity;
+import com.android.settings.Settings.GamesStorageActivity;
 import com.android.settings.Settings.HighPowerApplicationsActivity;
 import com.android.settings.Settings.ManageExternalSourcesActivity;
 import com.android.settings.Settings.NotificationAppListActivity;
@@ -216,6 +216,7 @@
     public static final int LIST_TYPE_OVERLAY = 6;
     public static final int LIST_TYPE_WRITE_SETTINGS = 7;
     public static final int LIST_TYPE_MANAGE_SOURCES = 8;
+    public static final int LIST_TYPE_GAMES = 9;
 
     private View mRootView;
 
@@ -267,6 +268,9 @@
             mListType = LIST_TYPE_WRITE_SETTINGS;
         } else if (className.equals(ManageExternalSourcesActivity.class.getName())) {
             mListType = LIST_TYPE_MANAGE_SOURCES;
+        } else if (className.equals(GamesStorageActivity.class.getName())) {
+            mListType = LIST_TYPE_GAMES;
+            mSortOrder = R.id.sort_order_size;
         } else {
             mListType = LIST_TYPE_MAIN;
         }
@@ -359,6 +363,9 @@
         if (mListType == LIST_TYPE_STORAGE) {
             mApplications.setOverrideFilter(new VolumeFilter(mVolumeUuid));
         }
+        if (mListType == LIST_TYPE_GAMES) {
+            mApplications.setOverrideFilter(ApplicationsState.FILTER_GAMES);
+        }
     }
 
     @Override
@@ -399,6 +406,7 @@
             case LIST_TYPE_MAIN:
             case LIST_TYPE_NOTIFICATION:
             case LIST_TYPE_STORAGE:
+            case LIST_TYPE_GAMES:
                 return mSortOrder == R.id.sort_order_alpha;
             default:
                 return false;
@@ -413,6 +421,7 @@
             case LIST_TYPE_NOTIFICATION:
                 return MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS;
             case LIST_TYPE_STORAGE:
+            case LIST_TYPE_GAMES:
                 return MetricsEvent.APPLICATIONS_STORAGE_APPS;
             case LIST_TYPE_USAGE_ACCESS:
                 return MetricsEvent.USAGE_ACCESS;
@@ -517,6 +526,8 @@
                 break;
             case LIST_TYPE_MANAGE_SOURCES:
                 startAppInfoFragment(ExternalSourcesDetails.class, R.string.install_other_apps);
+            case LIST_TYPE_GAMES:
+                startAppInfoFragment(AppStorageSettings.class, R.string.game_storage_settings);
                 break;
             // TODO: Figure out if there is a way where we can spin up the profile's settings
             // process ahead of time, to avoid a long load of data when user clicks on a managed app.
diff --git a/src/com/android/settings/applications/PictureInPictureSettings.java b/src/com/android/settings/applications/PictureInPictureSettings.java
index a17c894..df73572 100644
--- a/src/com/android/settings/applications/PictureInPictureSettings.java
+++ b/src/com/android/settings/applications/PictureInPictureSettings.java
@@ -18,7 +18,6 @@
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
 import static android.content.pm.PackageManager.GET_ACTIVITIES;
 
 import android.annotation.Nullable;
@@ -91,7 +90,7 @@
         // picture-in-picture
         if (activities != null) {
             for (int i = activities.length - 1; i >= 0; i--) {
-                if (activities[i].getResizeMode() == RESIZE_MODE_RESIZEABLE_AND_PIPABLE) {
+                if (activities[i].supportsPictureInPicture()) {
                     return true;
                 }
             }
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index b956aa5..3fc0abf 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -284,12 +284,12 @@
             }
         }
         if (btClass != null) {
-            if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
-                return new Pair<Integer, String>(R.drawable.ic_bt_headphones_a2dp, HEADPHONE);
-            }
-            if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
+          if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
                 return new Pair<Integer, String>(R.drawable.ic_bt_headset_hfp, HEADSET);
             }
+          if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
+                return new Pair<Integer, String>(R.drawable.ic_bt_headphones_a2dp, HEADPHONE);
+            }
         }
         return new Pair<Integer, String>(R.drawable.ic_settings_bluetooth, BLUETOOTH);
     }
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
index fe0e1d2..481a0eb 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
@@ -30,7 +30,6 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settingslib.drawer.CategoryKey;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -48,11 +47,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_DEVICE;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
@@ -73,8 +67,7 @@
         mUsbPrefController = new UsbModePreferenceController(context, new UsbBackend(context));
         lifecycle.addObserver(mUsbPrefController);
         controllers.add(mUsbPrefController);
-        mBluetoothPreferenceController =
-            new BluetoothMasterSwitchPreferenceController(
+        mBluetoothPreferenceController = new BluetoothMasterSwitchPreferenceController(
                 context, Utils.getLocalBtManager(context));
         lifecycle.addObserver(mBluetoothPreferenceController);
         controllers.add(mBluetoothPreferenceController);
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index de86bd5..f8a27e0 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -194,7 +194,10 @@
     /**
      * Returns the CategoryKey for loading {@link DashboardCategory} for this fragment.
      */
-    protected abstract String getCategoryKey();
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public String getCategoryKey() {
+        return DashboardFragmentRegistry.PARENT_TO_CATEGORY_KEY_MAP.get(getClass().getName());
+    }
 
     /**
      * Get the tag string for logging.
@@ -302,9 +305,9 @@
         final Context context = getContext();
         mSummaryLoader = new SummaryLoader(getActivity(), getCategoryKey());
         mSummaryLoader.setSummaryConsumer(this);
-        final TypedArray a = context.obtainStyledAttributes(new int[] {
-            mDashboardFeatureProvider.isEnabled() ? android.R.attr.colorControlNormal
-                : android.R.attr.colorAccent});
+        final TypedArray a = context.obtainStyledAttributes(new int[]{
+                mDashboardFeatureProvider.isEnabled() ? android.R.attr.colorControlNormal
+                        : android.R.attr.colorAccent});
         final int tintColor = a.getColor(0, context.getColor(android.R.color.white));
         a.recycle();
         final String pkgName = context.getPackageName();
@@ -319,7 +322,7 @@
                 continue;
             }
             if (pkgName != null && tile.intent != null
-                && !pkgName.equals(tile.intent.getComponent().getPackageName())) {
+                    && !pkgName.equals(tile.intent.getComponent().getPackageName())) {
                 // If this drawable is coming from outside Settings, tint it to match the color.
                 tile.icon.setTint(tintColor);
             }
diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
index 54d4fd0..a315836 100644
--- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
+++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java
@@ -21,13 +21,16 @@
 import com.android.settings.DevelopmentSettings;
 import com.android.settings.DisplaySettings;
 import com.android.settings.SecuritySettings;
+import com.android.settings.accounts.AccountDetailDashboardFragment;
 import com.android.settings.accounts.UserAndAccountDashboardFragment;
 import com.android.settings.applications.AdvancedAppSettings;
 import com.android.settings.applications.AppAndNotificationDashboardFragment;
 import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
 import com.android.settings.deviceinfo.StorageDashboardFragment;
+import com.android.settings.fuelgauge.PowerUsageSummary;
 import com.android.settings.inputmethod.InputAndGestureSettings;
 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
+import com.android.settings.language.LanguageAndRegionSettings;
 import com.android.settings.network.NetworkDashboardFragment;
 import com.android.settings.notification.SoundSettings;
 import com.android.settings.system.SystemDashboardFragment;
@@ -37,7 +40,6 @@
 
 /**
  * A registry to keep track of which page hosts which category.
- * TODO: Remove DashboardFragment#getCategoryKey() and just use this registry instead.
  */
 public class DashboardFragmentRegistry {
 
@@ -61,6 +63,8 @@
                 CategoryKey.CATEGORY_DEVICE);
         PARENT_TO_CATEGORY_KEY_MAP.put(AppAndNotificationDashboardFragment.class.getName(),
                 CategoryKey.CATEGORY_APPS);
+        PARENT_TO_CATEGORY_KEY_MAP.put(PowerUsageSummary.class.getName(),
+                CategoryKey.CATEGORY_BATTERY);
         PARENT_TO_CATEGORY_KEY_MAP.put(AdvancedAppSettings.class.getName(),
                 CategoryKey.CATEGORY_APPS_DEFAULT);
         PARENT_TO_CATEGORY_KEY_MAP.put(DisplaySettings.class.getName(),
@@ -71,7 +75,7 @@
                 CategoryKey.CATEGORY_STORAGE);
         PARENT_TO_CATEGORY_KEY_MAP.put(SecuritySettings.class.getName(),
                 CategoryKey.CATEGORY_SECURITY);
-        PARENT_TO_CATEGORY_KEY_MAP.put(UserAndAccountDashboardFragment.class.getName(),
+        PARENT_TO_CATEGORY_KEY_MAP.put(AccountDetailDashboardFragment.class.getName(),
                 CategoryKey.CATEGORY_ACCOUNT);
         PARENT_TO_CATEGORY_KEY_MAP.put(UserAndAccountDashboardFragment.class.getName(),
                 CategoryKey.CATEGORY_ACCOUNT);
@@ -81,6 +85,8 @@
                 InputAndGestureSettings.class.getName(), CategoryKey.CATEGORY_SYSTEM_INPUT);
         PARENT_TO_CATEGORY_KEY_MAP.put(InputMethodAndLanguageSettings.class.getName(),
                 CategoryKey.CATEGORY_SYSTEM_LANGUAGE);
+        PARENT_TO_CATEGORY_KEY_MAP.put(LanguageAndRegionSettings.class.getName(),
+                CategoryKey.CATEGORY_SYSTEM_LANGUAGE);
         PARENT_TO_CATEGORY_KEY_MAP.put(DevelopmentSettings.class.getName(),
                 CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT);
 
diff --git a/src/com/android/settings/datausage/BillingCycleSettings.java b/src/com/android/settings/datausage/BillingCycleSettings.java
index 9b39840..dda984b 100644
--- a/src/com/android/settings/datausage/BillingCycleSettings.java
+++ b/src/com/android/settings/datausage/BillingCycleSettings.java
@@ -44,14 +44,16 @@
 
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
-import static android.net.TrafficStats.GB_IN_BYTES;
-import static android.net.TrafficStats.MB_IN_BYTES;
 
 public class BillingCycleSettings extends DataUsageBase implements
         Preference.OnPreferenceChangeListener, DataUsageEditController {
 
     private static final String TAG = "BillingCycleSettings";
     private static final boolean LOGD = false;
+    public static final long KB_IN_BYTES = 1000;
+    public static final long MB_IN_BYTES = KB_IN_BYTES * 1000;
+    public static final long GB_IN_BYTES = MB_IN_BYTES * 1000;
+
     private static final long MAX_DATA_LIMIT_BYTES = 50000 * GB_IN_BYTES;
 
     private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index 8fabd8d..59f9a84 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -101,11 +101,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_STORAGE;
-    }
-
-    @Override
     protected int getPreferenceScreenResId() {
         return R.xml.storage_dashboard_fragment;
     }
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 7e98918..22f9c4c 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -122,7 +122,8 @@
                 intent = getAudioIntent();
                 break;
             case GAME_KEY:
-                // TODO: Once app categorization is added, make this section.
+                intent = getGamesIntent();
+                break;
             case OTHER_APPS_KEY:
                 // Because we are likely constructed with a null volume, this is theoretically
                 // possible.
@@ -259,6 +260,15 @@
                 false);
     }
 
+    private Intent getGamesIntent() {
+            Bundle args = new Bundle(1);
+            args.putString(ManageApplications.EXTRA_CLASSNAME,
+                    Settings.GamesStorageActivity.class.getName());
+            return Utils.onBuildStartFragmentIntent(mContext,
+                    ManageApplications.class.getName(), args, null, R.string.game_storage_settings,
+                    null, false);
+    }
+
     private Intent getFilesIntent() {
         return mSvp.findEmulatedForPrivate(mVolume).buildBrowseIntent();
     }
diff --git a/src/com/android/settings/display/ThemePreferenceController.java b/src/com/android/settings/display/ThemePreferenceController.java
new file mode 100644
index 0000000..da5b84a
--- /dev/null
+++ b/src/com/android/settings/display/ThemePreferenceController.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 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.display;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_THEME;
+
+import android.app.AlertDialog;
+import android.app.UiModeManager;
+import android.content.Context;
+import android.content.DialogInterface.OnClickListener;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
+
+import libcore.util.Objects;
+
+public class ThemePreferenceController extends PreferenceController implements
+        Preference.OnPreferenceChangeListener {
+
+    private static final String KEY_THEME = "theme";
+
+    private final UiModeManager mUiModeManager;
+    private final MetricsFeatureProvider mMetricsFeatureProvider;
+
+    public ThemePreferenceController(Context context) {
+        super(context);
+        mUiModeManager = context.getSystemService(UiModeManager.class);
+        mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_THEME;
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (KEY_THEME.equals(preference.getKey())) {
+            mMetricsFeatureProvider.action(mContext, ACTION_THEME);
+        }
+        return false;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        ListPreference pref = (ListPreference) preference;
+        String[] options = mUiModeManager.getAvailableThemes();
+        for (int i = 0; i < options.length; i++) {
+            options[i] = nullToDefault(options[i]);
+        }
+        pref.setEntries(options);
+        pref.setEntryValues(options);
+        String theme = mUiModeManager.getTheme();
+        if (theme == null) {
+            theme = mContext.getString(R.string.default_theme);
+        }
+        pref.setValue(nullToDefault(theme));
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (Objects.equal(newValue, mUiModeManager.getTheme())) {
+            return true;
+        }
+        // TODO: STOPSHIP Don't require reboot and remove this prompt.
+        OnClickListener onConfirm = (d, i) -> {
+            mUiModeManager.setTheme(defaultToNull((String) newValue));
+            ((ListPreference) preference).setValue((String) newValue);
+        };
+        new AlertDialog.Builder(mContext)
+                .setTitle(R.string.change_theme_reboot)
+                .setPositiveButton(com.android.internal.R.string.global_action_restart, onConfirm)
+                .setNegativeButton(android.R.string.cancel, null)
+                .show();
+        return false;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        String[] themes = mUiModeManager.getAvailableThemes();
+        return themes != null && themes.length > 1;
+    }
+
+    private String nullToDefault(String input) {
+        if (input == null) {
+            return mContext.getString(R.string.default_theme);
+        }
+        return input;
+    }
+
+    private String defaultToNull(String input) {
+        if (mContext.getString(R.string.default_theme).equals(input)) {
+            return null;
+        }
+        return input;
+    }
+}
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
index 208bf0c..75d3b10 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
@@ -24,7 +24,6 @@
 import com.android.settings.core.PreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.search.Indexable.SearchIndexProvider;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -40,11 +39,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
@@ -65,6 +59,7 @@
         controllers.add(new AdminGrantedLocationPermissionsPreferenceController(context));
         controllers.add(new AdminGrantedMicrophonePermissionPreferenceController(context));
         controllers.add(new AdminGrantedCameraPermissionPreferenceController(context));
+        controllers.add(new EnterpriseSetDefaultAppsPreferenceController(context));
         controllers.add(new AlwaysOnVpnPrimaryUserPreferenceController(context));
         controllers.add(new AlwaysOnVpnManagedProfilePreferenceController(context));
         controllers.add(new GlobalHttpProxyPreferenceController(context));
diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
new file mode 100644
index 0000000..23627cd
--- /dev/null
+++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceController.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 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.enterprise;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+
+public class EnterpriseSetDefaultAppsPreferenceController extends PreferenceController {
+
+    private static final String KEY_DEFAULT_APPS = "number_enterprise_set_default_apps";
+    private final ApplicationFeatureProvider mFeatureProvider;
+
+    public EnterpriseSetDefaultAppsPreferenceController(Context context) {
+        super(context);
+        mFeatureProvider = FeatureFactory.getFactory(context)
+                .getApplicationFeatureProvider(context);
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        // Browser
+        int num = mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
+                buildIntent(Intent.ACTION_VIEW, Intent.CATEGORY_BROWSABLE, "http:", null)}).size();
+        // Camera
+        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
+                new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
+                new Intent(MediaStore.ACTION_VIDEO_CAPTURE)}).size();
+        // Map
+        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
+                buildIntent(Intent.ACTION_VIEW, null, "geo:", null)}).size();
+        // E-mail
+        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
+                new Intent(Intent.ACTION_SENDTO), new Intent(Intent.ACTION_SEND),
+                new Intent(Intent.ACTION_SEND_MULTIPLE)}).size();
+        // Calendar
+        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
+                buildIntent(Intent.ACTION_INSERT, null, null, "vnd.android.cursor.dir/event")})
+                .size();
+        // Contacts
+        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
+                buildIntent(Intent.ACTION_PICK, null, null,
+                        ContactsContract.Contacts.CONTENT_TYPE)}).size();
+        // Dialer
+        num += mFeatureProvider.findPersistentPreferredActivities(new Intent[] {
+                new Intent(Intent.ACTION_DIAL), new Intent(Intent.ACTION_CALL)}).size();
+
+        if (num == 0) {
+            preference.setVisible(false);
+        } else {
+            preference.setVisible(true);
+            preference.setTitle(mContext.getResources().getQuantityString(
+                    R.plurals.enterprise_privacy_number_enterprise_set_default_apps,
+                    num, num));
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_DEFAULT_APPS;
+    }
+
+    private static Intent buildIntent(String action, String category, String protocol,
+            String type) {
+        final Intent intent = new Intent(action);
+        if (category != null) {
+            intent.addCategory(category);
+        }
+        if (protocol != null) {
+            intent.setData(Uri.parse(protocol));
+        }
+        if (type != null) {
+            intent.setType(type);
+        }
+        return intent;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageDetail.java b/src/com/android/settings/fuelgauge/PowerUsageDetail.java
index dbc5360..7acb7ce 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageDetail.java
@@ -388,11 +388,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index c4a780f..db6bfb8 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -37,6 +37,7 @@
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatterySipper.DrainType;
@@ -52,7 +53,6 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.BatteryInfo;
-import com.android.settingslib.drawer.CategoryKey;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -139,11 +139,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_BATTERY;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
@@ -414,8 +409,9 @@
                 pref.setTitle(entry.getLabel());
                 pref.setOrder(i + 1);
                 pref.setPercent(percentOfMax, percentOfTotal);
-                if ((sipper.drainType != DrainType.APP || sipper.uidObj.getUid() == Process.ROOT_UID)
-                         && sipper.drainType != DrainType.USER) {
+                if ((sipper.drainType != DrainType.APP
+                        || sipper.uidObj.getUid() == Process.ROOT_UID)
+                        && sipper.drainType != DrainType.USER) {
                     pref.setTint(colorControl);
                 }
                 addedSome = true;
@@ -579,7 +575,7 @@
             = new SummaryLoader.SummaryProviderFactory() {
         @Override
         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
-                                                                   SummaryLoader summaryLoader) {
+                SummaryLoader summaryLoader) {
             return new SummaryProvider(activity, summaryLoader);
         }
     };
diff --git a/src/com/android/settings/gestures/DoubleTapPowerSettings.java b/src/com/android/settings/gestures/DoubleTapPowerSettings.java
index c31e999..9251f0b 100644
--- a/src/com/android/settings/gestures/DoubleTapPowerSettings.java
+++ b/src/com/android/settings/gestures/DoubleTapPowerSettings.java
@@ -40,11 +40,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/gestures/DoubleTapScreenSettings.java b/src/com/android/settings/gestures/DoubleTapScreenSettings.java
index d203fbc..f374f9e 100644
--- a/src/com/android/settings/gestures/DoubleTapScreenSettings.java
+++ b/src/com/android/settings/gestures/DoubleTapScreenSettings.java
@@ -42,11 +42,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/gestures/DoubleTwistGestureSettings.java b/src/com/android/settings/gestures/DoubleTwistGestureSettings.java
index 1303ca1..5f5b873 100644
--- a/src/com/android/settings/gestures/DoubleTwistGestureSettings.java
+++ b/src/com/android/settings/gestures/DoubleTwistGestureSettings.java
@@ -40,11 +40,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/gestures/GestureSettings.java b/src/com/android/settings/gestures/GestureSettings.java
index 9b71b96..db6925e 100644
--- a/src/com/android/settings/gestures/GestureSettings.java
+++ b/src/com/android/settings/gestures/GestureSettings.java
@@ -122,11 +122,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
@@ -170,7 +165,7 @@
 
                     List<PreferenceController> preferenceControllers =
                             getPreferenceControllers(context);
-                    for(PreferenceController controller : preferenceControllers) {
+                    for (PreferenceController controller : preferenceControllers) {
                         controller.updateNonIndexableKeys(result);
                     }
                     return result;
diff --git a/src/com/android/settings/gestures/PickupGestureSettings.java b/src/com/android/settings/gestures/PickupGestureSettings.java
index 8c0f74c..08e61fa 100644
--- a/src/com/android/settings/gestures/PickupGestureSettings.java
+++ b/src/com/android/settings/gestures/PickupGestureSettings.java
@@ -42,11 +42,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/gestures/SwipeToNotificationSettings.java b/src/com/android/settings/gestures/SwipeToNotificationSettings.java
index fba074e..155412c 100644
--- a/src/com/android/settings/gestures/SwipeToNotificationSettings.java
+++ b/src/com/android/settings/gestures/SwipeToNotificationSettings.java
@@ -40,11 +40,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/inputmethod/InputAndGestureSettings.java b/src/com/android/settings/inputmethod/InputAndGestureSettings.java
index 4ad5af2..5b2c51a 100644
--- a/src/com/android/settings/inputmethod/InputAndGestureSettings.java
+++ b/src/com/android/settings/inputmethod/InputAndGestureSettings.java
@@ -34,7 +34,6 @@
 import com.android.settings.gestures.SwipeToNotificationPreferenceController;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.drawer.CategoryKey;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -52,11 +51,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_SYSTEM_INPUT;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
index 587d039..121af98 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndLanguageSettings.java
@@ -38,7 +38,6 @@
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
 import com.android.settings.search.SearchIndexableRaw;
-import com.android.settingslib.drawer.CategoryKey;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -61,11 +60,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_SYSTEM_LANGUAGE;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/language/LanguageAndRegionSettings.java b/src/com/android/settings/language/LanguageAndRegionSettings.java
index 7e92e05..649b2ff 100644
--- a/src/com/android/settings/language/LanguageAndRegionSettings.java
+++ b/src/com/android/settings/language/LanguageAndRegionSettings.java
@@ -24,7 +24,6 @@
 import com.android.settings.core.PreferenceController;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.inputmethod.SpellCheckerPreferenceController;
-import com.android.settingslib.drawer.CategoryKey;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -39,11 +38,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_SYSTEM_LANGUAGE;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index 89dfa02..7e6de96 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -27,7 +27,6 @@
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.drawer.CategoryKey;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -47,11 +46,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_NETWORK;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/notification/ConfigureNotificationSettings.java b/src/com/android/settings/notification/ConfigureNotificationSettings.java
index fd59c97..2a737d4 100644
--- a/src/com/android/settings/notification/ConfigureNotificationSettings.java
+++ b/src/com/android/settings/notification/ConfigureNotificationSettings.java
@@ -40,11 +40,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return "";
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/notification/OtherSoundSettings.java b/src/com/android/settings/notification/OtherSoundSettings.java
index 15e7f8c..5c409d3 100644
--- a/src/com/android/settings/notification/OtherSoundSettings.java
+++ b/src/com/android/settings/notification/OtherSoundSettings.java
@@ -47,11 +47,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index a55278d..1cf0ae6 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -20,7 +20,6 @@
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -43,7 +42,7 @@
 import com.android.settings.dashboard.SummaryLoader;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.drawer.CategoryKey;
+
 import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -103,11 +102,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_SOUND;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 2c81241..c45bf0e 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -25,6 +25,7 @@
 
 import com.android.settings.applications.ApplicationFeatureProvider;
 import com.android.settings.applications.ApplicationFeatureProviderImpl;
+import com.android.settings.applications.IPackageManagerWrapperImpl;
 import com.android.settings.applications.PackageManagerWrapperImpl;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.core.instrumentation.MetricsFeatureProviderImpl;
@@ -90,7 +91,7 @@
         if (mApplicationFeatureProvider == null) {
             mApplicationFeatureProvider = new ApplicationFeatureProviderImpl(context,
                     new PackageManagerWrapperImpl(context.getPackageManager()),
-                    AppGlobals.getPackageManager(),
+                    new IPackageManagerWrapperImpl(AppGlobals.getPackageManager()),
                     new DevicePolicyManagerWrapperImpl((DevicePolicyManager) context
                             .getSystemService(Context.DEVICE_POLICY_SERVICE)));
         }
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 4f9f92c..5040e45 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -72,6 +72,8 @@
 import com.android.settings.print.PrintSettingsFragment;
 import com.android.settings.sim.SimSettings;
 import com.android.settings.system.SystemDashboardFragment;
+import com.android.settings.tts.TtsEnginePreferenceFragment;
+import com.android.settings.tts.TtsSlidersFragment;
 import com.android.settings.users.UserSettings;
 import com.android.settings.wifi.AdvancedWifiSettings;
 import com.android.settings.wifi.ConfigureWifiSettings;
@@ -167,6 +169,9 @@
         addIndex(ConnectedDeviceDashboardFragment.class, NO_DATA_RES_ID, R.drawable.ic_bt_laptop);
         addIndex(EnterprisePrivacySettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_about);
         addIndex(PaymentSettings.class, NO_DATA_RES_ID, R.drawable.ic_settings_nfc_payment);
+        addIndex(
+                TtsEnginePreferenceFragment.class, NO_DATA_RES_ID, R.drawable.ic_settings_language);
+        addIndex(TtsSlidersFragment.class, NO_DATA_RES_ID, R.drawable.ic_settings_language);
     }
 
     private SearchIndexableResources() {
diff --git a/src/com/android/settings/search2/DatabaseIndexingManager.java b/src/com/android/settings/search2/DatabaseIndexingManager.java
index c75f93f..d7c8746 100644
--- a/src/com/android/settings/search2/DatabaseIndexingManager.java
+++ b/src/com/android/settings/search2/DatabaseIndexingManager.java
@@ -516,10 +516,7 @@
                 nonIndexableKeys.addAll(resNonIndxableKeys);
             }
 
-            indexFromResource(sir.context, database, localeStr,
-                    sir.xmlResId, sir.className, sir.iconResId, sir.rank,
-                    sir.intentAction, sir.intentTargetPackage, sir.intentTargetClass,
-                    nonIndexableKeys);
+            indexFromResource(database, localeStr, sir, nonIndexableKeys);
         } else {
             if (TextUtils.isEmpty(sir.className)) {
                 Log.w(LOG_TAG, "Cannot index an empty Search Provider name!");
@@ -543,20 +540,17 @@
                     nonIndexableKeys.addAll(providerNonIndexableKeys);
                 }
 
-                indexFromProvider(mContext, database, localeStr, provider, sir.className,
-                        sir.iconResId, sir.rank, sir.enabled, nonIndexableKeys);
+                indexFromProvider(database, localeStr, provider, sir, nonIndexableKeys);
             }
         }
     }
 
-    private void indexFromResource(Context context, SQLiteDatabase database, String localeStr,
-            int xmlResId, String fragmentName, int iconResId, int rank,
-            String intentAction, String intentTargetPackage, String intentTargetClass,
-            List<String> nonIndexableKeys) {
-
+    private void indexFromResource(SQLiteDatabase database, String localeStr,
+            SearchIndexableResource sir, List<String> nonIndexableKeys) {
+        final Context context = sir.context;
         XmlResourceParser parser = null;
         try {
-            parser = context.getResources().getXml(xmlResId);
+            parser = context.getResources().getXml(sir.xmlResId);
 
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -583,6 +577,13 @@
             String keywords;
             String childFragment;
             ResultPayload payload;
+            boolean enabled;
+            final String fragmentName = sir.className;
+            final int iconResId = sir.iconResId;
+            final int rank = sir.rank;
+            final String intentAction = sir.intentAction;
+            final String intentTargetPackage = sir.intentTargetPackage;
+            final String intentTargetClass = sir.intentTargetClass;
 
             Map<String, PreferenceController> controllerUriMap = null;
 
@@ -593,28 +594,28 @@
 
             // Insert rows for the main PreferenceScreen node. Rewrite the data for removing
             // hyphens.
-            if (!nonIndexableKeys.contains(key)) {
-                title = XmlParserUtils.getDataTitle(context, attrs);
-                summary = XmlParserUtils.getDataSummary(context, attrs);
-                keywords = XmlParserUtils.getDataKeywords(context, attrs);
 
-                DatabaseRow.Builder builder = new DatabaseRow.Builder();
-                builder.setLocale(localeStr)
-                        .setEntries(null)
-                        .setClassName(fragmentName)
-                        .setScreenTitle(screenTitle)
-                        .setIconResId(iconResId)
-                        .setRank(rank)
-                        .setIntentAction(intentAction)
-                        .setIntentTargetPackage(intentTargetPackage)
-                        .setIntentTargetClass(intentTargetClass)
-                        .setEnabled(true)
-                        .setKey(key)
-                        .setUserId(-1 /* default user id */);
+            title = XmlParserUtils.getDataTitle(context, attrs);
+            summary = XmlParserUtils.getDataSummary(context, attrs);
+            keywords = XmlParserUtils.getDataKeywords(context, attrs);
+            enabled = !nonIndexableKeys.contains(key);
 
-                updateOneRowWithFilteredData(database, builder, title, summary,
-                        null /* summary off */, keywords);
-            }
+            DatabaseRow.Builder builder = new DatabaseRow.Builder();
+            builder.setLocale(localeStr)
+                    .setEntries(null)
+                    .setClassName(fragmentName)
+                    .setScreenTitle(screenTitle)
+                    .setIconResId(iconResId)
+                    .setRank(rank)
+                    .setIntentAction(intentAction)
+                    .setIntentTargetPackage(intentTargetPackage)
+                    .setIntentTargetClass(intentTargetClass)
+                    .setEnabled(enabled)
+                    .setKey(key)
+                    .setUserId(-1 /* default user id */);
+
+            updateOneRowWithFilteredData(database, builder, title, summary,
+                    null /* summary off */, keywords);
 
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -625,14 +626,11 @@
                 nodeName = parser.getName();
 
                 key = XmlParserUtils.getDataKey(context, attrs);
-                if (nonIndexableKeys.contains(key)) {
-                    continue;
-                }
-
+                enabled = ! nonIndexableKeys.contains(key);
                 title = XmlParserUtils.getDataTitle(context, attrs);
                 keywords = XmlParserUtils.getDataKeywords(context, attrs);
 
-                DatabaseRow.Builder builder = new DatabaseRow.Builder();
+                builder = new DatabaseRow.Builder();
                 builder.setLocale(localeStr)
                         .setClassName(fragmentName)
                         .setScreenTitle(screenTitle)
@@ -641,7 +639,7 @@
                         .setIntentAction(intentAction)
                         .setIntentTargetPackage(intentTargetPackage)
                         .setIntentTargetClass(intentTargetClass)
-                        .setEnabled(true)
+                        .setEnabled(enabled)
                         .setKey(key)
                         .setUserId(-1 /* default user id */);
 
@@ -685,16 +683,21 @@
         }
     }
 
-    private void indexFromProvider(Context context, SQLiteDatabase database, String localeStr,
-            Indexable.SearchIndexProvider provider, String className, int iconResId, int rank,
-            boolean enabled, List<String> nonIndexableKeys) {
+    private void indexFromProvider(SQLiteDatabase database, String localeStr,
+            Indexable.SearchIndexProvider provider, SearchIndexableResource sir,
+            List<String> nonIndexableKeys) {
+
+        final String className = sir.className;
+        final int iconResId = sir.iconResId;
+        final int rank = sir.rank;
 
         if (provider == null) {
             Log.w(LOG_TAG, "Cannot find provider: " + className);
             return;
         }
 
-        final List<SearchIndexableRaw> rawList = provider.getRawDataToIndex(context, enabled);
+        final List<SearchIndexableRaw> rawList = provider.getRawDataToIndex(mContext,
+                true /* enabled */);
 
         if (rawList != null) {
 
@@ -706,10 +709,7 @@
                 if (!raw.locale.toString().equalsIgnoreCase(localeStr)) {
                     continue;
                 }
-
-                if (nonIndexableKeys.contains(raw.key)) {
-                    continue;
-                }
+                boolean enabled = !nonIndexableKeys.contains(raw.key);
 
                 DatabaseRow.Builder builder = new DatabaseRow.Builder();
                 builder.setLocale(localeStr)
@@ -721,7 +721,7 @@
                         .setIntentAction(raw.intentAction)
                         .setIntentTargetPackage(raw.intentTargetPackage)
                         .setIntentTargetClass(raw.intentTargetClass)
-                        .setEnabled(raw.enabled)
+                        .setEnabled(enabled)
                         .setKey(raw.key)
                         .setUserId(raw.userId);
 
@@ -731,7 +731,7 @@
         }
 
         final List<SearchIndexableResource> resList =
-                provider.getXmlResourcesToIndex(context, enabled);
+                provider.getXmlResourcesToIndex(mContext, true);
         if (resList != null) {
             final int resSize = resList.size();
             for (int i = 0; i < resSize; i++) {
@@ -742,15 +742,10 @@
                     continue;
                 }
 
-                final int itemIconResId = (item.iconResId == 0) ? iconResId : item.iconResId;
-                final int itemRank = (item.rank == 0) ? rank : item.rank;
-                String itemClassName = (TextUtils.isEmpty(item.className))
-                        ? className : item.className;
+                item.iconResId = (item.iconResId == 0) ? iconResId : item.iconResId;
+                item.className = (TextUtils.isEmpty(item.className)) ? className : item.className;
 
-                indexFromResource(context, database, localeStr,
-                        item.xmlResId, itemClassName, itemIconResId, itemRank,
-                        item.intentAction, item.intentTargetPackage,
-                        item.intentTargetClass, nonIndexableKeys);
+                indexFromResource(database, localeStr, item, nonIndexableKeys);
             }
         }
     }
@@ -1164,4 +1159,4 @@
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/settings/search2/DatabaseResultLoader.java b/src/com/android/settings/search2/DatabaseResultLoader.java
index 6c8def6..8bb1a41 100644
--- a/src/com/android/settings/search2/DatabaseResultLoader.java
+++ b/src/com/android/settings/search2/DatabaseResultLoader.java
@@ -20,6 +20,7 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 
+import android.text.TextUtils;
 import com.android.settings.dashboard.SiteMapManager;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.IndexDatabaseHelper;
@@ -129,7 +130,6 @@
         secondaryResults = query(MATCH_COLUMNS_SECONDARY, BASE_RANKS[1]);
         tertiaryResults = query(MATCH_COLUMNS_TERTIARY, BASE_RANKS[2]);
 
-
         final List<SearchResult> results = new ArrayList<>(primaryResults.size()
                 + secondaryResults.size()
                 + tertiaryResults.size());
@@ -163,11 +163,14 @@
      * @return the cleaned query string
      */
     private static String cleanQuery(String query) {
+        if (TextUtils.isEmpty(query)) {
+            return null;
+        }
         return query.trim();
     }
 
     private static String buildWhereClause(String[] matchColumns) {
-        StringBuilder sb = new StringBuilder(" ");
+        StringBuilder sb = new StringBuilder(" (");
         final int count = matchColumns.length;
         for (int n = 0; n < count; n++) {
             sb.append(matchColumns[n]);
@@ -176,6 +179,7 @@
                 sb.append(" OR ");
             }
         }
+        sb.append(") AND enabled = 1");
         return sb.toString();
     }
 }
diff --git a/src/com/android/settings/system/SystemDashboardFragment.java b/src/com/android/settings/system/SystemDashboardFragment.java
index b06d403..c46bc6f 100644
--- a/src/com/android/settings/system/SystemDashboardFragment.java
+++ b/src/com/android/settings/system/SystemDashboardFragment.java
@@ -30,7 +30,6 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settingslib.drawer.CategoryKey;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -57,11 +56,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        return CategoryKey.CATEGORY_SYSTEM;
-    }
-
-    @Override
     protected List<PreferenceController> getPreferenceControllers(Context context) {
         final List<PreferenceController> controllers = new ArrayList<>();
         controllers.add(new SystemUpdatePreferenceController(context, UserManager.get(context)));
diff --git a/src/com/android/settings/tts/TextToSpeechSettings.java b/src/com/android/settings/tts/TextToSpeechSettings.java
index 96a7f4e..fe78d20 100644
--- a/src/com/android/settings/tts/TextToSpeechSettings.java
+++ b/src/com/android/settings/tts/TextToSpeechSettings.java
@@ -16,29 +16,27 @@
 
 package com.android.settings.tts;
 
+import android.support.v7.preference.ListPreference;
 import android.app.AlertDialog;
 import android.content.ActivityNotFoundException;
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.os.Bundle;
-import android.provider.Settings.SettingNotFoundException;
 import android.speech.tts.TextToSpeech;
 import android.speech.tts.TextToSpeech.EngineInfo;
 import android.speech.tts.TtsEngines;
 import android.speech.tts.UtteranceProgressListener;
-import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceCategory;
 import android.text.TextUtils;
 import android.util.Log;
-import android.widget.Checkable;
+import android.util.Pair;
+import java.util.Comparator;
+import java.util.Collections;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
-import com.android.settings.SeekBarPreference;
 import com.android.settings.SettingsActivity;
 import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.tts.TtsEnginePreference.RadioButtonGroupState;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -48,13 +46,15 @@
 import java.util.Objects;
 import java.util.Set;
 
-import static android.provider.Settings.Secure.TTS_DEFAULT_PITCH;
-import static android.provider.Settings.Secure.TTS_DEFAULT_RATE;
 import static android.provider.Settings.Secure.TTS_DEFAULT_SYNTH;
+import static android.provider.Settings.Secure.TTS_DEFAULT_PITCH;
 
-public class TextToSpeechSettings extends SettingsPreferenceFragment implements
-        Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
-        RadioButtonGroupState {
+public class TextToSpeechSettings extends SettingsPreferenceFragment
+        implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
+
+    private static final String STATE_KEY_LOCALE_ENTRIES = "locale_entries";
+    private static final String STATE_KEY_LOCALE_ENTRY_VALUES = "locale_entry_values";
+    private static final String STATE_KEY_LOCALE_VALUE = "locale_value";
 
     private static final String TAG = "TextToSpeechSettings";
     private static final boolean DBG = false;
@@ -62,84 +62,37 @@
     /** Preference key for the "play TTS example" preference. */
     private static final String KEY_PLAY_EXAMPLE = "tts_play_example";;
 
-    /** Preference key for the TTS pitch selection slider. */
-    private static final String KEY_DEFAULT_PITCH = "tts_default_pitch";
-
-    /** Preference key for the TTS rate selection slider. */
-    private static final String KEY_DEFAULT_RATE = "tts_default_rate";
-
-    /** Preference key for the TTS reset speech rate preference. */
-    private static final String KEY_RESET_SPEECH_RATE = "reset_speech_rate";
-
-    /** Preference key for the TTS reset speech pitch preference. */
-    private static final String KEY_RESET_SPEECH_PITCH = "reset_speech_pitch";
-
     /** Preference key for the TTS status field. */
     private static final String KEY_STATUS = "tts_status";
 
     /**
-     * Preference key for the engine selection preference.
-     */
-    private static final String KEY_ENGINE_PREFERENCE_SECTION =
-            "tts_engine_preference_section";
-
-    /**
      * These look like birth years, but they aren't mine. I'm much younger than this.
      */
     private static final int GET_SAMPLE_TEXT = 1983;
     private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
 
-    /**
-     * Speech rate value.
-     * This value should be kept in sync with the max value set in tts_settings xml.
-     */
-    private static final int MAX_SPEECH_RATE = 600;
-    private static final int MIN_SPEECH_RATE = 10;
-
-    /**
-     * Speech pitch value.
-     * TTS pitch value varies from 25 to 400, where 100 is the value
-     * for normal pitch. The max pitch value is set to 400, based on feedback from users
-     * and the GoogleTTS pitch variation range. The range for pitch is not set in stone
-     * and should be readjusted based on user need.
-     * This value should be kept in sync with the max value set in tts_settings xml.
-     */
-    private static final int MAX_SPEECH_PITCH = 400;
-    private static final int MIN_SPEECH_PITCH = 25;
-
-    private PreferenceCategory mEnginePreferenceCategory;
-    private SeekBarPreference mDefaultPitchPref;
-    private SeekBarPreference mDefaultRatePref;
-    private Preference mResetSpeechRate;
-    private Preference mResetSpeechPitch;
     private Preference mPlayExample;
     private Preference mEngineStatus;
 
-    private int mDefaultPitch = TextToSpeech.Engine.DEFAULT_PITCH;
-    private int mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
 
-    /**
-     * The currently selected engine.
-     */
+    private static final String KEY_ENGINE_LOCALE = "tts_default_lang";
+    private static final String KEY_ENGINE_SETTINGS = "tts_engine_settings";
+    private static final String KEY_INSTALL_DATA = "tts_install_data";
+
+    private int mSelectedLocaleIndex = -1;
+
+    /** The currently selected engine. */
     private String mCurrentEngine;
 
-    /**
-     * The engine checkbox that is currently checked. Saves us a bit of effort
-     * in deducing the right one from the currently selected engine.
-     */
-    private Checkable mCurrentChecked;
-
-    /**
-     * The previously selected TTS engine. Useful for rollbacks if the users
-     * choice is not loaded or fails a voice integrity check.
-     */
-    private String mPreviousEngine;
-
     private TextToSpeech mTts = null;
     private TtsEngines mEnginesHelper = null;
 
     private String mSampleText = null;
 
+    private ListPreference mLocalePreference;
+    private Preference mEngineSettingsPreference;
+    private Preference mInstallVoicesPreference;
+
     /**
      * Default locale used by selected TTS engine, null if not connected to any engine.
      */
@@ -164,18 +117,6 @@
         }
     };
 
-    /**
-     * The initialization listener used when the user changes his choice of
-     * engine (as opposed to when then screen is being initialized for the first
-     * time).
-     */
-    private final TextToSpeech.OnInitListener mUpdateListener = new TextToSpeech.OnInitListener() {
-        @Override
-        public void onInit(int status) {
-            onUpdateEngine(status);
-        }
-    };
-
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.TTS_TEXT_TO_SPEECH;
@@ -192,21 +133,40 @@
         mPlayExample.setOnPreferenceClickListener(this);
         mPlayExample.setEnabled(false);
 
-        mResetSpeechRate = findPreference(KEY_RESET_SPEECH_RATE);
-        mResetSpeechRate.setOnPreferenceClickListener(this);
-        mResetSpeechPitch = findPreference(KEY_RESET_SPEECH_PITCH);
-        mResetSpeechPitch.setOnPreferenceClickListener(this);
+        mEnginesHelper = new TtsEngines(getActivity().getApplicationContext());
 
-        mEnginePreferenceCategory = (PreferenceCategory) findPreference(
-                KEY_ENGINE_PREFERENCE_SECTION);
-        mDefaultPitchPref = (SeekBarPreference) findPreference(KEY_DEFAULT_PITCH);
-        mDefaultRatePref = (SeekBarPreference) findPreference(KEY_DEFAULT_RATE);
+        mLocalePreference = (ListPreference) findPreference(KEY_ENGINE_LOCALE);
+        mLocalePreference.setOnPreferenceChangeListener(this);
+
+        if (savedInstanceState == null) {
+            mLocalePreference.setEnabled(false);
+            mLocalePreference.setEntries(new CharSequence[0]);
+            mLocalePreference.setEntryValues(new CharSequence[0]);
+        } else {
+            // Repopulate mLocalePreference with saved state. Will be updated later with
+            // up-to-date values when checkTtsData() calls back with results.
+            final CharSequence[] entries =
+                    savedInstanceState.getCharSequenceArray(STATE_KEY_LOCALE_ENTRIES);
+            final CharSequence[] entryValues =
+                    savedInstanceState.getCharSequenceArray(STATE_KEY_LOCALE_ENTRY_VALUES);
+            final CharSequence value = savedInstanceState.getCharSequence(STATE_KEY_LOCALE_VALUE);
+
+            mLocalePreference.setEntries(entries);
+            mLocalePreference.setEntryValues(entryValues);
+            mLocalePreference.setValue(value != null ? value.toString() : null);
+            mLocalePreference.setEnabled(entries.length > 0);
+        }
+
+        mEngineSettingsPreference = findPreference(KEY_ENGINE_SETTINGS);
+        mEngineSettingsPreference.setOnPreferenceClickListener(this);
+        mInstallVoicesPreference = findPreference(KEY_INSTALL_DATA);
+        mInstallVoicesPreference.setOnPreferenceClickListener(this);
+        mInstallVoicesPreference.setEnabled(false);
 
         mEngineStatus = findPreference(KEY_STATUS);
         updateEngineStatus(R.string.tts_status_checking);
 
         mTts = new TextToSpeech(getActivity().getApplicationContext(), mInitListener);
-        mEnginesHelper = new TtsEngines(getActivity().getApplicationContext());
 
         setTtsUtteranceProgressListener();
         initSettings();
@@ -222,6 +182,23 @@
         if (mTts == null || mCurrentDefaultLocale == null) {
             return;
         }
+        if (!mTts.getDefaultEngine().equals(mTts.getCurrentEngine())) {
+            try {
+                mTts.shutdown();
+                mTts = null;
+            } catch (Exception e) {
+                Log.e(TAG, "Error shutting down TTS engine" + e);
+            }
+            mTts = new TextToSpeech(getActivity().getApplicationContext(), mInitListener);
+            setTtsUtteranceProgressListener();
+            initSettings();
+        } else {
+            // Do set pitch correctly after it may have changed, and unlike speed, it doesn't change
+            // immediately.
+            final ContentResolver resolver = getContentResolver();
+            mTts.setPitch(android.provider.Settings.Secure.getInt(resolver, TTS_DEFAULT_PITCH, TextToSpeech.Engine.DEFAULT_PITCH)/100.0f);
+        }
+
         Locale ttsDefaultLocale = mTts.getDefaultLanguage();
         if (mCurrentDefaultLocale != null && !mCurrentDefaultLocale.equals(ttsDefaultLocale)) {
             updateWidgetState(false);
@@ -259,26 +236,9 @@
     private void initSettings() {
         final ContentResolver resolver = getContentResolver();
 
-        // Set up the default rate and pitch.
-        mDefaultRate = android.provider.Settings.Secure.getInt(
-            resolver, TTS_DEFAULT_RATE, TextToSpeech.Engine.DEFAULT_RATE);
-        mDefaultPitch = android.provider.Settings.Secure.getInt(
-            resolver, TTS_DEFAULT_PITCH, TextToSpeech.Engine.DEFAULT_PITCH);
-
-        mDefaultRatePref.setProgress(getSeekBarProgressFromValue(KEY_DEFAULT_RATE, mDefaultRate));
-        mDefaultRatePref.setOnPreferenceChangeListener(this);
-        mDefaultRatePref.setMax(getSeekBarProgressFromValue(KEY_DEFAULT_RATE, MAX_SPEECH_RATE));
-
-        mDefaultPitchPref.setProgress(getSeekBarProgressFromValue(KEY_DEFAULT_PITCH,
-              mDefaultPitch));
-        mDefaultPitchPref.setOnPreferenceChangeListener(this);
-        mDefaultPitchPref.setMax(getSeekBarProgressFromValue(KEY_DEFAULT_PITCH,
-              MAX_SPEECH_PITCH));
-
         if (mTts != null) {
             mCurrentEngine = mTts.getCurrentEngine();
-            mTts.setSpeechRate(mDefaultRate/100.0f);
-            mTts.setPitch(mDefaultPitch/100.0f);
+            mTts.setPitch(android.provider.Settings.Secure.getInt(resolver, TTS_DEFAULT_PITCH, TextToSpeech.Engine.DEFAULT_PITCH)/100.0f);
         }
 
         SettingsActivity activity = null;
@@ -289,53 +249,37 @@
                     "Settings");
         }
 
-        mEnginePreferenceCategory.removeAll();
+        if (mCurrentEngine != null) {
+            EngineInfo info = mEnginesHelper.getEngineInfo(mCurrentEngine);
+            mEngineSettingsPreference.setSummary(info.label);
+            final Intent settingsIntent = mEnginesHelper.getSettingsIntent(info.name);
+            mEngineSettingsPreference.setIntent(settingsIntent);
+            if (settingsIntent == null) {
+                mEngineSettingsPreference.setEnabled(false);
+            }
 
-        List<EngineInfo> engines = mEnginesHelper.getEngines();
-        for (EngineInfo engine : engines) {
-            TtsEnginePreference enginePref = new TtsEnginePreference(getPrefContext(), engine,
-                    this, activity);
-            mEnginePreferenceCategory.addPreference(enginePref);
+            Preference mEnginePreference = findPreference("tts_engine_preference");
+            mEnginePreference.setSummary(info.label);
         }
 
         checkVoiceData(mCurrentEngine);
     }
 
     /**
-     * The minimum speech pitch/rate value should be > 0 but the minimum value of a seekbar in
-     * android is fixed at 0. Therefore, we increment the seekbar progress with MIN_SPEECH_VALUE
-     * so that the minimum seekbar progress value is MIN_SPEECH_PITCH/RATE.
-     *     SPEECH_VALUE = MIN_SPEECH_VALUE + SEEKBAR_PROGRESS
-     */
-    private int getValueFromSeekBarProgress(String preferenceKey, int progress) {
-        if (preferenceKey.equals(KEY_DEFAULT_RATE)) {
-            return MIN_SPEECH_RATE + progress;
-        } else if (preferenceKey.equals(KEY_DEFAULT_PITCH)) {
-            return MIN_SPEECH_PITCH + progress;
-        }
-        return progress;
-    }
-
-    /**
-     * Since we are appending the MIN_SPEECH value to the speech seekbar progress, the
-     * speech seekbar progress should be set to (speechValue - MIN_SPEECH value).
-     */
-    private int getSeekBarProgressFromValue(String preferenceKey, int value) {
-        if (preferenceKey.equals(KEY_DEFAULT_RATE)) {
-            return value - MIN_SPEECH_RATE;
-        } else if (preferenceKey.equals(KEY_DEFAULT_PITCH)) {
-            return value - MIN_SPEECH_PITCH;
-        }
-        return value;
-    }
-
-    /**
      * Called when the TTS engine is initialized.
      */
     public void onInitEngine(int status) {
         if (status == TextToSpeech.SUCCESS) {
             if (DBG) Log.d(TAG, "TTS engine for settings screen initialized.");
             checkDefaultLocale();
+            getActivity()
+                    .runOnUiThread(
+                            new Runnable() {
+                                @Override
+                                public void run() {
+                                    mLocalePreference.setEnabled(true);
+                                }
+                            });
         } else {
             if (DBG) Log.d(TAG, "TTS engine for settings screen failed to initialize successfully.");
             updateWidgetState(false);
@@ -452,9 +396,88 @@
             onSampleTextReceived(resultCode, data);
         } else if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
             onVoiceDataIntegrityCheckDone(data);
+            if (resultCode != TextToSpeech.Engine.CHECK_VOICE_DATA_FAIL) {
+                updateDefaultLocalePref(data);
+            }
         }
     }
 
+    private void updateDefaultLocalePref(Intent data) {
+        final ArrayList<String> availableLangs =
+                data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
+
+        final ArrayList<String> unavailableLangs =
+                data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
+
+        if (availableLangs != null && unavailableLangs.size() > 0) {
+            mInstallVoicesPreference.setEnabled(true);
+        } else {
+            mInstallVoicesPreference.setEnabled(false);
+        }
+
+        if (availableLangs == null || availableLangs.size() == 0) {
+            mLocalePreference.setEnabled(false);
+            return;
+        }
+        Locale currentLocale = null;
+        if (!mEnginesHelper.isLocaleSetToDefaultForEngine(mTts.getCurrentEngine())) {
+            currentLocale = mEnginesHelper.getLocalePrefForEngine(mTts.getCurrentEngine());
+        }
+
+        ArrayList<Pair<String, Locale>> entryPairs =
+                new ArrayList<Pair<String, Locale>>(availableLangs.size());
+        for (int i = 0; i < availableLangs.size(); i++) {
+            Locale locale = mEnginesHelper.parseLocaleString(availableLangs.get(i));
+            if (locale != null) {
+                entryPairs.add(new Pair<String, Locale>(locale.getDisplayName(), locale));
+            }
+        }
+
+        // Sort it
+        Collections.sort(
+                entryPairs,
+                new Comparator<Pair<String, Locale>>() {
+                    @Override
+                    public int compare(Pair<String, Locale> lhs, Pair<String, Locale> rhs) {
+                        return lhs.first.compareToIgnoreCase(rhs.first);
+                    }
+                });
+
+        // Get two arrays out of one of pairs
+        mSelectedLocaleIndex = 0; // Will point to the R.string.tts_lang_use_system value
+        CharSequence[] entries = new CharSequence[availableLangs.size() + 1];
+        CharSequence[] entryValues = new CharSequence[availableLangs.size() + 1];
+
+        entries[0] = getActivity().getString(R.string.tts_lang_use_system);
+        entryValues[0] = "";
+
+        int i = 1;
+        for (Pair<String, Locale> entry : entryPairs) {
+            if (entry.second.equals(currentLocale)) {
+                mSelectedLocaleIndex = i;
+            }
+            entries[i] = entry.first;
+            entryValues[i++] = entry.second.toString();
+        }
+
+        mLocalePreference.setEntries(entries);
+        mLocalePreference.setEntryValues(entryValues);
+        mLocalePreference.setEnabled(true);
+        setLocalePreference(mSelectedLocaleIndex);
+    }
+
+    /** Set entry from entry table in mLocalePreference */
+    private void setLocalePreference(int index) {
+        if (index < 0) {
+            mLocalePreference.setValue("");
+            mLocalePreference.setSummary(R.string.tts_lang_not_selected);
+        } else {
+            mLocalePreference.setValueIndex(index);
+            mLocalePreference.setSummary(mLocalePreference.getEntries()[index]);
+        }
+    }
+
+
     private String getDefaultSampleString() {
         if (mTts != null && mTts.getLanguage() != null) {
             try {
@@ -522,17 +545,59 @@
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object objValue) {
-        if (KEY_DEFAULT_RATE.equals(preference.getKey())) {
-            updateSpeechRate((Integer) objValue);
-        } else if (KEY_DEFAULT_PITCH.equals(preference.getKey())) {
-            updateSpeechPitchValue((Integer) objValue);
+        if (preference == mLocalePreference) {
+            String localeString = (String) objValue;
+            updateLanguageTo(
+                    (!TextUtils.isEmpty(localeString)
+                            ? mEnginesHelper.parseLocaleString(localeString)
+                            : null));
+            checkDefaultLocale();
+            return true;
         }
         return true;
     }
 
+    private void updateLanguageTo(Locale locale) {
+        int selectedLocaleIndex = -1;
+        String localeString = (locale != null) ? locale.toString() : "";
+        for (int i = 0; i < mLocalePreference.getEntryValues().length; i++) {
+            if (localeString.equalsIgnoreCase(mLocalePreference.getEntryValues()[i].toString())) {
+                selectedLocaleIndex = i;
+                break;
+            }
+        }
+
+        if (selectedLocaleIndex == -1) {
+            Log.w(TAG, "updateLanguageTo called with unknown locale argument");
+            return;
+        }
+        mLocalePreference.setSummary(mLocalePreference.getEntries()[selectedLocaleIndex]);
+        mSelectedLocaleIndex = selectedLocaleIndex;
+
+        mEnginesHelper.updateLocalePrefForEngine(mTts.getCurrentEngine(), locale);
+
+        // Null locale means "use system default"
+        mTts.setLanguage((locale != null) ? locale : Locale.getDefault());
+    }
+
     /**
-     * Called when mPlayExample, mResetSpeechRate or mResetSpeechPitch is
-     * clicked.
+     * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity so the
+     * required TTS files are properly installed.
+     */
+    private void installVoiceData() {
+        if (TextUtils.isEmpty(mCurrentEngine)) return;
+        Intent intent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
+        intent.setPackage(mCurrentEngine);
+        try {
+            Log.v(TAG, "Installing voice data: " + intent.toUri(0));
+            startActivity(intent);
+        } catch (ActivityNotFoundException ex) {
+            Log.e(TAG, "Failed to install TTS data, no acitivty found for " + intent + ")");
+        }
+    }
+
+    /**
+     * Called when mPlayExample, mInstallVoicesPreference is clicked.
      */
     @Override
     public boolean onPreferenceClick(Preference preference) {
@@ -541,57 +606,15 @@
             // the actual speaking
             speakSampleText();
             return true;
-        } else if (preference == mResetSpeechRate) {
-          int speechRateSeekbarProgress = getSeekBarProgressFromValue(
-              KEY_DEFAULT_RATE, TextToSpeech.Engine.DEFAULT_RATE);
-          mDefaultRatePref.setProgress(speechRateSeekbarProgress);
-          updateSpeechRate(speechRateSeekbarProgress);
-          return true;
-        } else if (preference == mResetSpeechPitch) {
-          int pitchSeekbarProgress = getSeekBarProgressFromValue(
-              KEY_DEFAULT_PITCH, TextToSpeech.Engine.DEFAULT_PITCH);
-          mDefaultPitchPref.setProgress(pitchSeekbarProgress);
-          updateSpeechPitchValue(pitchSeekbarProgress);
-          return true;
+        } else if (preference == mInstallVoicesPreference) {
+            installVoiceData();
+            return true;
         }
         return false;
     }
 
-    private void updateSpeechRate(int speechRateSeekBarProgress) {
-        mDefaultRate = getValueFromSeekBarProgress(KEY_DEFAULT_RATE,
-            speechRateSeekBarProgress);
-        try {
-            android.provider.Settings.Secure.putInt(getContentResolver(),
-                    TTS_DEFAULT_RATE, mDefaultRate);
-            if (mTts != null) {
-                mTts.setSpeechRate(mDefaultRate / 100.0f);
-            }
-            if (DBG) Log.d(TAG, "TTS default rate changed, now " + mDefaultRate);
-        } catch (NumberFormatException e) {
-            Log.e(TAG, "could not persist default TTS rate setting", e);
-        }
-        return;
-    }
-
-    private void updateSpeechPitchValue(int speechPitchSeekBarProgress) {
-        mDefaultPitch = getValueFromSeekBarProgress(KEY_DEFAULT_PITCH,
-            speechPitchSeekBarProgress);
-        try {
-            android.provider.Settings.Secure.putInt(getContentResolver(),
-                    TTS_DEFAULT_PITCH, mDefaultPitch);
-            if (mTts != null) {
-                mTts.setPitch(mDefaultPitch / 100.0f);
-            }
-            if (DBG) Log.d(TAG, "TTS default pitch changed, now" + mDefaultPitch);
-        } catch (NumberFormatException e) {
-            Log.e(TAG, "could not persist default TTS pitch setting", e);
-        }
-        return;
-    }
-
     private void updateWidgetState(boolean enable) {
         mPlayExample.setEnabled(enable);
-        mDefaultRatePref.setEnabled(enable);
         mEngineStatus.setEnabled(enable);
     }
 
@@ -614,67 +637,7 @@
         dialog.show();
     }
 
-    private void updateDefaultEngine(String engine) {
-        if (DBG) Log.d(TAG, "Updating default synth to : " + engine);
-
-        // Disable the "play sample text" preference and the speech
-        // rate preference while the engine is being swapped.
-        updateWidgetState(false);
-        updateEngineStatus(R.string.tts_status_checking);
-
-        // Keep track of the previous engine that was being used. So that
-        // we can reuse the previous engine.
-        //
-        // Note that if TextToSpeech#getCurrentEngine is not null, it means at
-        // the very least that we successfully bound to the engine service.
-        mPreviousEngine = mTts.getCurrentEngine();
-
-        // Step 1: Shut down the existing TTS engine.
-        if (mTts != null) {
-            try {
-                mTts.shutdown();
-                mTts = null;
-            } catch (Exception e) {
-                Log.e(TAG, "Error shutting down TTS engine" + e);
-            }
-        }
-
-        // Step 2: Connect to the new TTS engine.
-        // Step 3 is continued on #onUpdateEngine (below) which is called when
-        // the app binds successfully to the engine.
-        if (DBG) Log.d(TAG, "Updating engine : Attempting to connect to engine: " + engine);
-        mTts = new TextToSpeech(getActivity().getApplicationContext(), mUpdateListener, engine);
-        setTtsUtteranceProgressListener();
-    }
-
-    /*
-     * Step 3: We have now bound to the TTS engine the user requested. We will
-     * attempt to check voice data for the engine if we successfully bound to it,
-     * or revert to the previous engine if we didn't.
-     */
-    public void onUpdateEngine(int status) {
-        if (status == TextToSpeech.SUCCESS) {
-            if (DBG) {
-                Log.d(TAG, "Updating engine: Successfully bound to the engine: " +
-                        mTts.getCurrentEngine());
-            }
-            checkVoiceData(mTts.getCurrentEngine());
-        } else {
-            if (DBG) Log.d(TAG, "Updating engine: Failed to bind to engine, reverting.");
-            if (mPreviousEngine != null) {
-                // This is guaranteed to at least bind, since mPreviousEngine would be
-                // null if the previous bind to this engine failed.
-                mTts = new TextToSpeech(getActivity().getApplicationContext(), mInitListener,
-                        mPreviousEngine);
-                setTtsUtteranceProgressListener();
-            }
-            mPreviousEngine = null;
-        }
-    }
-
-    /*
-     * Step 4: Check whether the voice data for the engine is ok.
-     */
+    /** Check whether the voice data for the engine is ok. */
     private void checkVoiceData(String engine) {
         Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
         intent.setPackage(engine);
@@ -686,9 +649,7 @@
         }
     }
 
-    /*
-     * Step 5: The voice data check is complete.
-     */
+    /** The voice data check is complete. */
     private void onVoiceDataIntegrityCheckDone(Intent data) {
         final String engine = mTts.getCurrentEngine();
 
@@ -715,39 +676,5 @@
         if (evaluateDefaultLocale()) {
             getSampleText();
         }
-
-        final int engineCount = mEnginePreferenceCategory.getPreferenceCount();
-        for (int i = 0; i < engineCount; ++i) {
-            final Preference p = mEnginePreferenceCategory.getPreference(i);
-            if (p instanceof TtsEnginePreference) {
-                TtsEnginePreference enginePref = (TtsEnginePreference) p;
-                if (enginePref.getKey().equals(engine)) {
-                    enginePref.setVoiceDataDetails(data);
-                    break;
-                }
-            }
-        }
     }
-
-    @Override
-    public Checkable getCurrentChecked() {
-        return mCurrentChecked;
-    }
-
-    @Override
-    public String getCurrentKey() {
-        return mCurrentEngine;
-    }
-
-    @Override
-    public void setCurrentChecked(Checkable current) {
-        mCurrentChecked = current;
-    }
-
-    @Override
-    public void setCurrentKey(String key) {
-        mCurrentEngine = key;
-        updateDefaultEngine(mCurrentEngine);
-    }
-
 }
diff --git a/src/com/android/settings/tts/TtsEnginePreference.java b/src/com/android/settings/tts/TtsEnginePreference.java
index 385b861..9a84002 100644
--- a/src/com/android/settings/tts/TtsEnginePreference.java
+++ b/src/com/android/settings/tts/TtsEnginePreference.java
@@ -20,19 +20,16 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.os.Bundle;
 import android.speech.tts.TextToSpeech.EngineInfo;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceViewHolder;
 import android.util.Log;
-import android.view.View;
 import android.widget.Checkable;
 import android.widget.CompoundButton;
 import android.widget.RadioButton;
 
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
-import com.android.settings.Utils;
 
 
 public class TtsEnginePreference extends Preference {
@@ -40,31 +37,6 @@
     private static final String TAG = "TtsEnginePreference";
 
     /**
-     * Key for the name of the TTS engine passed in to the engine
-     * settings fragment {@link TtsEngineSettingsFragment}.
-     */
-    static final String FRAGMENT_ARGS_NAME = "name";
-
-    /**
-     * Key for the label of the TTS engine passed in to the engine
-     * settings fragment. This is used as the title of the fragment
-     * {@link TtsEngineSettingsFragment}.
-     */
-    static final String FRAGMENT_ARGS_LABEL = "label";
-
-    /**
-     * Key for the voice data data passed in to the engine settings
-     * fragmetn {@link TtsEngineSettingsFragment}.
-     */
-    static final String FRAGMENT_ARGS_VOICES = "voices";
-
-    /**
-     * The preference activity that owns this preference. Required
-     * for instantiating the engine specific settings screen.
-     */
-    private final SettingsActivity mSettingsActivity;
-
-    /**
      * The engine information for the engine this preference represents.
      * Contains it's name, label etc. which are used for display.
      */
@@ -81,7 +53,6 @@
      */
     private volatile boolean mPreventRadioButtonCallbacks;
 
-    private View mSettingsIcon;
     private RadioButton mRadioButton;
     private Intent mVoiceCheckData;
 
@@ -99,7 +70,6 @@
         setLayoutResource(R.layout.preference_tts_engine);
 
         mSharedState = state;
-        mSettingsActivity = prefActivity;
         mEngineInfo = info;
         mPreventRadioButtonCallbacks = false;
 
@@ -130,52 +100,10 @@
         mPreventRadioButtonCallbacks = false;
 
         mRadioButton = rb;
-
-        mSettingsIcon = view.findViewById(R.id.tts_engine_settings);
-        // Will be enabled only the engine has passed the voice check, and
-        // is currently enabled.
-        mSettingsIcon.setEnabled(isChecked && mVoiceCheckData != null);
-        if (!isChecked) {
-            mSettingsIcon.setAlpha(Utils.DISABLED_ALPHA);
-        }
-        mSettingsIcon.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                Bundle args = new Bundle();
-                args.putString(FRAGMENT_ARGS_NAME, mEngineInfo.name);
-                args.putString(FRAGMENT_ARGS_LABEL, mEngineInfo.label);
-                if (mVoiceCheckData != null) {
-                    args.putParcelable(FRAGMENT_ARGS_VOICES, mVoiceCheckData);
-                }
-
-                // Note that we use this instead of the (easier to use)
-                // SettingsActivity.startPreferenceFragment because the
-                // title will not be updated correctly in the fragment
-                // breadcrumb since it isn't inflated from the XML layout.
-                mSettingsActivity.startPreferencePanel(
-                        TtsEngineSettingsFragment.class.getName(),
-                        args, 0, mEngineInfo.label, null, 0);
-            }
-        });
-
-        if (mVoiceCheckData != null) {
-            mSettingsIcon.setEnabled(mRadioButton.isChecked());
-        }
     }
 
     public void setVoiceDataDetails(Intent data) {
         mVoiceCheckData = data;
-        // This might end up running before getView aboive, in which
-        // case mSettingsIcon && mRadioButton will be null. In this case
-        // getView will set the right values.
-        if (mSettingsIcon != null && mRadioButton != null) {
-            if (mRadioButton.isChecked()) {
-                mSettingsIcon.setEnabled(true);
-            } else {
-                mSettingsIcon.setEnabled(false);
-                mSettingsIcon.setAlpha(Utils.DISABLED_ALPHA);
-            }
-        }
     }
 
     private boolean shouldDisplayDataAlert() {
@@ -227,8 +155,6 @@
                 // Privileged engine, set it current
                 makeCurrentEngine(buttonView);
             }
-        } else {
-            mSettingsIcon.setEnabled(false);
         }
     }
 
@@ -239,7 +165,6 @@
         mSharedState.setCurrentChecked(current);
         mSharedState.setCurrentKey(getKey());
         callChangeListener(mSharedState.getCurrentKey());
-        mSettingsIcon.setEnabled(true);
     }
 
 
diff --git a/src/com/android/settings/tts/TtsEnginePreferenceFragment.java b/src/com/android/settings/tts/TtsEnginePreferenceFragment.java
new file mode 100644
index 0000000..e0ed8b7
--- /dev/null
+++ b/src/com/android/settings/tts/TtsEnginePreferenceFragment.java
@@ -0,0 +1,194 @@
+package com.android.settings.tts;
+
+import android.speech.tts.TextToSpeech;
+import com.android.settings.R;
+import android.os.Bundle;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import android.support.v7.preference.PreferenceCategory;
+import android.speech.tts.TtsEngines;
+import android.speech.tts.TextToSpeech.EngineInfo;
+import com.android.settings.SettingsActivity;
+import com.android.settings.tts.TtsEnginePreference.RadioButtonGroupState;
+import android.widget.Checkable;
+import android.util.Log;
+import static android.provider.Settings.Secure.TTS_DEFAULT_SYNTH;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.BaseSearchIndexProvider;
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import java.util.List;
+import java.util.Arrays;
+
+public class TtsEnginePreferenceFragment extends SettingsPreferenceFragment //implements
+        implements RadioButtonGroupState, Indexable {
+    private static final String TAG = "TtsEnginePreferenceFragment";
+
+    private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
+
+    /** The currently selected engine. */
+    private String mCurrentEngine;
+
+    /**
+     * The engine checkbox that is currently checked. Saves us a bit of effort in deducing the right
+     * one from the currently selected engine.
+     */
+    private Checkable mCurrentChecked;
+
+    /**
+     * The previously selected TTS engine. Useful for rollbacks if the users choice is not loaded or
+     * fails a voice integrity check.
+     */
+    private String mPreviousEngine;
+
+    private PreferenceCategory mEnginePreferenceCategory;
+
+    private TextToSpeech mTts = null;
+    private TtsEngines mEnginesHelper = null;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.tts_engine_picker);
+
+        mEnginePreferenceCategory =
+                (PreferenceCategory) findPreference("tts_engine_preference_category");
+        mEnginesHelper = new TtsEngines(getActivity().getApplicationContext());
+
+        mTts = new TextToSpeech(getActivity().getApplicationContext(), null);
+
+        initSettings();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.TTS_ENGINE_SETTINGS;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mTts != null) {
+            mTts.shutdown();
+            mTts = null;
+        }
+    }
+
+    private void initSettings() {
+        if (mTts != null) {
+            mCurrentEngine = mTts.getCurrentEngine();
+        }
+
+        mEnginePreferenceCategory.removeAll();
+
+        SettingsActivity activity = (SettingsActivity) getActivity();
+
+        List<EngineInfo> engines = mEnginesHelper.getEngines();
+        for (EngineInfo engine : engines) {
+            TtsEnginePreference enginePref =
+                    new TtsEnginePreference(getPrefContext(), engine, this, activity);
+            mEnginePreferenceCategory.addPreference(enginePref);
+        }
+    }
+
+    @Override
+    public Checkable getCurrentChecked() {
+        return mCurrentChecked;
+    }
+
+    @Override
+    public String getCurrentKey() {
+        return mCurrentEngine;
+    }
+
+    @Override
+    public void setCurrentChecked(Checkable current) {
+        mCurrentChecked = current;
+    }
+
+    /**
+     * The initialization listener used when the user changes his choice of engine (as opposed to
+     * when then screen is being initialized for the first time).
+     */
+    private final TextToSpeech.OnInitListener mUpdateListener =
+            new TextToSpeech.OnInitListener() {
+                @Override
+                public void onInit(int status) {
+                    onUpdateEngine(status);
+                }
+            };
+
+    private void updateDefaultEngine(String engine) {
+        Log.d(TAG, "Updating default synth to : " + engine);
+
+        // Keep track of the previous engine that was being used. So that
+        // we can reuse the previous engine.
+        //
+        // Note that if TextToSpeech#getCurrentEngine is not null, it means at
+        // the very least that we successfully bound to the engine service.
+        mPreviousEngine = mTts.getCurrentEngine();
+
+        // Step 1: Shut down the existing TTS engine.
+        Log.i(TAG, "Shutting down current tts engine");
+        if (mTts != null) {
+            try {
+                mTts.shutdown();
+                mTts = null;
+            } catch (Exception e) {
+                Log.e(TAG, "Error shutting down TTS engine" + e);
+            }
+        }
+
+        // Step 2: Connect to the new TTS engine.
+        // Step 3 is continued on #onUpdateEngine (below) which is called when
+        // the app binds successfully to the engine.
+        Log.i(TAG, "Updating engine : Attempting to connect to engine: " + engine);
+        mTts = new TextToSpeech(getActivity().getApplicationContext(), mUpdateListener, engine);
+        Log.i(TAG, "Success");
+    }
+
+    /**
+     * Step 3: We have now bound to the TTS engine the user requested. We will attempt to check
+     * voice data for the engine if we successfully bound to it, or revert to the previous engine if
+     * we didn't.
+     */
+    public void onUpdateEngine(int status) {
+        if (status == TextToSpeech.SUCCESS) {
+            Log.d(
+                    TAG,
+                    "Updating engine: Successfully bound to the engine: "
+                            + mTts.getCurrentEngine());
+            android.provider.Settings.Secure.putString(
+                    getContentResolver(), TTS_DEFAULT_SYNTH, mTts.getCurrentEngine());
+        } else {
+            Log.d(TAG, "Updating engine: Failed to bind to engine, reverting.");
+            if (mPreviousEngine != null) {
+                // This is guaranteed to at least bind, since mPreviousEngine would be
+                // null if the previous bind to this engine failed.
+                mTts =
+                        new TextToSpeech(
+                                getActivity().getApplicationContext(), null, mPreviousEngine);
+            }
+            mPreviousEngine = null;
+        }
+    }
+
+    @Override
+    public void setCurrentKey(String key) {
+        mCurrentEngine = key;
+        updateDefaultEngine(mCurrentEngine);
+    }
+
+    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(
+                        Context context, boolean enabled) {
+                    Log.i(TAG, "Indexing");
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = R.xml.tts_engine_picker;
+                    return Arrays.asList(sir);
+                }
+            };
+}
diff --git a/src/com/android/settings/tts/TtsEngineSettingsFragment.java b/src/com/android/settings/tts/TtsEngineSettingsFragment.java
deleted file mode 100644
index 42222df..0000000
--- a/src/com/android/settings/tts/TtsEngineSettingsFragment.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * 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.tts;
-
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.speech.tts.TextToSpeech;
-import android.speech.tts.TtsEngines;
-import android.support.v7.preference.ListPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.Preference.OnPreferenceChangeListener;
-import android.support.v7.preference.Preference.OnPreferenceClickListener;
-import android.support.v7.preference.PreferenceScreen;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Locale;
-
-
-public class TtsEngineSettingsFragment extends SettingsPreferenceFragment implements
-        OnPreferenceClickListener, OnPreferenceChangeListener {
-    private static final String TAG = "TtsEngineSettings";
-    private static final boolean DBG = false;
-
-    private static final String KEY_ENGINE_LOCALE = "tts_default_lang";
-    private static final String KEY_ENGINE_SETTINGS = "tts_engine_settings";
-    private static final String KEY_INSTALL_DATA = "tts_install_data";
-
-    private static final String STATE_KEY_LOCALE_ENTRIES = "locale_entries";
-    private static final String STATE_KEY_LOCALE_ENTRY_VALUES= "locale_entry_values";
-    private static final String STATE_KEY_LOCALE_VALUE = "locale_value";
-
-    private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
-
-    private TtsEngines mEnginesHelper;
-    private ListPreference mLocalePreference;
-    private Preference mEngineSettingsPreference;
-    private Preference mInstallVoicesPreference;
-    private Intent mEngineSettingsIntent;
-    private Intent mVoiceDataDetails;
-
-    private TextToSpeech mTts;
-
-    private int mSelectedLocaleIndex = -1;
-
-    private final TextToSpeech.OnInitListener mTtsInitListener = new TextToSpeech.OnInitListener() {
-        @Override
-        public void onInit(int status) {
-            if (status != TextToSpeech.SUCCESS) {
-                finishFragment();
-            } else {
-                getActivity().runOnUiThread(new Runnable() {
-                    @Override
-                    public void run() {
-                        mLocalePreference.setEnabled(true);
-                    }
-                });
-            }
-        }
-    };
-
-    private final BroadcastReceiver mLanguagesChangedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            // Installed or uninstalled some data packs
-            if (TextToSpeech.Engine.ACTION_TTS_DATA_INSTALLED.equals(intent.getAction())) {
-                checkTtsData();
-            }
-        }
-    };
-
-    public TtsEngineSettingsFragment() {
-        super();
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return MetricsEvent.TTS_ENGINE_SETTINGS;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        addPreferencesFromResource(R.xml.tts_engine_settings);
-        mEnginesHelper = new TtsEngines(getActivity());
-
-        final PreferenceScreen root = getPreferenceScreen();
-        mLocalePreference = (ListPreference) root.findPreference(KEY_ENGINE_LOCALE);
-        mLocalePreference.setOnPreferenceChangeListener(this);
-        mEngineSettingsPreference = root.findPreference(KEY_ENGINE_SETTINGS);
-        mEngineSettingsPreference.setOnPreferenceClickListener(this);
-        mInstallVoicesPreference = root.findPreference(KEY_INSTALL_DATA);
-        mInstallVoicesPreference.setOnPreferenceClickListener(this);
-
-        root.setTitle(getEngineLabel());
-        root.setKey(getEngineName());
-        mEngineSettingsPreference.setTitle(getResources().getString(
-                R.string.tts_engine_settings_title, getEngineLabel()));
-
-        mEngineSettingsIntent = mEnginesHelper.getSettingsIntent(getEngineName());
-        if (mEngineSettingsIntent == null) {
-            mEngineSettingsPreference.setEnabled(false);
-        }
-        mInstallVoicesPreference.setEnabled(false);
-
-        if (savedInstanceState == null) {
-            mLocalePreference.setEnabled(false);
-            mLocalePreference.setEntries(new CharSequence[0]);
-            mLocalePreference.setEntryValues(new CharSequence[0]);
-        } else {
-            // Repopulate mLocalePreference with saved state. Will be updated later with
-            // up-to-date values when checkTtsData() calls back with results.
-            final CharSequence[] entries =
-                    savedInstanceState.getCharSequenceArray(STATE_KEY_LOCALE_ENTRIES);
-            final CharSequence[] entryValues =
-                    savedInstanceState.getCharSequenceArray(STATE_KEY_LOCALE_ENTRY_VALUES);
-            final CharSequence value =
-                    savedInstanceState.getCharSequence(STATE_KEY_LOCALE_VALUE);
-
-            mLocalePreference.setEntries(entries);
-            mLocalePreference.setEntryValues(entryValues);
-            mLocalePreference.setValue(value != null ? value.toString() : null);
-            mLocalePreference.setEnabled(entries.length > 0);
-        }
-
-        mVoiceDataDetails = getArguments().getParcelable(TtsEnginePreference.FRAGMENT_ARGS_VOICES);
-
-        mTts = new TextToSpeech(getActivity().getApplicationContext(), mTtsInitListener,
-                getEngineName());
-
-        // Check if data packs changed
-        checkTtsData();
-
-        getActivity().registerReceiver(mLanguagesChangedReceiver,
-                new IntentFilter(TextToSpeech.Engine.ACTION_TTS_DATA_INSTALLED));
-    }
-
-    @Override
-    public void onDestroy() {
-        getActivity().unregisterReceiver(mLanguagesChangedReceiver);
-        mTts.shutdown();
-        super.onDestroy();
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-
-        // Save the mLocalePreference values, so we can repopulate it with entries.
-        outState.putCharSequenceArray(STATE_KEY_LOCALE_ENTRIES,
-                mLocalePreference.getEntries());
-        outState.putCharSequenceArray(STATE_KEY_LOCALE_ENTRY_VALUES,
-                mLocalePreference.getEntryValues());
-        outState.putCharSequence(STATE_KEY_LOCALE_VALUE,
-                mLocalePreference.getValue());
-    }
-
-    private final void checkTtsData() {
-        Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
-        intent.setPackage(getEngineName());
-        try {
-            if (DBG) Log.d(TAG, "Updating engine: Checking voice data: " + intent.toUri(0));
-            startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
-        } catch (ActivityNotFoundException ex) {
-            Log.e(TAG, "Failed to check TTS data, no activity found for " + intent + ")");
-        }
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
-            if (resultCode != TextToSpeech.Engine.CHECK_VOICE_DATA_FAIL) {
-                updateVoiceDetails(data);
-            } else {
-                Log.e(TAG, "CheckVoiceData activity failed");
-            }
-        }
-    }
-
-    private void updateVoiceDetails(Intent data) {
-        if (data == null){
-            Log.e(TAG, "Engine failed voice data integrity check (null return)" +
-                    mTts.getCurrentEngine());
-            return;
-        }
-        mVoiceDataDetails = data;
-
-        if (DBG) Log.d(TAG, "Parsing voice data details, data: " + mVoiceDataDetails.toUri(0));
-
-        final ArrayList<String> available = mVoiceDataDetails.getStringArrayListExtra(
-                TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
-        final ArrayList<String> unavailable = mVoiceDataDetails.getStringArrayListExtra(
-                TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
-
-        if (unavailable != null && unavailable.size() > 0) {
-            mInstallVoicesPreference.setEnabled(true);
-        } else {
-            mInstallVoicesPreference.setEnabled(false);
-        }
-
-        if (available == null){
-            Log.e(TAG, "TTS data check failed (available == null).");
-            mLocalePreference.setEnabled(false);
-            return;
-        } else {
-            updateDefaultLocalePref(available);
-        }
-    }
-
-    private void updateDefaultLocalePref(ArrayList<String> availableLangs) {
-        if (availableLangs == null || availableLangs.size() == 0) {
-            mLocalePreference.setEnabled(false);
-            return;
-        }
-        Locale currentLocale = null;
-        if (!mEnginesHelper.isLocaleSetToDefaultForEngine(getEngineName())) {
-            currentLocale = mEnginesHelper.getLocalePrefForEngine(getEngineName());
-        }
-
-        ArrayList<Pair<String, Locale>> entryPairs =
-                new ArrayList<Pair<String, Locale>>(availableLangs.size());
-        for (int i = 0; i < availableLangs.size(); i++) {
-            Locale locale = mEnginesHelper.parseLocaleString(availableLangs.get(i));
-            if (locale != null){
-                entryPairs.add(new Pair<String, Locale>(
-                        locale.getDisplayName(), locale));
-            }
-        }
-
-        // Sort it
-        Collections.sort(entryPairs, new Comparator<Pair<String, Locale>>() {
-            @Override
-            public int compare(Pair<String, Locale> lhs, Pair<String, Locale> rhs) {
-                return lhs.first.compareToIgnoreCase(rhs.first);
-            }
-        });
-
-        // Get two arrays out of one of pairs
-        mSelectedLocaleIndex = 0; // Will point to the R.string.tts_lang_use_system value
-        CharSequence[] entries = new CharSequence[availableLangs.size()+1];
-        CharSequence[] entryValues = new CharSequence[availableLangs.size()+1];
-
-        entries[0] = getActivity().getString(R.string.tts_lang_use_system);
-        entryValues[0] = "";
-
-        int i = 1;
-        for (Pair<String, Locale> entry : entryPairs) {
-            if (entry.second.equals(currentLocale)) {
-                mSelectedLocaleIndex = i;
-            }
-            entries[i] = entry.first;
-            entryValues[i++] = entry.second.toString();
-        }
-
-        mLocalePreference.setEntries(entries);
-        mLocalePreference.setEntryValues(entryValues);
-        mLocalePreference.setEnabled(true);
-        setLocalePreference(mSelectedLocaleIndex);
-    }
-
-    /** Set entry from entry table in mLocalePreference */
-    private void setLocalePreference(int index) {
-        if (index < 0) {
-            mLocalePreference.setValue("");
-            mLocalePreference.setSummary(R.string.tts_lang_not_selected);
-        } else {
-            mLocalePreference.setValueIndex(index);
-            mLocalePreference.setSummary(mLocalePreference.getEntries()[index]);
-        }
-    }
-
-    /**
-     * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity
-     * so the required TTS files are properly installed.
-     */
-    private void installVoiceData() {
-        if (TextUtils.isEmpty(getEngineName())) return;
-        Intent intent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
-        intent.setPackage(getEngineName());
-        try {
-            Log.v(TAG, "Installing voice data: " + intent.toUri(0));
-            startActivity(intent);
-        } catch (ActivityNotFoundException ex) {
-            Log.e(TAG, "Failed to install TTS data, no acitivty found for " + intent + ")");
-        }
-    }
-
-    @Override
-    public boolean onPreferenceClick(Preference preference) {
-        if (preference == mInstallVoicesPreference) {
-            installVoiceData();
-            return true;
-        } else if (preference == mEngineSettingsPreference) {
-            startActivity(mEngineSettingsIntent);
-            return true;
-        }
-
-        return false;
-    }
-
-    @Override
-    public boolean onPreferenceChange(Preference preference, Object newValue) {
-        if (preference == mLocalePreference) {
-            String localeString = (String) newValue;
-            updateLanguageTo((!TextUtils.isEmpty(localeString) ?
-                    mEnginesHelper.parseLocaleString(localeString) : null));
-            return true;
-        }
-        return false;
-    }
-
-    private void updateLanguageTo(Locale locale) {
-        int selectedLocaleIndex = -1;
-        String localeString = (locale != null) ? locale.toString() : "";
-        for (int i=0; i < mLocalePreference.getEntryValues().length; i++) {
-            if (localeString.equalsIgnoreCase(mLocalePreference.getEntryValues()[i].toString())) {
-                selectedLocaleIndex = i;
-                break;
-            }
-        }
-
-        if (selectedLocaleIndex == -1) {
-            Log.w(TAG, "updateLanguageTo called with unknown locale argument");
-            return;
-        }
-        mLocalePreference.setSummary(mLocalePreference.getEntries()[selectedLocaleIndex]);
-        mSelectedLocaleIndex = selectedLocaleIndex;
-
-        mEnginesHelper.updateLocalePrefForEngine(getEngineName(), locale);
-
-        if (getEngineName().equals(mTts.getCurrentEngine())) {
-            // Null locale means "use system default"
-            mTts.setLanguage((locale != null) ? locale : Locale.getDefault());
-        }
-    }
-
-    private String getEngineName() {
-        return getArguments().getString(TtsEnginePreference.FRAGMENT_ARGS_NAME);
-    }
-
-    private String getEngineLabel() {
-        return getArguments().getString(TtsEnginePreference.FRAGMENT_ARGS_LABEL);
-    }
-}
diff --git a/src/com/android/settings/tts/TtsSlidersFragment.java b/src/com/android/settings/tts/TtsSlidersFragment.java
new file mode 100644
index 0000000..7fdbd62
--- /dev/null
+++ b/src/com/android/settings/tts/TtsSlidersFragment.java
@@ -0,0 +1,207 @@
+package com.android.settings.tts;
+
+import android.speech.tts.TextToSpeech;
+import com.android.settings.R;
+import android.os.Bundle;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import android.util.Log;
+import com.android.settings.SeekBarPreference;
+import android.support.v7.preference.Preference;
+import android.content.ContentResolver;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.BaseSearchIndexProvider;
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import java.util.List;
+import java.util.Arrays;
+
+import static android.provider.Settings.Secure.TTS_DEFAULT_PITCH;
+import static android.provider.Settings.Secure.TTS_DEFAULT_RATE;
+
+public class TtsSlidersFragment extends SettingsPreferenceFragment
+        implements Preference.OnPreferenceChangeListener,
+                Preference.OnPreferenceClickListener,
+                Indexable {
+    private static final String TAG = TtsSlidersFragment.class.getSimpleName();
+    private static final boolean DBG = false;
+
+    /** Preference key for the TTS pitch selection slider. */
+    private static final String KEY_DEFAULT_PITCH = "tts_default_pitch";
+
+    /** Preference key for the TTS rate selection slider. */
+    private static final String KEY_DEFAULT_RATE = "tts_default_rate";
+
+    /** Preference key for the TTS reset speech rate preference. */
+    private static final String KEY_RESET_SPEECH_RATE = "reset_speech_rate";
+
+    /** Preference key for the TTS reset speech pitch preference. */
+    private static final String KEY_RESET_SPEECH_PITCH = "reset_speech_pitch";
+
+    /**
+     * Speech rate value. This value should be kept in sync with the max value set in tts_settings
+     * xml.
+     */
+    private static final int MAX_SPEECH_RATE = 600;
+
+    private static final int MIN_SPEECH_RATE = 10;
+
+    /**
+     * Speech pitch value. TTS pitch value varies from 25 to 400, where 100 is the value for normal
+     * pitch. The max pitch value is set to 400, based on feedback from users and the GoogleTTS
+     * pitch variation range. The range for pitch is not set in stone and should be readjusted based
+     * on user need. This value should be kept in sync with the max value set in tts_settings xml.
+     */
+    private static final int MAX_SPEECH_PITCH = 400;
+
+    private static final int MIN_SPEECH_PITCH = 25;
+
+    private int mDefaultPitch = TextToSpeech.Engine.DEFAULT_PITCH;
+    private int mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
+
+    private SeekBarPreference mDefaultPitchPref;
+    private SeekBarPreference mDefaultRatePref;
+    private Preference mResetSpeechRate;
+    private Preference mResetSpeechPitch;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.tts_sliders);
+
+        mResetSpeechRate = findPreference(KEY_RESET_SPEECH_RATE);
+        mResetSpeechRate.setOnPreferenceClickListener(this);
+        mResetSpeechPitch = findPreference(KEY_RESET_SPEECH_PITCH);
+        mResetSpeechPitch.setOnPreferenceClickListener(this);
+
+        mDefaultPitchPref = (SeekBarPreference) findPreference(KEY_DEFAULT_PITCH);
+        mDefaultRatePref = (SeekBarPreference) findPreference(KEY_DEFAULT_RATE);
+
+        initSettings();
+    }
+
+    private void initSettings() {
+        final ContentResolver resolver = getContentResolver();
+        // Set up the default rate and pitch.
+        mDefaultRate =
+                android.provider.Settings.Secure.getInt(
+                        resolver, TTS_DEFAULT_RATE, TextToSpeech.Engine.DEFAULT_RATE);
+        mDefaultPitch =
+                android.provider.Settings.Secure.getInt(
+                        resolver, TTS_DEFAULT_PITCH, TextToSpeech.Engine.DEFAULT_PITCH);
+
+        mDefaultRatePref.setProgress(getSeekBarProgressFromValue(KEY_DEFAULT_RATE, mDefaultRate));
+        mDefaultRatePref.setOnPreferenceChangeListener(this);
+        mDefaultRatePref.setMax(getSeekBarProgressFromValue(KEY_DEFAULT_RATE, MAX_SPEECH_RATE));
+
+        mDefaultPitchPref.setProgress(
+                getSeekBarProgressFromValue(KEY_DEFAULT_PITCH, mDefaultPitch));
+        mDefaultPitchPref.setOnPreferenceChangeListener(this);
+        mDefaultPitchPref.setMax(getSeekBarProgressFromValue(KEY_DEFAULT_PITCH, MAX_SPEECH_PITCH));
+    }
+
+    /**
+     * The minimum speech pitch/rate value should be > 0 but the minimum value of a seekbar in
+     * android is fixed at 0. Therefore, we increment the seekbar progress with MIN_SPEECH_VALUE so
+     * that the minimum seekbar progress value is MIN_SPEECH_PITCH/RATE. SPEECH_VALUE =
+     * MIN_SPEECH_VALUE + SEEKBAR_PROGRESS
+     */
+    private int getValueFromSeekBarProgress(String preferenceKey, int progress) {
+        if (preferenceKey.equals(KEY_DEFAULT_RATE)) {
+            return MIN_SPEECH_RATE + progress;
+        } else if (preferenceKey.equals(KEY_DEFAULT_PITCH)) {
+            return MIN_SPEECH_PITCH + progress;
+        }
+        return progress;
+    }
+
+    /**
+     * Since we are appending the MIN_SPEECH value to the speech seekbar progress, the speech
+     * seekbar progress should be set to (speechValue - MIN_SPEECH value).
+     */
+    private int getSeekBarProgressFromValue(String preferenceKey, int value) {
+        if (preferenceKey.equals(KEY_DEFAULT_RATE)) {
+            return value - MIN_SPEECH_RATE;
+        } else if (preferenceKey.equals(KEY_DEFAULT_PITCH)) {
+            return value - MIN_SPEECH_PITCH;
+        }
+        return value;
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object objValue) {
+        if (KEY_DEFAULT_RATE.equals(preference.getKey())) {
+            updateSpeechRate((Integer) objValue);
+        } else if (KEY_DEFAULT_PITCH.equals(preference.getKey())) {
+            updateSpeechPitchValue((Integer) objValue);
+        }
+        return true;
+    }
+
+    /** Called when mPlayExample, mResetSpeechRate or mResetSpeechPitch is clicked. */
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        if (preference == mResetSpeechRate) {
+            int speechRateSeekbarProgress =
+                    getSeekBarProgressFromValue(KEY_DEFAULT_RATE, TextToSpeech.Engine.DEFAULT_RATE);
+            mDefaultRatePref.setProgress(speechRateSeekbarProgress);
+            updateSpeechRate(speechRateSeekbarProgress);
+            return true;
+        } else if (preference == mResetSpeechPitch) {
+            int pitchSeekbarProgress =
+                    getSeekBarProgressFromValue(
+                            KEY_DEFAULT_PITCH, TextToSpeech.Engine.DEFAULT_PITCH);
+            mDefaultPitchPref.setProgress(pitchSeekbarProgress);
+            updateSpeechPitchValue(pitchSeekbarProgress);
+            return true;
+        }
+        return false;
+    }
+
+    private void updateSpeechRate(int speechRateSeekBarProgress) {
+        mDefaultRate = getValueFromSeekBarProgress(KEY_DEFAULT_RATE, speechRateSeekBarProgress);
+        try {
+            android.provider.Settings.Secure.putInt(
+                    getContentResolver(), TTS_DEFAULT_RATE, mDefaultRate);
+            if (DBG) Log.d(TAG, "TTS default rate changed, now " + mDefaultRate);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "could not persist default TTS rate setting", e);
+        }
+        return;
+    }
+
+    private void updateSpeechPitchValue(int speechPitchSeekBarProgress) {
+        mDefaultPitch = getValueFromSeekBarProgress(KEY_DEFAULT_PITCH, speechPitchSeekBarProgress);
+        try {
+            android.provider.Settings.Secure.putInt(
+                    getContentResolver(), TTS_DEFAULT_PITCH, mDefaultPitch);
+            if (DBG) Log.d(TAG, "TTS default pitch changed, now" + mDefaultPitch);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "could not persist default TTS pitch setting", e);
+        }
+        return;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.TTS_SLIDERS;
+    }
+
+    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(
+                        Context context, boolean enabled) {
+                    Log.i(TAG, "Indexing");
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = R.xml.tts_sliders;
+                    return Arrays.asList(sir);
+                }
+            };
+}
diff --git a/src/com/android/settings/webview/WebViewAppListAdapter.java b/src/com/android/settings/webview/WebViewAppListAdapter.java
index 85dbf7c..4c36a47 100644
--- a/src/com/android/settings/webview/WebViewAppListAdapter.java
+++ b/src/com/android/settings/webview/WebViewAppListAdapter.java
@@ -16,8 +16,10 @@
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
+import android.graphics.Color;
 import android.support.annotation.VisibleForTesting;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -36,6 +38,7 @@
  */
 class WebViewAppListAdapter extends ArrayAdapter<WebViewApplicationInfo> {
     private final LayoutInflater mInflater;
+    private final String mCurrentWebViewPackageName;
 
     public WebViewAppListAdapter(Context context,
             WebViewUpdateServiceWrapper webviewUpdateServiceWrapper) {
@@ -53,6 +56,10 @@
             packageInfoList.add(info);
         }
         addAll(packageInfoList);
+
+        PackageInfo currentWebViewPackage = webviewUpdateServiceWrapper.getCurrentWebViewPackage();
+        mCurrentWebViewPackageName =
+                currentWebViewPackage == null ? null : currentWebViewPackage.packageName;
     }
 
     @Override
@@ -80,6 +87,11 @@
         holder.disabled.setVisibility(View.GONE);
         // Only allow a package to be chosen if it is enabled and installed for all users.
         convertView.setEnabled(isEnabled(position));
+        if (info.info.packageName.equals(mCurrentWebViewPackageName)) {
+            convertView.setBackgroundColor(Color.GRAY);
+        } else {
+            convertView.setBackgroundColor(Color.WHITE);
+        }
         return convertView;
     }
 
diff --git a/src/com/android/settings/webview/WebViewAppPreferenceController.java b/src/com/android/settings/webview/WebViewAppPreferenceController.java
index eb5467a..cfb358e 100644
--- a/src/com/android/settings/webview/WebViewAppPreferenceController.java
+++ b/src/com/android/settings/webview/WebViewAppPreferenceController.java
@@ -73,9 +73,10 @@
      * Handle the return-value from the WebViewAppPicker Activity.
      */
     public void onActivityResult(int resultCode, Intent data) {
-        if (resultCode == Activity.RESULT_OK) {
-            updateState(null);
-        }
+        // Update the preference summary no matter whether we succeeded to change the webview
+        // implementation correctly - we might have changed implementation to one the user did not
+        // choose.
+        updateState(null);
     }
 
     private String getCurrentWebViewPackageLabel(Context context) {
diff --git a/src/com/android/settings/wifi/ConfigureWifiSettings.java b/src/com/android/settings/wifi/ConfigureWifiSettings.java
index 6cbdb29..682662a 100644
--- a/src/com/android/settings/wifi/ConfigureWifiSettings.java
+++ b/src/com/android/settings/wifi/ConfigureWifiSettings.java
@@ -15,8 +15,6 @@
  */
 package com.android.settings.wifi;
 
-import static android.content.Context.WIFI_SERVICE;
-
 import android.content.Context;
 import android.net.wifi.WifiManager;
 import android.provider.SearchIndexableResource;
@@ -32,6 +30,8 @@
 import java.util.Arrays;
 import java.util.List;
 
+import static android.content.Context.WIFI_SERVICE;
+
 public class ConfigureWifiSettings extends DashboardFragment {
 
     private static final String TAG = "ConfigureWifiSettings";
@@ -44,12 +44,6 @@
     }
 
     @Override
-    protected String getCategoryKey() {
-        // We don't want to inject any external settings into this screen.
-        return null;
-    }
-
-    @Override
     protected String getLogTag() {
         return TAG;
     }
@@ -64,7 +58,6 @@
         mWifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
         final List<PreferenceController> controllers = new ArrayList<>();
         controllers.add(new WifiInfoPreferenceController(context, getLifecycle(), mWifiManager));
-        controllers.add(new SavedNetworkPreferenceController(context, mWifiManager));
         controllers.add(new CellularFallbackPreferenceController(context));
         controllers.add(new AllowRecommendationPreferenceController(context));
         controllers.add(new NotifyOpenNetworksPreferenceController(context, getLifecycle()));
diff --git a/src/com/android/settings/wifi/LinkablePreference.java b/src/com/android/settings/wifi/LinkablePreference.java
new file mode 100644
index 0000000..6b1b87d
--- /dev/null
+++ b/src/com/android/settings/wifi/LinkablePreference.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 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.wifi;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.Spannable;
+import android.text.style.TextAppearanceSpan;
+import android.util.AttributeSet;
+import android.widget.TextView;
+import com.android.settings.LinkifyUtils;
+
+/**
+ * A preference with a title that can have linkable content on click.
+ */
+public class LinkablePreference extends Preference {
+
+    private LinkifyUtils.OnClickListener mClickListener;
+    private CharSequence mContentTitle;
+    private CharSequence mContentDescription;
+
+    public LinkablePreference(Context ctx, AttributeSet attrs, int defStyle) {
+        super(ctx, attrs, defStyle);
+        setSelectable(false);
+    }
+
+    public LinkablePreference(Context ctx, AttributeSet attrs) {
+        super(ctx, attrs);
+        setSelectable(false);
+    }
+
+    public LinkablePreference(Context ctx) {
+        super(ctx);
+        setSelectable(false);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+
+        TextView textView = (TextView) view.findViewById(android.R.id.title);
+        if (textView == null || mContentTitle == null || mClickListener == null) {
+            return;
+        }
+
+        textView.setSingleLine(false);
+        StringBuilder contentBuilder = new StringBuilder().append(mContentTitle);
+        if (mContentDescription != null) {
+            contentBuilder.append("\n\n");
+            contentBuilder.append(mContentDescription);
+        }
+
+        boolean linked = LinkifyUtils.linkify(textView, contentBuilder, mClickListener);
+        if (linked && mContentTitle != null) {
+            // Embolden and enlarge the title.
+            Spannable boldSpan = (Spannable) textView.getText();
+            boldSpan.setSpan(
+                    new TextAppearanceSpan(
+                            getContext(), android.R.style.TextAppearance_Medium),
+                    0,
+                    mContentTitle.length(),
+                    Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+            textView.setText(boldSpan);
+        }
+    }
+
+    /**
+     * Sets the linkable text for the Preference title.
+     * @param contentTitle text to set the Preference title.
+     * @param contentDescription description text to append underneath title, can be null.
+     * @param clickListener OnClickListener for the link portion of the text.
+     */
+    public void setText(
+            CharSequence contentTitle,
+            @Nullable CharSequence contentDescription,
+            LinkifyUtils.OnClickListener clickListener) {
+        mContentTitle = contentTitle;
+        mContentDescription = contentDescription;
+        mClickListener = clickListener;
+        // sets the title so that the title TextView is not hidden in super.onBindViewHolder()
+        super.setTitle(contentTitle);
+    }
+
+    /**
+     * Sets the title of the LinkablePreference. resets linkable text for reusability.
+     */
+    @Override
+    public void setTitle(int titleResId) {
+        mContentTitle = null;
+        mContentDescription = null;
+        super.setTitle(titleResId);
+    }
+
+    /**
+     * Sets the title of the LinkablePreference. resets linkable text for reusability.
+     */
+    @Override
+    public void setTitle(CharSequence title) {
+        mContentTitle = null;
+        mContentDescription = null;
+        super.setTitle(title);
+    }
+}
diff --git a/src/com/android/settings/wifi/SavedNetworkPreferenceController.java b/src/com/android/settings/wifi/SavedNetworkPreferenceController.java
deleted file mode 100644
index c3ad355..0000000
--- a/src/com/android/settings/wifi/SavedNetworkPreferenceController.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2017 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.wifi;
-
-import android.content.Context;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager;
-
-import com.android.settings.core.PreferenceController;
-
-import java.util.List;
-
-/**
- * {@link PreferenceController} that opens saved network subsetting.
- */
-public class SavedNetworkPreferenceController extends PreferenceController {
-
-    private static final String KEY_SAVED_NETWORKS = "saved_networks";
-
-    private final WifiManager mWifiManager;
-
-    public SavedNetworkPreferenceController(Context context, WifiManager wifiManager) {
-        super(context);
-        mWifiManager = wifiManager;
-    }
-
-    @Override
-    public boolean isAvailable() {
-        final List<WifiConfiguration> config = mWifiManager.getConfiguredNetworks();
-        return config != null && !config.isEmpty();
-    }
-
-    @Override
-    public String getPreferenceKey() {
-        return KEY_SAVED_NETWORKS;
-    }
-}
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 1dd18bd..4fd93b5 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -40,11 +40,10 @@
 import android.os.Process;
 import android.provider.Settings;
 import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
 import android.support.v7.preference.PreferenceManager;
 import android.support.v7.preference.PreferenceViewHolder;
-import android.text.Spannable;
 import android.text.TextUtils;
-import android.text.style.TextAppearanceSpan;
 import android.util.Log;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
@@ -53,8 +52,6 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.TextView.BufferType;
 import android.widget.Toast;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -102,7 +99,6 @@
     private static final int MENU_ID_FORGET = Menu.FIRST + 7;
     private static final int MENU_ID_MODIFY = Menu.FIRST + 8;
     private static final int MENU_ID_WRITE_NFC = Menu.FIRST + 9;
-    private static final int MENU_ID_CONFIGURE = Menu.FIRST + 10;
 
     public static final int WIFI_DIALOG_ID = 1;
     /* package */ static final int WPS_PBC_DIALOG_ID = 2;
@@ -115,6 +111,9 @@
     private static final String SAVED_WIFI_NFC_DIALOG_STATE = "wifi_nfc_dlg_state";
 
     private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list";
+    private static final String PREF_KEY_ACCESS_POINTS = "access_points";
+    private static final String PREF_KEY_ADDITIONAL_SETTINGS = "additional_settings";
+    private static final String PREF_KEY_SAVED_NETWORKS = "saved_networks";
 
     protected WifiManager mWifiManager;
     private WifiManager.ActionListener mConnectListener;
@@ -152,7 +151,12 @@
     private HandlerThread mBgThread;
 
     private AccessPointPreference.UserBadgeCache mUserBadgeCache;
+
+    private PreferenceCategory mAccessPointsPreferenceCategory;
+    private PreferenceCategory mAdditionalSettingsPreferenceCategory;
     private Preference mAddPreference;
+    private Preference mSavedNetworksPreference;
+    private LinkablePreference mStatusMessagePreference;
 
     private MenuItem mScanMenuItem;
 
@@ -177,9 +181,18 @@
         getPreferenceManager().setPreferenceComparisonCallback(
                 new PreferenceManager.SimplePreferenceComparisonCallback());
         addPreferencesFromResource(R.xml.wifi_settings);
-        mAddPreference = new Preference(getContext());
+
+        mAccessPointsPreferenceCategory =
+                (PreferenceCategory) findPreference(PREF_KEY_ACCESS_POINTS);
+        mAdditionalSettingsPreferenceCategory =
+                (PreferenceCategory) findPreference(PREF_KEY_ADDITIONAL_SETTINGS);
+        mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS);
+
+        Context prefContext = getPrefContext();
+        mAddPreference = new Preference(prefContext);
         mAddPreference.setIcon(R.drawable.ic_menu_add_inset);
         mAddPreference.setTitle(R.string.wifi_add_network);
+        mStatusMessagePreference = new LinkablePreference(prefContext);
 
         mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager());
 
@@ -305,7 +318,7 @@
     /**
      * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard)
      */
-    /* package */ WifiEnabler createWifiEnabler() {
+    private WifiEnabler createWifiEnabler() {
         final SettingsActivity activity = (SettingsActivity) getActivity();
         return new WifiEnabler(activity, activity.getSwitchBar(), mMetricsFeatureProvider);
     }
@@ -314,7 +327,6 @@
     public void onResume() {
         final Activity activity = getActivity();
         super.onResume();
-        removePreference("dummy");
         if (mWifiEnabler != null) {
             mWifiEnabler.resume(activity);
         }
@@ -346,14 +358,13 @@
      * @param menu
      */
     void addOptionsMenuItems(Menu menu) {
-        final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled();
-        mScanMenuItem = menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh);
-        mScanMenuItem.setEnabled(wifiIsEnabled)
-               .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
         menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-        menu.add(Menu.NONE, MENU_ID_CONFIGURE, 0, R.string.wifi_menu_configure)
-                .setIcon(R.drawable.ic_settings_24dp)
+
+        final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled();
+        mScanMenuItem = menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh)
+                .setIcon(com.android.internal.R.drawable.ic_menu_refresh);
+        mScanMenuItem.setEnabled(wifiIsEnabled)
                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
     }
 
@@ -424,18 +435,6 @@
                             null);
                 }
                 return true;
-            case MENU_ID_CONFIGURE:
-                if (getActivity() instanceof SettingsActivity) {
-                    ((SettingsActivity) getActivity()).startPreferencePanel(
-                            ConfigureWifiSettings.class.getCanonicalName(), null,
-                            R.string.wifi_configure_titlebar, null, this, 0);
-                } else {
-                    startFragment(this, ConfigureWifiSettings.class.getCanonicalName(),
-                            R.string.wifi_configure_titlebar, -1 /* Do not request a results */,
-                            null);
-                }
-                return true;
-
         }
         return super.onOptionsItemSelected(item);
     }
@@ -622,10 +621,10 @@
         // Safeguard from some delayed event handling
         if (getActivity() == null) return;
         if (isUiRestricted()) {
+            mAccessPointsPreferenceCategory.removeAll();
             if (!isUiRestrictedByOnlyAdmin()) {
                 addMessagePreference(R.string.wifi_empty_list_user_restricted);
             }
-            getPreferenceScreen().removeAll();
             return;
         }
         final int wifiState = mWifiManager.getWifiState();
@@ -638,7 +637,8 @@
 
                 boolean hasAvailableAccessPoints = false;
                 int index = 0;
-                cacheRemoveAllPrefs(getPreferenceScreen());
+                mAccessPointsPreferenceCategory.removePreference(mStatusMessagePreference);
+                cacheRemoveAllPrefs(mAccessPointsPreferenceCategory);
                 for (AccessPoint accessPoint : accessPoints) {
                     // Ignore access points that are out of range.
                     if (accessPoint.getLevel() != -1) {
@@ -665,12 +665,12 @@
                             onPreferenceTreeClick(preference);
                             mOpenSsid = null;
                         }
-                        getPreferenceScreen().addPreference(preference);
+                        mAccessPointsPreferenceCategory.addPreference(preference);
                         accessPoint.setListener(this);
                         preference.refresh();
                     }
                 }
-                removeCachedPrefs(getPreferenceScreen());
+                removeCachedPrefs(mAccessPointsPreferenceCategory);
                 if (!hasAvailableAccessPoints) {
                     setProgressBarVisible(true);
                     Preference pref = new Preference(getContext()) {
@@ -683,14 +683,16 @@
                     };
                     pref.setSelectable(false);
                     pref.setSummary(R.string.wifi_empty_list_wifi_on);
-                    pref.setOrder(0);
+                    pref.setOrder(index++);
                     pref.setKey(PREF_KEY_EMPTY_WIFI_LIST);
-                    getPreferenceScreen().addPreference(pref);
-                    mAddPreference.setOrder(1);
-                    getPreferenceScreen().addPreference(mAddPreference);
+                    mAccessPointsPreferenceCategory.addPreference(pref);
+                    mAddPreference.setOrder(index++);
+                    mAccessPointsPreferenceCategory.addPreference(mAddPreference);
+                    setSavedNetworkPreferenceVisibility();
                 } else {
                     mAddPreference.setOrder(index++);
-                    getPreferenceScreen().addPreference(mAddPreference);
+                    mAccessPointsPreferenceCategory.addPreference(mAddPreference);
+                    setSavedNetworkPreferenceVisibility();
                     setProgressBarVisible(false);
                 }
                 if (mScanMenuItem != null) {
@@ -699,7 +701,7 @@
                 break;
 
             case WifiManager.WIFI_STATE_ENABLING:
-                getPreferenceScreen().removeAll();
+                mAccessPointsPreferenceCategory.removeAll();
                 setProgressBarVisible(true);
                 break;
 
@@ -710,6 +712,7 @@
 
             case WifiManager.WIFI_STATE_DISABLED:
                 setOffMessage();
+                setSavedNetworkPreferenceVisibility();
                 setProgressBarVisible(false);
                 if (mScanMenuItem != null) {
                     mScanMenuItem.setEnabled(false);
@@ -718,17 +721,20 @@
         }
     }
 
+    private void setSavedNetworkPreferenceVisibility() {
+        if (mWifiTracker.doSavedNetworksExist()) {
+            mAdditionalSettingsPreferenceCategory.addPreference(mSavedNetworksPreference);
+        } else {
+            mAdditionalSettingsPreferenceCategory.removePreference(mSavedNetworksPreference);
+        }
+    }
+
     private void setOffMessage() {
         if (isUiRestricted()) {
             if (!isUiRestrictedByOnlyAdmin()) {
                 addMessagePreference(R.string.wifi_empty_list_user_restricted);
             }
-            getPreferenceScreen().removeAll();
-            return;
-        }
-
-        TextView emptyTextView = getEmptyTextView();
-        if (emptyTextView == null) {
+            mAccessPointsPreferenceCategory.removeAll();
             return;
         }
 
@@ -744,35 +750,27 @@
         if (!wifiScanningMode) {
             // Show only the brief text if the user is not allowed to configure scanning settings,
             // or the scanning mode has been turned off.
-            emptyTextView.setText(briefText, BufferType.SPANNABLE);
+            mStatusMessagePreference.setTitle(briefText);
         } else {
-            // Append the description of scanning settings with link.
-            final StringBuilder contentBuilder = new StringBuilder();
-            contentBuilder.append(briefText);
-            contentBuilder.append("\n\n");
-            contentBuilder.append(getText(R.string.wifi_scan_notify_text));
-            LinkifyUtils.linkify(emptyTextView, contentBuilder, new LinkifyUtils.OnClickListener() {
+            LinkifyUtils.OnClickListener clickListener = new LinkifyUtils.OnClickListener() {
                 @Override
                 public void onClick() {
-                    final SettingsActivity activity =
-                            (SettingsActivity) WifiSettings.this.getActivity();
+                    final SettingsActivity activity = (SettingsActivity) getActivity();
                     activity.startPreferencePanel(ScanningSettings.class.getName(), null,
                             R.string.location_scanning_screen_title, null, null, 0);
                 }
-            });
+            };
+            mStatusMessagePreference.setText(
+                    briefText, getText(R.string.wifi_scan_notify_text), clickListener);
         }
-        // Embolden and enlarge the brief description anyway.
-        Spannable boldSpan = (Spannable) emptyTextView.getText();
-        boldSpan.setSpan(
-                new TextAppearanceSpan(getActivity(), android.R.style.TextAppearance_Medium), 0,
-                briefText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-        getPreferenceScreen().removeAll();
+        mAccessPointsPreferenceCategory.removeAll();
+        mAccessPointsPreferenceCategory.addPreference(mStatusMessagePreference);
     }
 
     private void addMessagePreference(int messageId) {
-        TextView emptyTextView = getEmptyTextView();
-        if (emptyTextView != null) emptyTextView.setText(messageId);
-        getPreferenceScreen().removeAll();
+        mStatusMessagePreference.setTitle(messageId);
+        mAccessPointsPreferenceCategory.removeAll();
+        mAccessPointsPreferenceCategory.addPreference(mStatusMessagePreference);
     }
 
     protected void setProgressBarVisible(boolean visible) {
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
index 3ba4d02..31090be 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
@@ -82,7 +82,8 @@
 
     @Test
     public void testCategory_isAccount() {
-        assertThat(mFragment.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_ACCOUNT);
+        assertThat(new AccountDetailDashboardFragment().getCategoryKey())
+                .isEqualTo(CategoryKey.CATEGORY_ACCOUNT);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java
index 9fc416d..3dd3a65 100644
--- a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java
@@ -18,7 +18,6 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.Build;
@@ -79,7 +78,7 @@
 
     @Mock private Context mContext;
     @Mock private PackageManagerWrapper mPackageManager;
-    @Mock private IPackageManager mPackageManagerService;
+    @Mock private IPackageManagerWrapper mPackageManagerService;
     @Mock private DevicePolicyManagerWrapper mDevicePolicyManager;
     private List<UserInfo> mUsersToCount;
 
diff --git a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
index aba4a12..2f344dc 100644
--- a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java
@@ -18,12 +18,15 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
-import android.content.pm.IPackageManager;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.ArraySet;
 
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
@@ -39,10 +42,11 @@
 import org.robolectric.shadows.ShadowApplication;
 
 import java.util.Arrays;
+import java.util.Set;
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.when;
-import org.robolectric.shadows.ShadowApplication;
+
 /**
  * Tests for {@link ApplicationFeatureProviderImpl}.
  */
@@ -66,7 +70,7 @@
     private @Mock UserManager mUserManager;
     private @Mock Context mContext;
     private @Mock PackageManagerWrapper mPackageManager;
-    @Mock private IPackageManager mPackageManagerService;
+    @Mock private IPackageManagerWrapper mPackageManagerService;
     @Mock private DevicePolicyManagerWrapper mDevicePolicyManager;
 
     private ApplicationFeatureProvider mProvider;
@@ -139,6 +143,43 @@
 
     }
 
+    @Test
+    public void testFindPersistentPreferredActivities() throws Exception {
+        when(mUserManager.getUserProfiles()).thenReturn(Arrays.asList(new UserHandle(MAIN_USER_ID),
+                new UserHandle(MANAGED_PROFILE_ID)));
+
+        final Intent viewIntent = new Intent(Intent.ACTION_VIEW);
+        final Intent editIntent = new Intent(Intent.ACTION_EDIT);
+        final Intent sendIntent = new Intent(Intent.ACTION_SEND);
+
+        final ResolveInfo app1 = createResolveInfo(APP_1);
+        final ResolveInfo app2 = createResolveInfo(APP_2);
+        when(mPackageManagerService.findPersistentPreferredActivity(viewIntent, MAIN_USER_ID))
+                .thenReturn(app1);
+        when(mPackageManagerService.findPersistentPreferredActivity(viewIntent, MANAGED_PROFILE_ID))
+                .thenReturn(app1);
+        when(mPackageManagerService.findPersistentPreferredActivity(editIntent, MAIN_USER_ID))
+                .thenReturn(null);
+        when(mPackageManagerService.findPersistentPreferredActivity(editIntent, MANAGED_PROFILE_ID))
+                .thenReturn(app2);
+        when(mPackageManagerService.findPersistentPreferredActivity(sendIntent, MAIN_USER_ID))
+                .thenReturn(app1);
+        when(mPackageManagerService.findPersistentPreferredActivity(sendIntent, MANAGED_PROFILE_ID))
+                .thenReturn(null);
+
+        final Set<ApplicationFeatureProvider.PersistentPreferredActivityInfo> expectedActivities
+                = new ArraySet<>();
+        expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_1,
+                MAIN_USER_ID));
+        expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_1,
+                MANAGED_PROFILE_ID));
+        expectedActivities.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo(APP_2,
+                MANAGED_PROFILE_ID));
+
+        assertThat(mProvider.findPersistentPreferredActivities(
+                new Intent[] {viewIntent, editIntent, sendIntent})).isEqualTo(expectedActivities);
+    }
+
     private void setUpUsersAndInstalledApps() {
         when(mUserManager.getUsers(true)).thenReturn(Arrays.asList(
                 new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN),
@@ -156,4 +197,12 @@
                         ApplicationTestUtils.buildInfo(APP_2_UID, APP_2, 0 /* flags */,
                                 Build.VERSION_CODES.LOLLIPOP)));
     }
+
+    private ResolveInfo createResolveInfo(String packageName) {
+        final ActivityInfo activityInfo = new ActivityInfo();
+        activityInfo.packageName = packageName;
+        final ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = activityInfo;
+        return resolveInfo;
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java b/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java
index daed00d..262c9e0 100644
--- a/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/PictureInPictureSettingsTest.java
@@ -107,9 +107,7 @@
         if (resizeableActivityState.length > 0) {
             activities = new ActivityInfoWrapper[resizeableActivityState.length];
             for (int i = 0; i < activities.length; i++) {
-                activities[i] = new MockActivityInfo(resizeableActivityState[i]
-                        ? ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE
-                        : ActivityInfo.RESIZE_MODE_UNRESIZEABLE);
+                activities[i] = new MockActivityInfo(resizeableActivityState[i]);
             }
         }
         return PictureInPictureSettings.checkPackageHasPictureInPictureActivities(packageName,
@@ -118,15 +116,15 @@
 
     private class MockActivityInfo implements ActivityInfoWrapper {
 
-        private int mResizeMode;
+        private boolean mSupportsPictureInPicture;
 
-        public MockActivityInfo(int resizeMode) {
-            mResizeMode = resizeMode;
+        public MockActivityInfo(boolean supportsPictureInPicture) {
+            mSupportsPictureInPicture = supportsPictureInPicture;
         }
 
         @Override
-        public int getResizeMode() {
-            return mResizeMode;
+        public boolean supportsPictureInPicture() {
+            return mSupportsPictureInPicture;
         }
     }
 }
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
index d479e0a..327575e 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
@@ -26,7 +26,6 @@
 import com.android.settings.core.PreferenceController;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settingslib.drawer.CategoryKey;
 import com.android.settingslib.drawer.DashboardCategory;
 import com.android.settingslib.drawer.Tile;
 
@@ -200,11 +199,6 @@
         }
 
         @Override
-        protected String getCategoryKey() {
-            return CategoryKey.CATEGORY_HOMEPAGE;
-        }
-
-        @Override
         public PreferenceScreen getPreferenceScreen() {
             return mScreen;
         }
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
index 7776633..a377505 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -140,6 +140,8 @@
         assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
         assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
                 ManageApplications.class.getName());
+        assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0))
+                .isEqualTo(R.string.apps_storage);
     }
 
     @Test
@@ -159,6 +161,24 @@
     }
 
     @Test
+    public void testClickGames() {
+        mPreference.setKey("pref_games");
+        mController.handlePreferenceTreeClick(mPreference);
+
+        final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mFragment.getActivity()).startActivityAsUser(argumentCaptor.capture(),
+                any(UserHandle.class));
+
+        Intent intent = argumentCaptor.getValue();
+        assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN);
+        assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
+        assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
+                ManageApplications.class.getName());
+        assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0))
+                .isEqualTo(R.string.game_storage_settings);
+    }
+
+    @Test
     public void testMeasurementCompletedUpdatesPreferences() {
         StorageItemPreferenceAlternate audio = new StorageItemPreferenceAlternate(mContext);
         StorageItemPreferenceAlternate image = new StorageItemPreferenceAlternate(mContext);
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java
index 3dd1fd7..b55b512 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java
@@ -81,6 +81,10 @@
         final Preference preference = new Preference(mContext, null, 0, 0);
         preference.setVisible(true);
 
+        setNumberOfEnterpriseInstalledPackages(0);
+        mController.updateState(preference);
+        assertThat(preference.isVisible()).isFalse();
+
         setNumberOfEnterpriseInstalledPackages(20);
         when(mContext.getResources().getQuantityString(
                 R.plurals.enterprise_privacy_number_enterprise_installed_packages, 20, 20))
@@ -88,10 +92,6 @@
         mController.updateState(preference);
         assertThat(preference.getTitle()).isEqualTo("20 packages");
         assertThat(preference.isVisible()).isTrue();
-
-        setNumberOfEnterpriseInstalledPackages(0);
-        mController.updateState(preference);
-        assertThat(preference.isVisible()).isFalse();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
index de4d02e..2559769 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
@@ -73,7 +73,7 @@
         final List<PreferenceController> controllers = mSettings.getPreferenceControllers(
                 ShadowApplication.getInstance().getApplicationContext());
         assertThat(controllers).isNotNull();
-        assertThat(controllers.size()).isEqualTo(11);
+        assertThat(controllers.size()).isEqualTo(12);
         assertThat(controllers.get(0)).isInstanceOf(InstalledPackagesPreferenceController.class);
         assertThat(controllers.get(1)).isInstanceOf(NetworkLogsPreferenceController.class);
         assertThat(controllers.get(2)).isInstanceOf(BugReportsPreferenceController.class);
@@ -87,9 +87,11 @@
         assertThat(controllers.get(7)).isInstanceOf(
                 AdminGrantedCameraPermissionPreferenceController.class);
         assertThat(controllers.get(8)).isInstanceOf(
-                AlwaysOnVpnPrimaryUserPreferenceController.class);
+                EnterpriseSetDefaultAppsPreferenceController.class);
         assertThat(controllers.get(9)).isInstanceOf(
+                AlwaysOnVpnPrimaryUserPreferenceController.class);
+        assertThat(controllers.get(10)).isInstanceOf(
                 AlwaysOnVpnManagedProfilePreferenceController.class);
-        assertThat(controllers.get(10)).isInstanceOf(GlobalHttpProxyPreferenceController.class);
+        assertThat(controllers.get(11)).isInstanceOf(GlobalHttpProxyPreferenceController.class);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
new file mode 100644
index 0000000..84520a5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsPreferenceControllerTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 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.enterprise;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import android.support.v7.preference.Preference;
+import android.util.ArraySet;
+
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.ApplicationFeatureProvider;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.Set;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link EnterpriseSetDefaultAppsPreferenceController}.
+ */
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public final class EnterpriseSetDefaultAppsPreferenceControllerTest {
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    private FakeFeatureFactory mFeatureFactory;
+
+    private EnterpriseSetDefaultAppsPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        FakeFeatureFactory.setupForTest(mContext);
+        mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+        mController = new EnterpriseSetDefaultAppsPreferenceController(mContext);
+    }
+
+    private static Intent buildIntent(String action, String category, String protocol,
+            String type) {
+        final Intent intent = new Intent(action);
+        if (category != null) {
+            intent.addCategory(category);
+        }
+        if (protocol != null) {
+            intent.setData(Uri.parse(protocol));
+        }
+        if (type != null) {
+            intent.setType(type);
+        }
+        return intent;
+    }
+
+    private void setEnterpriseSetDefaultApps(Intent[] intents, int number) {
+        final Set<ApplicationFeatureProvider.PersistentPreferredActivityInfo> apps
+                = new ArraySet<>(number);
+        for (int i = 0; i < number; i++) {
+            apps.add(new ApplicationFeatureProvider.PersistentPreferredActivityInfo("app", i));
+        }
+        when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(
+                argThat(new MatchesIntents(intents)))).thenReturn(apps);
+    }
+
+    @Test
+    public void testUpdateState() {
+        final Preference preference = new Preference(mContext, null, 0, 0);
+        preference.setVisible(true);
+
+        when(mFeatureFactory.applicationFeatureProvider.findPersistentPreferredActivities(
+                anyObject())).thenReturn(
+                        new ArraySet<ApplicationFeatureProvider.PersistentPreferredActivityInfo>());
+        mController.updateState(preference);
+        assertThat(preference.isVisible()).isFalse();
+
+        setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_VIEW,
+                Intent.CATEGORY_BROWSABLE, "http:", null)}, 1);
+        setEnterpriseSetDefaultApps(new Intent[] {new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
+                new Intent(MediaStore.ACTION_VIDEO_CAPTURE)}, 2);
+        setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_VIEW, null, "geo:",
+                null)}, 4);
+        setEnterpriseSetDefaultApps(new Intent[] {new Intent(Intent.ACTION_SENDTO),
+                new Intent(Intent.ACTION_SEND), new Intent(Intent.ACTION_SEND_MULTIPLE)}, 8);
+        setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_INSERT, null, null,
+                "vnd.android.cursor.dir/event")}, 16);
+        setEnterpriseSetDefaultApps(new Intent[] {buildIntent(Intent.ACTION_PICK, null, null,
+                ContactsContract.Contacts.CONTENT_TYPE)}, 32);
+        setEnterpriseSetDefaultApps(new Intent[] {new Intent(Intent.ACTION_DIAL),
+                new Intent(Intent.ACTION_CALL)}, 64);
+        when(mContext.getResources().getQuantityString(
+                R.plurals.enterprise_privacy_number_enterprise_set_default_apps, 127, 127))
+                .thenReturn("127 apps");
+        mController.updateState(preference);
+        assertThat(preference.getTitle()).isEqualTo("127 apps");
+        assertThat(preference.isVisible()).isTrue();
+    }
+
+    @Test
+    public void testIsAvailable() {
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void testHandlePreferenceTreeClick() {
+        assertThat(mController.handlePreferenceTreeClick(new Preference(mContext, null, 0, 0)))
+                .isFalse();
+    }
+
+    @Test
+    public void testGetPreferenceKey() {
+        assertThat(mController.getPreferenceKey())
+                .isEqualTo("number_enterprise_set_default_apps");
+    }
+
+    private static class MatchesIntents extends ArgumentMatcher<Intent[]> {
+        private final Intent[] mExpectedIntents;
+
+        MatchesIntents(Intent[] intents) {
+            mExpectedIntents = intents;
+        }
+
+        @Override
+        public boolean matches(Object object) {
+            final Intent[] actualIntents = (Intent[]) object;
+            if (actualIntents == null) {
+                return false;
+            }
+            if (actualIntents.length != mExpectedIntents.length) {
+                return false;
+            }
+            for (int i = 0; i < mExpectedIntents.length; i++) {
+                if (!mExpectedIntents[i].filterEquals(actualIntents[i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/network/NetworkDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/network/NetworkDashboardFragmentTest.java
index b30e3ff..dc96ed6 100644
--- a/tests/robotests/src/com/android/settings/network/NetworkDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/network/NetworkDashboardFragmentTest.java
@@ -21,6 +21,7 @@
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
 import com.android.settings.accounts.UserAndAccountDashboardFragment;
+import com.android.settings.dashboard.DashboardFragmentRegistry;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settingslib.drawer.CategoryKey;
 import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
index 1cf72ea..e89f009 100644
--- a/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
+++ b/tests/robotests/src/com/android/settings/search/DatabaseIndexingManagerTest.java
@@ -41,6 +41,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 import static com.android.settings.dashboard.SiteMapManager.SITE_MAP_COLUMNS;
 import static com.google.common.truth.Truth.assertThat;
@@ -51,7 +52,7 @@
 public class DatabaseIndexingManagerTest {
     private final String localeStr = "en_US";
 
-    private final int rank = 42;
+    private final int rank = 8;
     private final String title = "title\u2011title";
     private final String updatedTitle = "title-title";
     private final String normalizedTitle = "titletitle";
@@ -213,6 +214,20 @@
     }
 
     @Test
+    public void testAddResourceWithNIKs_RowsInsertedDisabled() {
+        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
+        // Only add 2 of 6 items to be disabled.
+        String[] keys = {"gesture_double_tap_power", "gesture_swipe_down_fingerprint"};
+        Map<String, List<String>> niks = getNonIndexableKeys(keys);
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource, niks);
+
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
+        assertThat(cursor.getCount()).isEqualTo(2);
+        cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
+        assertThat(cursor.getCount()).isEqualTo(4);
+    }
+
+    @Test
     public void testAddResourceHeader_RowsMatch() {
         SearchIndexableResource resource = getFakeResource(R.xml.application_settings);
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
@@ -529,21 +544,19 @@
 
     @Test
     public void testResourceProvider_ResourceRowInserted() {
-        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
-        resource.xmlResId = 0;
+        SearchIndexableResource resource = getFakeResource(0);
         resource.className = "com.android.settings.LegalSettings";
 
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
                 new HashMap<>());
         Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index", null);
-        assertThat(cursor.getCount()).isEqualTo(2);
+        assertThat(cursor.getCount()).isEqualTo(6);
     }
 
     @Test
     public void testResourceProvider_ResourceRowMatches() {
-        SearchIndexableResource resource = getFakeResource(R.xml.gesture_settings);
-        resource.xmlResId = 0;
-        resource.className = "com.android.settings.LegalSettings";
+        SearchIndexableResource resource = getFakeResource(0);
+        resource.className = "com.android.settings.display.ScreenZoomSettings";
 
         mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
                 new HashMap<>());
@@ -555,9 +568,9 @@
         // Data Rank
         assertThat(cursor.getInt(1)).isEqualTo(rank);
         // Data Title
-        assertThat(cursor.getString(2)).isEqualTo("Legal information");
+        assertThat(cursor.getString(2)).isEqualTo("Display size");
         // Normalized Title
-        assertThat(cursor.getString(3)).isEqualTo("legal information");
+        assertThat(cursor.getString(3)).isEqualTo("display size");
         // Summary On
         assertThat(cursor.getString(4)).isEmpty();
         // Summary On Normalized
@@ -569,12 +582,13 @@
         // Entries - only on for list preferences
         assertThat(cursor.getString(8)).isNull();
         // Keywords
-        assertThat(cursor.getString(9)).isEmpty();
+        assertThat(cursor.getString(9)).isEqualTo(
+                "display density screen zoom scale scaling");
         // Screen Title
-        assertThat(cursor.getString(10)).isEqualTo("Legal information");
+        assertThat(cursor.getString(10)).isEqualTo("Display size");
         // Class Name
         assertThat(cursor.getString(11))
-                .isEqualTo("com.android.settings.LegalSettings");
+                .isEqualTo("com.android.settings.display.ScreenZoomSettings");
         // Icon
         assertThat(cursor.getInt(12)).isEqualTo(iconResId);
         // Intent Action
@@ -595,6 +609,20 @@
         assertThat(cursor.getBlob(20)).isNull();
     }
 
+    @Test
+    public void testResourceProvider_DisabledResourceRowsInserted() {
+        SearchIndexableResource resource = getFakeResource(0);
+        resource.className = "com.android.settings.LegalSettings";
+
+        mManager.indexOneSearchIndexableData(mDb, localeStr, resource,
+                new HashMap<String, List<String>>());
+
+        Cursor cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 1", null);
+        assertThat(cursor.getCount()).isEqualTo(2);
+        cursor = mDb.rawQuery("SELECT * FROM prefs_index WHERE enabled = 0", null);
+        assertThat(cursor.getCount()).isEqualTo(4);
+    }
+
     // Util functions
 
     private SearchIndexableRaw getFakeRaw() {
@@ -636,4 +664,11 @@
         sir.enabled = enabled;
         return sir;
     }
-}
+
+    private Map<String, List<String>> getNonIndexableKeys(String[] keys) {
+        Map<String, List<String>> niks = new HashMap<>();
+        List<String> keysList = new ArrayList<>(Arrays.asList(keys));
+        niks.put(packageName, keysList);
+        return niks;
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java
index c749a00..31e6e6c 100644
--- a/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java
@@ -80,26 +80,26 @@
     @Test
     public void testMatchTitle() {
         loader = new DatabaseResultLoader(mContext, "title");
-        assertThat(loader.loadInBackground().size()).isEqualTo(3);
-        verify(mSiteMapManager, times(3)).buildBreadCrumb(eq(mContext), anyString(), anyString());
+        assertThat(loader.loadInBackground().size()).isEqualTo(2);
+        verify(mSiteMapManager, times(2)).buildBreadCrumb(eq(mContext), anyString(), anyString());
     }
 
     @Test
     public void testMatchSummary() {
         loader = new DatabaseResultLoader(mContext, "summary");
-        assertThat(loader.loadInBackground().size()).isEqualTo(3);
+        assertThat(loader.loadInBackground().size()).isEqualTo(2);
     }
 
     @Test
     public void testMatchKeywords() {
         loader = new DatabaseResultLoader(mContext, "keywords");
-        assertThat(loader.loadInBackground().size()).isEqualTo(3);
+        assertThat(loader.loadInBackground().size()).isEqualTo(2);
     }
 
     @Test
     public void testMatchEntries() {
         loader = new DatabaseResultLoader(mContext, "entries");
-        assertThat(loader.loadInBackground().size()).isEqualTo(3);
+        assertThat(loader.loadInBackground().size()).isEqualTo(2);
     }
 
     @Test
@@ -167,7 +167,7 @@
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
         values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
-        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, true);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
         values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
         values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
@@ -196,7 +196,7 @@
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
         values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
-        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, true);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
         values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
         values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
@@ -223,7 +223,7 @@
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
         values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
-        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, true);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
         values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
         values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
@@ -249,7 +249,7 @@
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_PACKAGE, "");
         values.put(IndexDatabaseHelper.IndexColumns.INTENT_TARGET_CLASS, "");
         values.put(IndexDatabaseHelper.IndexColumns.ICON, "");
-        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, "");
+        values.put(IndexDatabaseHelper.IndexColumns.ENABLED, false);
         values.put(IndexDatabaseHelper.IndexColumns.DATA_KEY_REF, "gesture_double_tap_power");
         values.put(IndexDatabaseHelper.IndexColumns.USER_ID, 0);
         values.put(IndexDatabaseHelper.IndexColumns.PAYLOAD_TYPE, 0);
diff --git a/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java b/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java
index 7a0bb54..2d4ac54 100644
--- a/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java
@@ -17,6 +17,7 @@
 package com.android.settings.search2;
 
 import android.content.Context;
+import android.content.Loader;
 import android.os.Bundle;
 
 import com.android.internal.logging.nano.MetricsProto;
@@ -35,6 +36,9 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ActivityController;
 
+import java.util.List;
+
+import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -97,6 +101,28 @@
     }
 
     @Test
+    public void screenRotateEmptyString_ShouldNotCrash() {
+        final Bundle bundle = new Bundle();
+        ActivityController<SearchActivity> activityController =
+                Robolectric.buildActivity(SearchActivity.class);
+        activityController.setup();
+        SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
+                .findFragmentById(R.id.main_content);
+
+        fragment.mQuery = "";
+
+        activityController.saveInstanceState(bundle).pause().stop().destroy();
+
+        activityController = Robolectric.buildActivity(SearchActivity.class);
+        activityController.setup(bundle);
+
+        verify(mFeatureFactory.searchFeatureProvider)
+                .getDatabaseSearchLoader(any(Context.class), anyString());
+        verify(mFeatureFactory.searchFeatureProvider)
+                .getInstalledAppSearchLoader(any(Context.class), anyString());
+    }
+
+    @Test
     public void queryTextChange_shouldTriggerLoader() {
         final String testQuery = "test";
         ActivityController<SearchActivity> activityController =
diff --git a/tests/robotests/src/com/android/settings/webview/WebViewAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/webview/WebViewAppPreferenceControllerTest.java
index c16bd1f..e0a32a4 100644
--- a/tests/robotests/src/com/android/settings/webview/WebViewAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/webview/WebViewAppPreferenceControllerTest.java
@@ -70,7 +70,7 @@
         verify(controller, times(1)).updateState(any());
     }
 
-    @Test public void testOnActivityResultWithFailureDoesNothing() {
+    @Test public void testOnActivityResultWithFailure() {
         WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
 
         WebViewAppPreferenceController controller =
@@ -78,6 +78,6 @@
 
         controller.displayPreference(mPreferenceScreen); // Makes sure Preference is non-null
         controller.onActivityResult(Activity.RESULT_CANCELED, new Intent(DEFAULT_PACKAGE_NAME));
-        verify(controller, never()).updateState(any());
+        verify(controller, times(1)).updateState(any());
     }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/SavedNetworkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/SavedNetworkPreferenceControllerTest.java
deleted file mode 100644
index 657c21e..0000000
--- a/tests/robotests/src/com/android/settings/wifi/SavedNetworkPreferenceControllerTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2017 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.wifi;
-
-import android.content.Context;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager;
-
-import com.android.settings.SettingsRobolectricTestRunner;
-import com.android.settings.TestConfig;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-
-import java.util.List;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class SavedNetworkPreferenceControllerTest {
-
-    @Mock
-    private Context mContext;
-    @Mock
-    private WifiManager mWifiManager;
-
-    private SavedNetworkPreferenceController mController;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mController = new SavedNetworkPreferenceController(mContext, mWifiManager);
-    }
-
-    @Test
-    public void isAvailable_noSavedNetwork_shouldReturnFalse() {
-        when(mWifiManager.getConfiguredNetworks()).thenReturn(null);
-        assertThat(mController.isAvailable()).isFalse();
-    }
-
-    @Test
-    public void isAvailable_hasSavedNetwork_shouldReturnTrue() {
-        List<WifiConfiguration> configs = mock(List.class);
-        when(configs.isEmpty()).thenReturn(false);
-        when(mWifiManager.getConfiguredNetworks()).thenReturn(configs);
-
-        assertThat(mController.isAvailable()).isTrue();
-    }
-}
diff --git a/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java b/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java
new file mode 100644
index 0000000..231787e
--- /dev/null
+++ b/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 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.core;
+
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.preference.ListPreference;
+
+import com.android.settings.R;
+import com.android.settings.display.ThemePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ThemePreferenceControllerTest {
+
+    private UiModeManager mMockUiModeManager;
+    private ContextWrapper mContext;
+    private ThemePreferenceController mPreferenceController;
+
+    @Before
+    public void setup() {
+        mMockUiModeManager = mock(UiModeManager.class);
+        mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) {
+            @Override
+            public Object getSystemService(String name) {
+                if (Context.UI_MODE_SERVICE.equals(name)) {
+                    return mMockUiModeManager;
+                }
+                return super.getSystemService(name);
+            }
+        };
+        mPreferenceController = new ThemePreferenceController(mContext);
+    }
+
+    @Test
+    public void testUpdateState() {
+        when(mMockUiModeManager.getAvailableThemes()).thenReturn(new String[] {
+                null,
+                "Theme1",
+                "Theme2",
+        });
+        when(mMockUiModeManager.getTheme()).thenReturn("Theme1");
+        ListPreference pref = mock(ListPreference.class);
+        mPreferenceController.updateState(pref);
+        ArgumentCaptor<String[]> arg = ArgumentCaptor.forClass(String[].class);
+        verify(pref).setEntries(arg.capture());
+
+        String[] entries = arg.getValue();
+        assertEquals(3, entries.length);
+        assertNotNull(entries[0]);
+        assertEquals("Theme1", entries[1]);
+        assertEquals("Theme2", entries[2]);
+
+        verify(pref).setEntryValues(arg.capture());
+        String[] entryValues = arg.getValue();
+        assertEquals(3, entryValues.length);
+        assertNotNull(entryValues[0]);
+        assertEquals("Theme1", entryValues[1]);
+        assertEquals("Theme2", entryValues[2]);
+
+        verify(pref).setValue(eq("Theme1"));
+    }
+
+    @Test
+    public void testAvailable_false() {
+        when(mMockUiModeManager.getAvailableThemes()).thenReturn(new String[1]);
+        assertFalse(mPreferenceController.isAvailable());
+    }
+
+    @Test
+    public void testAvailable_true() {
+        when(mMockUiModeManager.getAvailableThemes()).thenReturn(new String[2]);
+        assertTrue(mPreferenceController.isAvailable());
+    }
+}