Merge "Check device for config_enableWallpaperService before calling service."
diff --git a/res/drawable/ic_event.xml b/res/drawable/ic_event.xml
new file mode 100644
index 0000000..a82d754
--- /dev/null
+++ b/res/drawable/ic_event.xml
@@ -0,0 +1,34 @@
+<!--
+Copyright (C) 2018 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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item>
+        <shape android:shape="oval">
+            <solid
+                android:color="?android:attr/colorAccent" />
+            <size
+                android:width="@dimen/dashboard_tile_image_size"
+                android:height="@dimen/dashboard_tile_image_size" />
+        </shape>
+    </item>
+
+    <item
+        android:width="@dimen/dashboard_tile_foreground_image_size"
+        android:height="@dimen/dashboard_tile_foreground_image_size"
+        android:start="@dimen/dashboard_tile_foreground_image_inset"
+        android:top="@dimen/dashboard_tile_foreground_image_inset"
+        android:drawable="@drawable/ic_event_white" />
+</layer-list>
diff --git a/res/drawable/ic_event_white.xml b/res/drawable/ic_event_white.xml
new file mode 100644
index 0000000..d80429b
--- /dev/null
+++ b/res/drawable/ic_event_white.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2018 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M17.0,12.0l-5.0,0.0l0.0,5.0l5.0,0.0l0.0,-5.0zM16.0,1.0l0.0,2.0L8.0,3.0L8.0,1.0L6.0,1.0l0.0,2.0L5.0,3.0c-1.11,0.0 -1.9,0.9 -1.99,2.0L3.0,19.0c0.0,1.0 0.89,2.0 2.0,2.0l14.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L21.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-1.0,0.0L18.0,1.0l-2.0,0.0zm3.0,18.0L5.0,19.0L5.0,8.0l14.0,0.0l0.0,11.0z"/>
+</vector>
diff --git a/res/drawable/ic_timelapse.xml b/res/drawable/ic_timelapse.xml
new file mode 100644
index 0000000..ba97bae
--- /dev/null
+++ b/res/drawable/ic_timelapse.xml
@@ -0,0 +1,34 @@
+<!--
+Copyright (C) 2018 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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item>
+        <shape android:shape="oval">
+            <solid
+                android:color="?android:attr/colorAccent" />
+            <size
+                android:width="@dimen/dashboard_tile_image_size"
+                android:height="@dimen/dashboard_tile_image_size" />
+        </shape>
+    </item>
+
+    <item
+        android:width="@dimen/dashboard_tile_foreground_image_size"
+        android:height="@dimen/dashboard_tile_foreground_image_size"
+        android:start="@dimen/dashboard_tile_foreground_image_inset"
+        android:top="@dimen/dashboard_tile_foreground_image_inset"
+        android:drawable="@drawable/ic_timelapse_white" />
+</layer-list>
diff --git a/res/drawable/ic_timelapse_white.xml b/res/drawable/ic_timelapse_white.xml
new file mode 100644
index 0000000..f287747
--- /dev/null
+++ b/res/drawable/ic_timelapse_white.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2018 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M16.24,7.76C15.07,6.59 13.54,6.0 12.0,6.0l0.0,6.0l-4.24,4.24c2.34,2.34 6.14,2.34 8.49,0.0 2.34,-2.34 2.34,-6.14 -0.01,-8.48zM12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zm0.0,18.0c-4.42,0.0 -8.0,-3.58 -8.0,-8.0s3.58,-8.0 8.0,-8.0 8.0,3.58 8.0,8.0 -3.58,8.0 -8.0,8.0z"/>
+</vector>
diff --git a/res/layout/zen_mode_settings_button.xml b/res/layout/zen_mode_settings_button.xml
index 4fe522d7..7db52a2 100644
--- a/res/layout/zen_mode_settings_button.xml
+++ b/res/layout/zen_mode_settings_button.xml
@@ -17,31 +17,25 @@
 
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
-    android:gravity="bottom"
-    android:paddingStart="72dp"
-    android:paddingEnd="72dp"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="wrap_content">
 
     <Button
         android:id="@+id/zen_mode_settings_turn_on_button"
         style="@style/ActionPrimaryButton"
-        android:layout_width="0dp"
-        android:layout_weight="1"
+        android:layout_marginStart="@dimen/screen_margin_sides"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="left"
-        android:text="@string/zen_mode_button_turn_on"
-        android:paddingEnd="8dp" />
+        android:text="@string/zen_mode_button_turn_on"/>
 
     <Button
         android:id="@+id/zen_mode_settings_turn_off_button"
         style="@style/ActionSecondaryButton"
-        android:layout_width="0dp"
-        android:layout_weight="1"
+        android:layout_marginStart="@dimen/screen_margin_sides"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="left"
-        android:text="@string/zen_mode_button_turn_off"
-        android:paddingEnd="8dp" />
+        android:text="@string/zen_mode_button_turn_off" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/zen_rule_type.xml b/res/layout/zen_rule_type.xml
index a6675c9..dea39ed 100644
--- a/res/layout/zen_rule_type.xml
+++ b/res/layout/zen_rule_type.xml
@@ -23,10 +23,9 @@
 
     <ImageView
             android:id="@+id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            />
+            android:layout_width="@dimen/app_icon_size"
+            android:layout_height="@dimen/app_icon_size"
+            android:layout_gravity="center"/>
 
     <RelativeLayout
             android:layout_width="wrap_content"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 98afa5a..fbc7490 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -22,7 +22,7 @@
     <dimen name="action_bar_switch_padding">16dip</dimen>
 
     <dimen name="app_icon_size">40dip</dimen>
-    <dimen name="secondary_app_icon_size">24dp</dimen>
+    <dimen name="secondary_app_icon_size">32dp</dimen>
     <dimen name="min_tap_target_size">48dp</dimen>
     <dimen name="screen_margin_sides">64dip</dimen>
     <dimen name="screen_margin_top">72dip</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4b2c673..7abcc97 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2259,9 +2259,9 @@
     <!-- Wireless networks, item title to go into the WFC settings [CHAR LIMIT=30] -->
     <string name="wifi_calling_settings_title">Wi-Fi calling</string>
     <!-- Title of suggestion to turn on wifi calling [CHAR LIMIT=30] -->
-    <string name="wifi_calling_suggestion_title">Turn on Wi-Fi Calling</string>
+    <string name="wifi_calling_suggestion_title">Extend call coverage with Wi\u2011Fi</string>
     <!-- Summary of suggestion to turn on wifi calling [CHAR LIMIT=60] -->
-    <string name="wifi_calling_suggestion_summary">Extend coverage by calling over Wi-Fi</string>
+    <string name="wifi_calling_suggestion_summary">Turn on Wi\u2011Fi calling</string>
     <!-- Title of WFC preference item [CHAR LIMIT=30] -->
     <string name="wifi_calling_mode_title">Calling preference</string>
     <!-- Title of WFC preference selection dialog [CHAR LIMIT=30] -->
@@ -4431,6 +4431,12 @@
     <string name="accessibility_autoclick_preference_title">Click after pointer stops moving</string>
     <!-- Title for accessibility preference for configuring amount of time that has to pass after pointer stops moving before click action can be performed (if automatic click after pointer stops moving feature is enabled). [CHAR LIMIT=NONE] -->
     <string name="accessibility_autoclick_delay_preference_title">Delay before click</string>
+    <!-- Title for accessibility preference screen for configuring vibrations. -->
+    <string name="accessibility_vibration_settings_title">Vibration</string>
+    <!-- Title for accessibility preference for configuring notification vibrations. -->
+    <string name="accessibility_notification_vibration_title">Ring &amp; notification vibration</string>
+    <!-- Title for accessibility preference for configuring touch feedback vibrations. -->
+    <string name="accessibility_touch_vibration_title">Touch vibration</string>
     <!-- Used in the acessibilty service settings to control turning on/off the service entirely -->
     <string name="accessibility_service_master_switch_title">Use service</string>
     <!-- Used in the Color correction settings screen to control turning on/off the feature entirely -->
@@ -4474,6 +4480,30 @@
         <item quantity="other">Very long delay (<xliff:g id="click_delay_label" example="200">%1$d</xliff:g> ms)</item>
     </plurals>
 
+    <!-- Summary for vibration settings preference when ring & notification are set to off-->
+    <string name="accessibility_vibration_summary_off">Ring &amp; notification set to off</string>
+
+    <!-- Summary for vibration settings preference when ring & notification are set to low-->
+    <string name="accessibility_vibration_summary_low">Ring &amp; notification set to low</string>
+
+    <!-- Summary for vibration settings preference when ring & notification are set to medium-->
+    <string name="accessibility_vibration_summary_medium">Ring &amp; notification set to medium</string>
+
+    <!-- Summary for vibration settings preference when ring & notification are set to high-->
+    <string name="accessibility_vibration_summary_high">Ring &amp; notification set to high</string>
+
+    <!-- Label describing an option turning vibrations off. [CHAR LIMIT=15] -->
+    <string name="accessibility_vibration_intensity_off">Off</string>
+
+    <!-- Label describing a low intensity vibration option. [CHAR LIMIT=15] -->
+    <string name="accessibility_vibration_intensity_low">Low</string>
+
+    <!-- Label describing a medium intensity vibration option. [CHAR LIMIT=15] -->
+    <string name="accessibility_vibration_intensity_medium">Medium</string>
+
+    <!-- Label describing a high intensity vibration option. [CHAR LIMIT=15] -->
+    <string name="accessibility_vibration_intensity_high">High</string>
+
     <!-- Title for accessibility menu item to lauch a settings activity. [CHAR LIMIT=15] -->
     <string name="accessibility_menu_item_settings">Settings</string>
 
@@ -4842,6 +4872,41 @@
     <string name="battery_tip_dialog_message" product="tablet">Your tablet was used heavily and this consumed a lot of battery. Your battery is behaving normally.\n\n Your tablet was used for about <xliff:g id="hour">%1$s</xliff:g> since last full charge.\n\n Total usage:</string>
     <!-- Message for battery tip dialog to show the status about the battery [CHAR LIMIT=NONE] -->
     <string name="battery_tip_dialog_message" product="device">Your device was used heavily and this consumed a lot of battery. Your battery is behaving normally.\n\n Your device was used for about <xliff:g id="hour">%1$s</xliff:g> since last full charge.\n\n Total usage:</string>
+    <!-- Title for restricted app preference, showing how many app need to be restricted [CHAR LIMIT=NONE] -->
+    <plurals name="battery_tip_restrict_title">
+        <item quantity="one">Restrict %1$d app</item>
+        <item quantity="other">Restrict %1$d apps</item>
+    </plurals>
+    <!-- Title for restricted app preference, showing how many app been restricted [CHAR LIMIT=NONE] -->
+    <plurals name="battery_tip_restrict_handled_title">
+        <item quantity="one">%1$d recently restricted</item>
+        <item quantity="other">%1$d apps recently restricted</item>
+    </plurals>
+    <!-- Summary for restricted app preference, showing the impact of the apps [CHAR LIMIT=NONE] -->
+    <plurals name="battery_tip_restrict_summary">
+        <item quantity="one">%1$s has high battery usage</item>
+        <item quantity="other">%2$d apps have high battery usage</item>
+    </plurals>
+    <!-- Summary for restricted app preference, showing the impact of the apps [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_restrict_handled_summary">App changes are in progress</string>
+
+    <!-- Title for dialog to restrict the app [CHAR LIMIT=NONE] -->
+    <plurals name="battery_tip_restrict_app_dialog_title">
+        <item quantity="one">Restrict app?</item>
+        <item quantity="other">Restrict %1$d apps?</item>
+    </plurals>
+    <!-- Message for battery tip dialog to show the restrict app list [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_restrict_app_dialog_message">To save battery, you can stop this app from running in the background when it’s not being used.</string>
+    <!-- OK button for battery tip dialog to show the restrict app list [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_restrict_app_dialog_ok">Restrict</string>
+    <!-- Title for dialog to remove restriction for the app [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_unrestrict_app_dialog_title">Remove restriction for <xliff:g id="app">%1$s</xliff:g>?</string>
+    <!-- Message for  dialog to show the impact if remove restriction for app [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_unrestrict_app_dialog_message">This app will be able to use battery in the background. This may cause your battery to be used up faster.</string>
+    <!-- OK button for dialog to remove restriction for app [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_unrestrict_app_dialog_ok">Remove</string>
+    <!-- CANCEL button for dialog to remove restriction for app [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_unrestrict_app_dialog_cancel">Not now</string>
 
     <!-- Title for the smart battery manager preference [CHAR LIMIT=NONE] -->
     <string name="smart_battery_manager_title">Smart battery manager</string>
@@ -6937,6 +7002,21 @@
     <!-- [CHAR LIMIT=20] Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description.  -->
     <string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string>
 
+    <!-- Sound settings screen, summary format of do not disturb when on. [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_sound_summary_on">On / <xliff:g name="dnd_summary" example="No sound except alarms and media">%1$s</xliff:g></string>
+
+    <!-- Sound settings screen, summary format of do not disturb when off with extra information. [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_sound_summary_off_with_info">Off / <xliff:g name="dnd_summary" example="1 rule can turn on automatically">%1$s</xliff:g></string>
+
+    <!-- Sound settings screen, summary format of do not disturb when off with no extra information. [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_sound_summary_off">Off</string>
+
+    <!-- Summary for the Sound Do not Disturb option when at least one automatic rules is enabled. [CHAR LIMIT=NONE]-->
+    <plurals name="zen_mode_sound_summary_summary_off_info">
+        <item quantity="one">1 rule can turn on automatically</item>
+        <item quantity="other"><xliff:g id="on_count" example="3">%d</xliff:g> rules can turn on automatically</item>
+    </plurals>
+
     <!-- Work Sounds: Work sound settings section header.  [CHAR LIMIT=50] -->
     <string name="sound_work_settings">Work profile sounds</string>
 
@@ -7071,10 +7151,10 @@
      summary on the channel page-->
 
     <!-- [CHAR LIMIT=100] Notification Importance: min importance level description -->
-    <string name="notification_importance_min">No sound or visual interruption</string>
+    <string name="notification_importance_min">Show silently and minimize</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance: low importance level description -->
-    <string name="notification_importance_low">No sound</string>
+    <string name="notification_importance_low">Show silently</string>
 
     <!-- [CHAR LIMIT=100] Notification Importance: normal importance level description -->
     <string name="notification_importance_default">Make sound</string>
diff --git a/res/xml/accessibility_notification_vibration_settings.xml b/res/xml/accessibility_notification_vibration_settings.xml
new file mode 100644
index 0000000..b37d363
--- /dev/null
+++ b/res/xml/accessibility_notification_vibration_settings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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/accessibility_notification_vibration_title" />
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
index b5da848..060868e 100644
--- a/res/xml/accessibility_settings.xml
+++ b/res/xml/accessibility_settings.xml
@@ -93,6 +93,12 @@
                 android:entries="@array/long_press_timeout_selector_titles"
                 android:entryValues="@array/long_press_timeout_selector_values"
                 android:persistent="false"/>
+
+        <Preference
+            android:fragment="com.android.settings.accessibility.VibrationSettings"
+            android:key="vibration_preference_screen"
+            android:title="@string/accessibility_vibration_settings_title" />
+
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/xml/accessibility_touch_vibration_settings.xml b/res/xml/accessibility_touch_vibration_settings.xml
new file mode 100644
index 0000000..ed8f550
--- /dev/null
+++ b/res/xml/accessibility_touch_vibration_settings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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/accessibility_touch_vibration_title" />
diff --git a/res/xml/accessibility_vibration_settings.xml b/res/xml/accessibility_vibration_settings.xml
new file mode 100644
index 0000000..d61454d
--- /dev/null
+++ b/res/xml/accessibility_vibration_settings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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:key="accessibility_settings_screen"
+        android:title="@string/accessibility_vibration_settings_title"
+        android:persistent="true">
+
+    <Preference
+        android:fragment="com.android.settings.accessibility.NotificationVibrationPreferenceFragment"
+        android:key="notification_vibration_preference_screen"
+        android:title="@string/accessibility_notification_vibration_title" />
+
+    <Preference
+        android:fragment="com.android.settings.accessibility.TouchVibrationPreferenceFragment"
+        android:key="touch_vibration_preference_screen"
+        android:title="@string/accessibility_touch_vibration_title" />
+</PreferenceScreen>
diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml
index 267fce9..c86df68 100644
--- a/res/xml/location_settings.xml
+++ b/res/xml/location_settings.xml
@@ -49,5 +49,9 @@
 
         <PreferenceCategory
             android:key="location_services"
-            android:title="@string/location_category_location_services" />
+            android:title="@string/location_category_location_services"/>
+
+        <PreferenceCategory
+            android:key="location_footer"
+            settings:allowDividerAbove="false"/>
 </PreferenceScreen>
diff --git a/src/com/android/settings/CryptKeeperConfirm.java b/src/com/android/settings/CryptKeeperConfirm.java
index d61fd98..2271200 100644
--- a/src/com/android/settings/CryptKeeperConfirm.java
+++ b/src/com/android/settings/CryptKeeperConfirm.java
@@ -16,6 +16,7 @@
 
 package com.android.settings;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.StatusBarManager;
 import android.content.Context;
@@ -35,11 +36,11 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 
 import java.util.Locale;
 
-public class CryptKeeperConfirm extends InstrumentedPreferenceFragment {
+public class CryptKeeperConfirm extends InstrumentedFragment {
 
     private static final String TAG = "CryptKeeperConfirm";
 
@@ -154,6 +155,12 @@
     }
 
     @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getActivity().setTitle(R.string.crypt_keeper_confirm_title);
+    }
+
+    @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         mContentView = inflater.inflate(R.layout.crypt_keeper_confirm, null);
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 47aa8a6..4f5c6b9 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -55,7 +55,7 @@
 import android.widget.TextView;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settings.password.ConfirmLockPattern;
 import com.android.settingslib.RestrictedLockUtils;
@@ -72,7 +72,7 @@
  *
  * This is the initial screen.
  */
-public class MasterClear extends InstrumentedPreferenceFragment {
+public class MasterClear extends InstrumentedFragment {
     private static final String TAG = "MasterClear";
 
     @VisibleForTesting static final int KEYGUARD_REQUEST = 55;
diff --git a/src/com/android/settings/MasterClearConfirm.java b/src/com/android/settings/MasterClearConfirm.java
index 9b324c3..59736fd 100644
--- a/src/com/android/settings/MasterClearConfirm.java
+++ b/src/com/android/settings/MasterClearConfirm.java
@@ -33,7 +33,7 @@
 import android.widget.TextView;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settingslib.RestrictedLockUtils;
 
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -48,7 +48,7 @@
  *
  * This is the confirmation screen.
  */
-public class MasterClearConfirm extends InstrumentedPreferenceFragment {
+public class MasterClearConfirm extends InstrumentedFragment {
 
     private View mContentView;
     private boolean mEraseSdCard;
diff --git a/src/com/android/settings/ProxySelector.java b/src/com/android/settings/ProxySelector.java
index a72525c..79767fb 100644
--- a/src/com/android/settings/ProxySelector.java
+++ b/src/com/android/settings/ProxySelector.java
@@ -41,9 +41,9 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 
-public class ProxySelector extends InstrumentedPreferenceFragment implements DialogCreatable {
+public class ProxySelector extends InstrumentedFragment implements DialogCreatable {
     private static final String TAG = "ProxySelector";
 
     EditText    mHostnameField;
@@ -59,11 +59,6 @@
     private View mView;
 
     @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-    }
-
-    @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         mView = inflater.inflate(R.layout.proxy, container, false);
@@ -179,6 +174,8 @@
         String title = intent.getStringExtra("title");
         if (!TextUtils.isEmpty(title)) {
             activity.setTitle(title);
+        } else {
+            activity.setTitle(R.string.proxy_settings_title);
         }
     }
 
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index f64f6dc..5cbee63 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -43,7 +43,7 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.password.ChooseLockSettingsHelper;
 import com.android.settings.password.ConfirmLockPattern;
 import com.android.settingslib.RestrictedLockUtils;
@@ -61,7 +61,7 @@
  *
  * This is the initial screen.
  */
-public class ResetNetwork extends InstrumentedPreferenceFragment {
+public class ResetNetwork extends InstrumentedFragment {
     private static final String TAG = "ResetNetwork";
 
     // Arbitrary to avoid conficts
diff --git a/src/com/android/settings/ResetNetworkConfirm.java b/src/com/android/settings/ResetNetworkConfirm.java
index bc0fa77..78e8339 100644
--- a/src/com/android/settings/ResetNetworkConfirm.java
+++ b/src/com/android/settings/ResetNetworkConfirm.java
@@ -42,8 +42,8 @@
 import com.android.ims.ImsManager;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.settings.core.InstrumentedPreferenceFragment;
 import com.android.settings.wrapper.RecoverySystemWrapper;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settingslib.RestrictedLockUtils;
 
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -58,7 +58,7 @@
  *
  * This is the confirmation screen.
  */
-public class ResetNetworkConfirm extends InstrumentedPreferenceFragment {
+public class ResetNetworkConfirm extends InstrumentedFragment {
 
     private View mContentView;
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
diff --git a/src/com/android/settings/TrustedCredentialsSettings.java b/src/com/android/settings/TrustedCredentialsSettings.java
index 491419a..86340be 100644
--- a/src/com/android/settings/TrustedCredentialsSettings.java
+++ b/src/com/android/settings/TrustedCredentialsSettings.java
@@ -65,7 +65,7 @@
 import com.android.internal.app.UnlaunchableAppActivity;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
@@ -75,7 +75,7 @@
 import java.util.Set;
 import java.util.function.IntConsumer;
 
-public class TrustedCredentialsSettings extends InstrumentedPreferenceFragment
+public class TrustedCredentialsSettings extends InstrumentedFragment
         implements TrustedCredentialsDialogBuilder.DelegateInterface {
 
     public static final String ARG_SHOW_NEW_FOR_USER = "ARG_SHOW_NEW_FOR_USER";
@@ -117,7 +117,8 @@
         private final int mContentView;
         private final boolean mSwitch;
 
-        private Tab(String tag, int label, int view, int progress, int contentView, boolean withSwitch) {
+        private Tab(String tag, int label, int view, int progress, int contentView,
+                boolean withSwitch) {
             mTag = tag;
             mLabel = label;
             mView = view;
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index df8d4c8..7335bae 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.accessibility;
 
+import static android.os.Vibrator.VibrationIntensity;
+
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -28,6 +30,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
+import android.os.Vibrator;
 import android.provider.SearchIndexableResource;
 import android.provider.Settings;
 import android.support.annotation.VisibleForTesting;
@@ -111,6 +114,8 @@
             "tts_settings_preference";
     private static final String AUTOCLICK_PREFERENCE_SCREEN =
             "autoclick_preference_screen";
+    private static final String VIBRATION_PREFERENCE_SCREEN =
+            "vibration_preference_screen";
 
     @VisibleForTesting static final String TOGGLE_INVERSION_PREFERENCE =
             "toggle_inversion_preference";
@@ -215,6 +220,7 @@
     private Preference mAutoclickPreferenceScreen;
     private Preference mAccessibilityShortcutPreferenceScreen;
     private Preference mDisplayDaltonizerPreferenceScreen;
+    private Preference mVibrationPreferenceScreen;
     private SwitchPreference mToggleInversionPreference;
 
     private int mLongPressTimeoutDefault;
@@ -452,9 +458,11 @@
         // Display color adjustments.
         mDisplayDaltonizerPreferenceScreen = findPreference(DISPLAY_DALTONIZER_PREFERENCE_SCREEN);
 
-        // Accessibility shortcut
+        // Accessibility shortcut.
         mAccessibilityShortcutPreferenceScreen = findPreference(ACCESSIBILITY_SHORTCUT_PREFERENCE);
 
+        // Vibrations.
+        mVibrationPreferenceScreen = findPreference(VIBRATION_PREFERENCE_SCREEN);
     }
 
     private void updateAllPreferences() {
@@ -661,6 +669,8 @@
         mSelectLongPressTimeoutPreference.setValue(value);
         mSelectLongPressTimeoutPreference.setSummary(mLongPressTimeoutValueToTitleMap.get(value));
 
+        updateVibrationSummary(mVibrationPreferenceScreen);
+
         updateFeatureSummary(Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED,
                 mCaptioningPreferenceScreen);
         updateFeatureSummary(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
@@ -726,6 +736,29 @@
         pref.setSummary(entries[index]);
     }
 
+    private void updateVibrationSummary(Preference pref) {
+        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
+        final int intensity = Settings.System.getInt(getContext().getContentResolver(),
+                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                vibrator.getDefaultNotificationVibrationIntensity());
+        mVibrationPreferenceScreen.setSummary(getVibrationSummary(getContext(), intensity));
+    }
+
+    private String getVibrationSummary(Context context, @VibrationIntensity int intensity) {
+        switch (intensity) {
+            case Vibrator.VIBRATION_INTENSITY_OFF:
+                return context.getString(R.string.accessibility_vibration_summary_off);
+            case Vibrator.VIBRATION_INTENSITY_LOW:
+                return context.getString(R.string.accessibility_vibration_summary_low);
+            case Vibrator.VIBRATION_INTENSITY_MEDIUM:
+                return context.getString(R.string.accessibility_vibration_summary_medium);
+            case Vibrator.VIBRATION_INTENSITY_HIGH:
+                return context.getString(R.string.accessibility_vibration_summary_high);
+            default:
+                return "";
+        }
+    }
+
     private void updateLockScreenRotationCheckbox() {
         Context context = getActivity();
         if (context != null) {
diff --git a/src/com/android/settings/accessibility/NotificationVibrationPreferenceFragment.java b/src/com/android/settings/accessibility/NotificationVibrationPreferenceFragment.java
new file mode 100644
index 0000000..6340bb1
--- /dev/null
+++ b/src/com/android/settings/accessibility/NotificationVibrationPreferenceFragment.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import android.os.Vibrator;
+import android.provider.Settings;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+
+/**
+ * Fragment for picking accessibility shortcut service
+ */
+public class NotificationVibrationPreferenceFragment extends VibrationPreferenceFragment {
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.ACCESSIBILITY_VIBRATION_NOTIFICATION;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.accessibility_notification_vibration_settings;
+    }
+
+    /**
+     * Get the setting string of the vibration intensity setting this preference is dealing with.
+     */
+    @Override
+    protected String getVibrationIntensitySetting() {
+        return Settings.System.NOTIFICATION_VIBRATION_INTENSITY;
+    }
+
+    @Override
+    protected int getDefaultVibrationIntensity() {
+        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
+        return vibrator.getDefaultNotificationVibrationIntensity();
+    }
+}
diff --git a/src/com/android/settings/accessibility/TouchVibrationPreferenceFragment.java b/src/com/android/settings/accessibility/TouchVibrationPreferenceFragment.java
new file mode 100644
index 0000000..ea36833
--- /dev/null
+++ b/src/com/android/settings/accessibility/TouchVibrationPreferenceFragment.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import android.graphics.drawable.Drawable;
+import android.os.Vibrator;
+import android.provider.Settings;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+
+/**
+ * Fragment for picking accessibility shortcut service
+ */
+public class TouchVibrationPreferenceFragment extends VibrationPreferenceFragment {
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.ACCESSIBILITY_VIBRATION_TOUCH;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.accessibility_touch_vibration_settings;
+    }
+
+    /**
+     * Get the setting string of the vibration intensity setting this preference is dealing with.
+     */
+    @Override
+    protected String getVibrationIntensitySetting() {
+        return Settings.System.HAPTIC_FEEDBACK_INTENSITY;
+    }
+
+    @Override
+    protected int getDefaultVibrationIntensity() {
+        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
+        return vibrator.getDefaultHapticFeedbackIntensity();
+    }
+
+    @Override
+    public void onVibrationIntensitySelected(int intensity) {
+        // We want to keep HAPTIC_FEEDBACK_ENABLED consistent with this setting since some
+        // applications check it directly before triggering their own haptic feedback.
+        final boolean hapticFeedbackEnabled = !(intensity == Vibrator.VIBRATION_INTENSITY_OFF);
+        Settings.System.putInt(getContext().getContentResolver(),
+                Settings.System.HAPTIC_FEEDBACK_ENABLED, hapticFeedbackEnabled ? 1 : 0);
+    }
+}
diff --git a/src/com/android/settings/accessibility/VibrationPreferenceFragment.java b/src/com/android/settings/accessibility/VibrationPreferenceFragment.java
new file mode 100644
index 0000000..f81208f
--- /dev/null
+++ b/src/com/android/settings/accessibility/VibrationPreferenceFragment.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import static android.os.Vibrator.VibrationIntensity;
+
+import android.support.annotation.VisibleForTesting;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.widget.RadioButtonPickerFragment;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Fragment for changing vibration settings.
+ */
+public abstract class VibrationPreferenceFragment extends RadioButtonPickerFragment {
+    private static final String TAG = "VibrationPreferenceFragment";
+
+    @VisibleForTesting
+    final static String KEY_INTENSITY_OFF = "intensity_off";
+    @VisibleForTesting
+    final static String KEY_INTENSITY_LOW = "intensity_low";
+    @VisibleForTesting
+    final static String KEY_INTENSITY_MEDIUM = "intensity_medium";
+    @VisibleForTesting
+    final static String KEY_INTENSITY_HIGH = "intensity_high";
+
+    private final Map<String, VibrationIntensityCandidateInfo> mCandidates;
+    private final SettingsObserver mSettingsObserver;
+
+    public VibrationPreferenceFragment() {
+        mCandidates = new ArrayMap<>();
+        mCandidates.put(KEY_INTENSITY_OFF,
+                new VibrationIntensityCandidateInfo(KEY_INTENSITY_OFF,
+                    R.string.accessibility_vibration_intensity_off,
+                    Vibrator.VIBRATION_INTENSITY_OFF));
+        mCandidates.put(KEY_INTENSITY_LOW,
+                new VibrationIntensityCandidateInfo(KEY_INTENSITY_LOW,
+                    R.string.accessibility_vibration_intensity_low,
+                    Vibrator.VIBRATION_INTENSITY_LOW));
+        mCandidates.put(KEY_INTENSITY_MEDIUM,
+                new VibrationIntensityCandidateInfo(KEY_INTENSITY_MEDIUM,
+                    R.string.accessibility_vibration_intensity_medium,
+                    Vibrator.VIBRATION_INTENSITY_MEDIUM));
+        mCandidates.put(KEY_INTENSITY_HIGH,
+                new VibrationIntensityCandidateInfo(KEY_INTENSITY_HIGH,
+                    R.string.accessibility_vibration_intensity_high,
+                    Vibrator.VIBRATION_INTENSITY_HIGH));
+        mSettingsObserver = new SettingsObserver();
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mSettingsObserver.register();
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        mSettingsObserver.unregister();
+    }
+
+    /**
+     * Get the setting string of the vibration intensity setting this preference is dealing with.
+     */
+    protected abstract String getVibrationIntensitySetting();
+
+    /**
+     * Get the default intensity for the desired setting.
+     */
+    protected abstract int getDefaultVibrationIntensity();
+
+    /**
+     * When a new vibration intensity is selected by the user.
+     */
+    protected void onVibrationIntensitySelected(int intensity) { }
+
+    @Override
+    protected List<? extends CandidateInfo> getCandidates() {
+        List<VibrationIntensityCandidateInfo> candidates = new ArrayList<>(mCandidates.values());
+        candidates.sort(
+                Comparator.comparing(VibrationIntensityCandidateInfo::getIntensity).reversed());
+        return candidates;
+    }
+
+    @Override
+    protected String getDefaultKey() {
+        final int vibrationIntensity = Settings.System.getInt(getContext().getContentResolver(),
+                getVibrationIntensitySetting(), getDefaultVibrationIntensity());
+        for (VibrationIntensityCandidateInfo candidate : mCandidates.values()) {
+            if (candidate.getIntensity() == vibrationIntensity) {
+                return candidate.getKey();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected boolean setDefaultKey(String key) {
+        VibrationIntensityCandidateInfo candidate = mCandidates.get(key);
+        if (candidate == null) {
+            Log.e(TAG, "Tried to set unknown intensity (key=" + key + ")!");
+            return false;
+        }
+        Settings.System.putInt(getContext().getContentResolver(),
+                getVibrationIntensitySetting(), candidate.getIntensity());
+        onVibrationIntensitySelected(candidate.getIntensity());
+        return true;
+    }
+
+    @VisibleForTesting
+    class VibrationIntensityCandidateInfo extends CandidateInfo {
+        private String mKey;
+        private int mLabelId;
+        @VibrationIntensity
+        private int mIntensity;
+
+        public VibrationIntensityCandidateInfo(String key, int labelId, int intensity) {
+            super(true /* enabled */);
+            mKey = key;
+            mLabelId = labelId;
+            mIntensity = intensity;
+        }
+
+        @Override
+        public CharSequence loadLabel() {
+            return getContext().getString(mLabelId);
+        }
+
+        @Override
+        public Drawable loadIcon() {
+            return null;
+        }
+
+        @Override
+        public String getKey() {
+            return mKey;
+        }
+
+        public int getIntensity() {
+            return mIntensity;
+        }
+    }
+
+    private class SettingsObserver extends ContentObserver {
+        public SettingsObserver() {
+            super(new Handler());
+        }
+
+        public void register() {
+            getContext().getContentResolver().registerContentObserver(
+                    Settings.System.getUriFor(getVibrationIntensitySetting()), false, this);
+        }
+
+        public void unregister() {
+            getContext().getContentResolver().unregisterContentObserver(this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            updateCandidates();
+        }
+    }
+}
diff --git a/src/com/android/settings/accessibility/VibrationSettings.java b/src/com/android/settings/accessibility/VibrationSettings.java
new file mode 100644
index 0000000..8aa4513
--- /dev/null
+++ b/src/com/android/settings/accessibility/VibrationSettings.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Vibrator;
+import android.provider.SearchIndexableResource;
+import android.provider.Settings;
+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.SettingsPreferenceFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Activity with the accessibility settings.
+ */
+public class VibrationSettings extends SettingsPreferenceFragment implements Indexable {
+
+    // Preferences
+    @VisibleForTesting
+    static final String NOTIFICATION_VIBRATION_PREFERENCE_SCREEN =
+            "notification_vibration_preference_screen";
+    @VisibleForTesting
+    static final String TOUCH_VIBRATION_PREFERENCE_SCREEN =
+            "touch_vibration_preference_screen";
+
+    private final Handler mHandler = new Handler();
+    private final SettingsContentObserver mSettingsContentObserver;
+
+    private Preference mNotificationVibrationPreferenceScreen;
+    private Preference mTouchVibrationPreferenceScreen;
+
+    public VibrationSettings() {
+        List<String> vibrationSettings = new ArrayList<>();
+        vibrationSettings.add(Settings.System.HAPTIC_FEEDBACK_INTENSITY);
+        vibrationSettings.add(Settings.System.NOTIFICATION_VIBRATION_INTENSITY);
+        mSettingsContentObserver = new SettingsContentObserver(mHandler, vibrationSettings) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                updatePreferences();
+            }
+        };
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.ACCESSIBILITY_VIBRATION;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        addPreferencesFromResource(R.xml.accessibility_vibration_settings);
+        initializePreferences();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        updatePreferences();
+        mSettingsContentObserver.register(getContentResolver());
+    }
+
+    @Override
+    public void onPause() {
+        mSettingsContentObserver.unregister(getContentResolver());
+        super.onPause();
+    }
+
+    private void initializePreferences() {
+        // Notification and notification vibration strength adjustments.
+        mNotificationVibrationPreferenceScreen =
+                findPreference(NOTIFICATION_VIBRATION_PREFERENCE_SCREEN);
+
+        // Touch feedback strength adjustments.
+        mTouchVibrationPreferenceScreen = findPreference(TOUCH_VIBRATION_PREFERENCE_SCREEN);
+    }
+
+    private void updatePreferences() {
+        updateNotificationVibrationSummary(mNotificationVibrationPreferenceScreen);
+        updateTouchVibrationSummary(mTouchVibrationPreferenceScreen);
+    }
+
+    private void updateNotificationVibrationSummary(Preference pref) {
+        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
+        final int intensity = Settings.System.getInt(getContext().getContentResolver(),
+                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                vibrator.getDefaultNotificationVibrationIntensity());
+        CharSequence summary = getVibrationIntensitySummary(getContext(), intensity);
+        mNotificationVibrationPreferenceScreen.setSummary(summary);
+    }
+
+    private void updateTouchVibrationSummary(Preference pref) {
+        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
+        final int intensity = Settings.System.getInt(getContext().getContentResolver(),
+                Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                vibrator.getDefaultHapticFeedbackIntensity());
+        CharSequence summary = getVibrationIntensitySummary(getContext(), intensity);
+        mTouchVibrationPreferenceScreen.setSummary(summary);
+    }
+
+    public static String getVibrationIntensitySummary(Context context, int intensity) {
+        switch (intensity) {
+            case Vibrator.VIBRATION_INTENSITY_OFF:
+                return context.getString(R.string.accessibility_vibration_intensity_off);
+            case Vibrator.VIBRATION_INTENSITY_LOW:
+                return context.getString(R.string.accessibility_vibration_intensity_low);
+            case Vibrator.VIBRATION_INTENSITY_MEDIUM:
+                return context.getString(R.string.accessibility_vibration_intensity_medium);
+            case Vibrator.VIBRATION_INTENSITY_HIGH:
+                return context.getString(R.string.accessibility_vibration_intensity_high);
+            default:
+                return "";
+        }
+    }
+
+    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                        boolean enabled) {
+                    List<SearchIndexableResource> indexables = new ArrayList<>();
+                    SearchIndexableResource indexable = new SearchIndexableResource(context);
+                    indexable.xmlResId = R.xml.accessibility_vibration_settings;
+                    indexables.add(indexable);
+                    return indexables;
+                }
+            };
+}
diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java
index 7e73a9b..770b1d6 100644
--- a/src/com/android/settings/applications/RunningServiceDetails.java
+++ b/src/com/android/settings/applications/RunningServiceDetails.java
@@ -34,7 +34,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.Utils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settingslib.utils.ThreadUtils;
 
@@ -45,7 +45,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 
-public class RunningServiceDetails extends InstrumentedPreferenceFragment
+public class RunningServiceDetails extends InstrumentedFragment
         implements RunningState.OnRefreshUiListener {
     static final String TAG = "RunningServicesDetails";
 
diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java
index 4f045a2..690c795 100644
--- a/src/com/android/settings/dashboard/DashboardSummary.java
+++ b/src/com/android/settings/dashboard/DashboardSummary.java
@@ -268,6 +268,7 @@
         mSummaryLoader.updateSummaryToCache(category);
         mStagingCategory = category;
         if (mSuggestionControllerMixin == null) {
+            mAdapter.setCategory(mStagingCategory);
             return;
         }
         if (mSuggestionControllerMixin.isSuggestionLoaded()) {
diff --git a/src/com/android/settings/fingerprint/FingerprintAuthenticateSidecar.java b/src/com/android/settings/fingerprint/FingerprintAuthenticateSidecar.java
index d649c0b..1fa59a2 100644
--- a/src/com/android/settings/fingerprint/FingerprintAuthenticateSidecar.java
+++ b/src/com/android/settings/fingerprint/FingerprintAuthenticateSidecar.java
@@ -21,12 +21,12 @@
 import android.os.CancellationSignal;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 
 /**
  * Sidecar fragment to handle the state around fingerprint authentication
  */
-public class FingerprintAuthenticateSidecar extends InstrumentedPreferenceFragment {
+public class FingerprintAuthenticateSidecar extends InstrumentedFragment {
 
     private static final String TAG = "FingerprintAuthenticateSidecar";
 
diff --git a/src/com/android/settings/fingerprint/FingerprintRemoveSidecar.java b/src/com/android/settings/fingerprint/FingerprintRemoveSidecar.java
index 462d09e..7caca3f 100644
--- a/src/com/android/settings/fingerprint/FingerprintRemoveSidecar.java
+++ b/src/com/android/settings/fingerprint/FingerprintRemoveSidecar.java
@@ -21,7 +21,7 @@
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import android.os.UserHandle;
 import java.util.Queue;
@@ -31,7 +31,7 @@
 /**
  * Sidecar fragment to handle the state around fingerprint removal.
  */
-public class FingerprintRemoveSidecar extends InstrumentedPreferenceFragment {
+public class FingerprintRemoveSidecar extends InstrumentedFragment {
 
     private static final String TAG = "FingerprintRemoveSidecar";
     private Listener mListener;
@@ -99,20 +99,6 @@
         setRetainInstance(true);
     }
 
-    @Override
-    public void onAttach(Context context) {
-        super.onAttach(context);
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-    }
-
-    @Override
-    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
-    }
-
     public void setListener(Listener listener) {
         if (mListener == null && listener != null) {
             while (!mFingerprintsRemoved.isEmpty()) {
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index de027a3..e073456 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -46,6 +46,8 @@
 import com.android.settings.applications.LayoutPreference;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.wrapper.DevicePolicyManagerWrapper;
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment;
@@ -69,7 +71,7 @@
         ButtonActionDialogFragment.AppButtonsDialogListener,
         AnomalyDialogFragment.AnomalyDialogListener,
         LoaderManager.LoaderCallbacks<List<Anomaly>>,
-        BackgroundActivityPreferenceController.WarningConfirmationListener {
+        BatteryTipPreferenceController.BatteryTipListener {
 
     public static final String TAG = "AdvancedPowerUsageDetail";
     public static final String EXTRA_UID = "extra_uid";
@@ -373,8 +375,8 @@
     }
 
     @Override
-    public void onLimitBackgroundActivity() {
-        mBackgroundActivityPreferenceController.setRestricted(
+    public void onBatteryTipHandled(BatteryTip batteryTip) {
+        mBackgroundActivityPreferenceController.updateSummary(
                 findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
     }
 }
diff --git a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
index 01e4182..21bd4b7 100644
--- a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
@@ -14,27 +14,22 @@
 
 package com.android.settings.fuelgauge;
 
-import android.app.AlertDialog;
 import android.app.AppOpsManager;
-import android.app.Dialog;
 import android.app.Fragment;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Bundle;
 import android.os.UserManager;
 import android.support.annotation.VisibleForTesting;
-import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
-import android.util.Log;
 
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
 import com.android.settings.wrapper.DevicePolicyManagerWrapper;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
@@ -99,15 +94,6 @@
         return mTargetPackage != null;
     }
 
-    /**
-     * Called from the warning dialog, if the user decides to go ahead and disable background
-     * activity for this package
-     */
-    public void setRestricted(Preference preference) {
-        mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_IGNORED);
-        updateSummary(preference);
-    }
-
     @Override
     public String getPreferenceKey() {
         return KEY_BACKGROUND_ACTIVITY;
@@ -119,20 +105,13 @@
             final int mode = mAppOpsManager
                     .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
             final boolean restricted = mode == AppOpsManager.MODE_IGNORED;
-            if (!restricted) {
-                showDialog();
-                return false;
-            }
-            mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED);
-            updateSummary(preference);
-            return true;
+            showDialog(restricted);
         }
 
         return false;
     }
 
-    @VisibleForTesting
-    void updateSummary(Preference preference) {
+    public void updateSummary(Preference preference) {
         if (mPowerWhitelistBackend.isWhitelisted(mTargetPackage)) {
             preference.setSummary(R.string.background_activity_summary_whitelisted);
             return;
@@ -150,42 +129,16 @@
     }
 
     @VisibleForTesting
-    void showDialog() {
-        final WarningDialogFragment dialogFragment = new WarningDialogFragment();
+    void showDialog(boolean restricted) {
+        final AppInfo appInfo = new AppInfo.Builder()
+                .setPackageName(mTargetPackage)
+                .build();
+        BatteryTip tip = restricted
+                ? new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo)
+                : new RestrictAppTip(BatteryTip.StateType.NEW, appInfo);
+
+        final BatteryTipDialogFragment dialogFragment = BatteryTipDialogFragment.newInstance(tip);
         dialogFragment.setTargetFragment(mFragment, 0 /* requestCode */);
         dialogFragment.show(mFragment.getFragmentManager(), TAG);
     }
-
-    interface WarningConfirmationListener {
-        void onLimitBackgroundActivity();
-    }
-
-    /**
-     * Warning dialog to show to the user as turning off background activity can lead to
-     * apps misbehaving as their background task scheduling guarantees will no longer be honored.
-     */
-    public static class WarningDialogFragment extends InstrumentedDialogFragment {
-        @Override
-        public int getMetricsCategory() {
-            // TODO (b/65494831): add metric id
-            return 0;
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final WarningConfirmationListener listener =
-                    (WarningConfirmationListener) getTargetFragment();
-            return new AlertDialog.Builder(getContext())
-                    .setTitle(R.string.background_activity_warning_dialog_title)
-                    .setMessage(R.string.background_activity_warning_dialog_text)
-                    .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            listener.onLimitBackgroundActivity();
-                        }
-                    })
-                    .setNegativeButton(R.string.dlg_cancel, null)
-                    .create();
-        }
-    }
 }
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
index b51474d..66ce3ca 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
@@ -34,6 +34,10 @@
 import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
+
+import java.util.List;
 
 /**
  * Dialog Fragment to show action dialog for each anomaly
@@ -84,6 +88,39 @@
                         .setView(view)
                         .setPositiveButton(android.R.string.ok, null)
                         .create();
+            case BatteryTip.TipType.APP_RESTRICTION:
+                final RestrictAppTip restrictAppTip = (RestrictAppTip) mBatteryTip;
+                final List<AppInfo> restrictedAppList = restrictAppTip.getRestrictAppList();
+                final int num = restrictedAppList.size();
+
+                final AlertDialog.Builder builder = new AlertDialog.Builder(context)
+                        .setTitle(context.getResources().getQuantityString(
+                                R.plurals.battery_tip_restrict_app_dialog_title, num, num))
+                        .setMessage(getString(R.string.battery_tip_restrict_app_dialog_message))
+                        .setPositiveButton(R.string.battery_tip_restrict_app_dialog_ok, this)
+                        .setNegativeButton(android.R.string.cancel, null);
+
+                // TODO(b/72385333): consider building dialog with 5+ apps when strings are done
+                if (num > 1) {
+                    final RecyclerView restrictionView = (RecyclerView) LayoutInflater.from(
+                            context).inflate(R.layout.recycler_view, null);
+                    restrictionView.setLayoutManager(new LinearLayoutManager(context));
+                    restrictionView.setAdapter(new HighUsageAdapter(context, restrictedAppList));
+                    builder.setView(restrictionView);
+                }
+
+                return builder.create();
+            case BatteryTip.TipType.REMOVE_APP_RESTRICTION:
+                final UnrestrictAppTip unrestrictAppTip = (UnrestrictAppTip) mBatteryTip;
+                final CharSequence name = Utils.getApplicationLabel(context,
+                        unrestrictAppTip.getPackageName());
+
+                return new AlertDialog.Builder(context)
+                        .setTitle(getString(R.string.battery_tip_unrestrict_app_dialog_title, name))
+                        .setMessage(R.string.battery_tip_unrestrict_app_dialog_message)
+                        .setPositiveButton(R.string.battery_tip_unrestrict_app_dialog_ok, this)
+                        .setNegativeButton(R.string.battery_tip_unrestrict_app_dialog_cancel, null)
+                        .create();
             default:
                 throw new IllegalArgumentException("unknown type " + mBatteryTip.getType());
         }
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index ced3461..a615841 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -26,6 +26,7 @@
 import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.SmartBatteryDetector;
+import com.android.settings.fuelgauge.batterytip.detectors.RestrictAppDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.SummaryDetector;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
@@ -70,6 +71,7 @@
         tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect());
         tips.add(new EarlyWarningDetector(policy, context).detect());
         tips.add(new SummaryDetector(policy).detect());
+        tips.add(new RestrictAppDetector(policy).detect());
 
         Collections.sort(tips);
         return tips;
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
index 5781afd..5eec322 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
@@ -17,12 +17,17 @@
 package com.android.settings.fuelgauge.batterytip;
 
 import android.app.Fragment;
+import android.content.Context;
 
 import com.android.settings.SettingsActivity;
 import com.android.settings.fuelgauge.batterytip.actions.BatterySaverAction;
 import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
+import com.android.settings.fuelgauge.batterytip.actions.RestrictAppAction;
 import com.android.settings.fuelgauge.batterytip.actions.SmartBatteryAction;
+import com.android.settings.fuelgauge.batterytip.actions.UnrestrictAppAction;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
 
 /**
  * Utility class for {@link BatteryTip}
@@ -42,7 +47,11 @@
             case BatteryTip.TipType.SMART_BATTERY_MANAGER:
                 return new SmartBatteryAction(settingsActivity, fragment);
             case BatteryTip.TipType.BATTERY_SAVER:
-                return new BatterySaverAction(settingsActivity.getApplicationContext());
+                return new BatterySaverAction(settingsActivity);
+            case BatteryTip.TipType.APP_RESTRICTION:
+                return new RestrictAppAction(settingsActivity, (RestrictAppTip) batteryTip);
+            case BatteryTip.TipType.REMOVE_APP_RESTRICTION:
+                return new UnrestrictAppAction(settingsActivity, (UnrestrictAppTip) batteryTip);
             default:
                 return null;
         }
diff --git a/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java b/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
index 60aa6c8..6c129d8 100644
--- a/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
+++ b/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
@@ -77,7 +77,9 @@
                 Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, app.packageName,
                         UserHandle.myUserId()));
         holder.appName.setText(Utils.getApplicationLabel(mContext, app.packageName));
-        holder.appTime.setText(Utils.formatElapsedTime(mContext, app.screenOnTimeMs, false));
+        if (app.screenOnTimeMs != 0) {
+            holder.appTime.setText(Utils.formatElapsedTime(mContext, app.screenOnTimeMs, false));
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java
new file mode 100644
index 0000000..9c49822
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppAction.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 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.fuelgauge.batterytip.actions;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+
+import java.util.List;
+
+/**
+ * Action to restrict the apps, then app is not allowed to run in the background.
+ */
+public class RestrictAppAction extends BatteryTipAction {
+    private RestrictAppTip mRestrictAppTip;
+    @VisibleForTesting
+    BatteryUtils mBatteryUtils;
+
+    public RestrictAppAction(Context context, RestrictAppTip tip) {
+        super(context);
+        mRestrictAppTip = tip;
+        mBatteryUtils = BatteryUtils.getInstance(context);
+    }
+
+    /**
+     * Handle the action when user clicks positive button
+     */
+    @Override
+    public void handlePositiveAction() {
+        final List<AppInfo> appInfos = mRestrictAppTip.getRestrictAppList();
+
+        for (int i = 0, size = appInfos.size(); i < size; i++) {
+            final String packageName = appInfos.get(i).packageName;
+            // Force app standby, then app can't run in the background
+            mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName,
+                    AppOpsManager.MODE_IGNORED);
+        }
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java
new file mode 100644
index 0000000..16812f6
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.fuelgauge.batterytip.actions;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
+
+/**
+ * Action to clear the restriction to the app
+ */
+public class UnrestrictAppAction extends BatteryTipAction {
+    private UnrestrictAppTip mUnRestrictAppTip;
+    private BatteryUtils mBatteryUtils;
+
+    public UnrestrictAppAction(Context context, UnrestrictAppTip tip) {
+        super(context);
+        mUnRestrictAppTip = tip;
+        mBatteryUtils = BatteryUtils.getInstance(context);
+    }
+
+    /**
+     * Handle the action when user clicks positive button
+     */
+    @Override
+    public void handlePositiveAction() {
+        final String packageName = mUnRestrictAppTip.getPackageName();
+        // Clear force app standby, then app can run in the background
+        mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName,
+                AppOpsManager.MODE_ALLOWED);
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java
new file mode 100644
index 0000000..46e241a
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.fuelgauge.batterytip.detectors;
+
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Detector whether to show summary tip. This detector should be executed as the last
+ * {@link BatteryTipDetector} since it need the most up-to-date {@code visibleTips}
+ */
+public class RestrictAppDetector implements BatteryTipDetector {
+    private BatteryTipPolicy mPolicy;
+
+    public RestrictAppDetector(BatteryTipPolicy policy) {
+        mPolicy = policy;
+    }
+
+    @Override
+    public BatteryTip detect() {
+        // TODO(b/70570352): Detect restrict apps here, get data from database
+        final List<AppInfo> highUsageApps = new ArrayList<>();
+        return new RestrictAppTip(
+                highUsageApps.isEmpty() ? BatteryTip.StateType.INVISIBLE : BatteryTip.StateType.NEW,
+                highUsageApps);
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
index 09ebc4b..59cd5ee 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
@@ -50,7 +50,8 @@
             TipType.SMART_BATTERY_MANAGER,
             TipType.APP_RESTRICTION,
             TipType.REDUCED_BATTERY,
-            TipType.LOW_BATTERY})
+            TipType.LOW_BATTERY,
+            TipType.REMOVE_APP_RESTRICTION})
     public @interface TipType {
         int SMART_BATTERY_MANAGER = 0;
         int APP_RESTRICTION = 1;
@@ -59,6 +60,7 @@
         int REDUCED_BATTERY = 4;
         int LOW_BATTERY = 5;
         int SUMMARY = 6;
+        int REMOVE_APP_RESTRICTION = 7;
     }
 
     private static final String KEY_PREFIX = "key_battery_tip";
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
new file mode 100644
index 0000000..1d84d7f
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 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.fuelgauge.batterytip.tips;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Parcel;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tip to suggest user to restrict some bad apps
+ */
+public class RestrictAppTip extends BatteryTip {
+    private List<AppInfo> mRestrictAppList;
+
+    public RestrictAppTip(@StateType int state, List<AppInfo> restrictApps) {
+        super(TipType.APP_RESTRICTION, state, true /* showDialog */);
+        mRestrictAppList = restrictApps;
+    }
+
+    public RestrictAppTip(@StateType int state, AppInfo appInfo) {
+        super(TipType.APP_RESTRICTION, state, true /* showDialog */);
+        mRestrictAppList = new ArrayList<>();
+        mRestrictAppList.add(appInfo);
+    }
+
+    @VisibleForTesting
+    RestrictAppTip(Parcel in) {
+        super(in);
+        mRestrictAppList = in.createTypedArrayList(AppInfo.CREATOR);
+    }
+
+    @Override
+    public CharSequence getTitle(Context context) {
+        final int num = mRestrictAppList.size();
+        return context.getResources().getQuantityString(
+                mState == StateType.HANDLED
+                        ? R.plurals.battery_tip_restrict_handled_title
+                        : R.plurals.battery_tip_restrict_title,
+                num, num);
+    }
+
+    @Override
+    public CharSequence getSummary(Context context) {
+        final int num = mRestrictAppList.size();
+        final CharSequence appLabel = num > 0 ? Utils.getApplicationLabel(context,
+                mRestrictAppList.get(0).packageName) : "";
+        return mState == StateType.HANDLED
+                ? context.getString(R.string.battery_tip_restrict_handled_summary)
+                : context.getResources().getQuantityString(R.plurals.battery_tip_restrict_summary,
+                num, appLabel, num);
+    }
+
+    @Override
+    public int getIconId() {
+        return mState == StateType.HANDLED
+                ? R.drawable.ic_perm_device_information_green_24dp
+                : R.drawable.ic_battery_alert_24dp;
+    }
+
+    @Override
+    public void updateState(BatteryTip tip) {
+        mState = tip.mState;
+    }
+
+    public List<AppInfo> getRestrictAppList() {
+        return mRestrictAppList;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeTypedList(mRestrictAppList);
+    }
+
+    public static final Creator CREATOR = new Creator() {
+        public BatteryTip createFromParcel(Parcel in) {
+            return new RestrictAppTip(in);
+        }
+
+        public BatteryTip[] newArray(int size) {
+            return new RestrictAppTip[size];
+        }
+    };
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java
new file mode 100644
index 0000000..ec67f6a
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 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.fuelgauge.batterytip.tips;
+
+import android.content.Context;
+import android.os.Parcel;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+
+/**
+ * Tip to suggest user to remove app restriction. This is the empty tip and it is only used in
+ * {@link com.android.settings.fuelgauge.AdvancedPowerUsageDetail} to create dialog.
+ */
+public class UnrestrictAppTip extends BatteryTip {
+    private AppInfo mAppInfo;
+
+    public UnrestrictAppTip(@StateType int state, AppInfo appInfo) {
+        super(TipType.REMOVE_APP_RESTRICTION, state, true /* showDialog */);
+        mAppInfo = appInfo;
+    }
+
+    @VisibleForTesting
+    UnrestrictAppTip(Parcel in) {
+        super(in);
+        mAppInfo = in.readParcelable(getClass().getClassLoader());
+    }
+
+    @Override
+    public CharSequence getTitle(Context context) {
+        // Don't need title since this is an empty tip
+        return null;
+    }
+
+    @Override
+    public CharSequence getSummary(Context context) {
+        // Don't need summary since this is an empty tip
+        return null;
+    }
+
+    @Override
+    public int getIconId() {
+        return 0;
+    }
+
+    public String getPackageName() {
+        return mAppInfo.packageName;
+    }
+
+    @Override
+    public void updateState(BatteryTip tip) {
+        mState = tip.mState;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeParcelable(mAppInfo, flags);
+    }
+
+    public static final Creator CREATOR = new Creator() {
+        public BatteryTip createFromParcel(Parcel in) {
+            return new UnrestrictAppTip(in);
+        }
+
+        public BatteryTip[] newArray(int size) {
+            return new UnrestrictAppTip[size];
+        }
+    };
+}
diff --git a/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java b/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java
index 38b64a5..3243e56 100644
--- a/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java
+++ b/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java
@@ -27,7 +27,7 @@
 import android.widget.ArrayAdapter;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.inputmethod.UserDictionaryAddWordContents.LocaleRenderer;
@@ -41,7 +41,7 @@
  * As opposed to the UserDictionaryActivity, this is only invoked within Settings
  * from the UserDictionarySettings.
  */
-public class UserDictionaryAddWordFragment extends InstrumentedPreferenceFragment
+public class UserDictionaryAddWordFragment extends InstrumentedFragment
         implements AdapterView.OnItemSelectedListener,
         com.android.internal.app.LocalePicker.LocaleSelectionListener {
 
@@ -55,7 +55,6 @@
     public void onActivityCreated(final Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
         setHasOptionsMenu(true);
-        getActivity().getActionBar().setTitle(R.string.user_dict_settings_title);
         // Keep the instance so that we remember mContents when configuration changes (eg rotation)
         setRetainInstance(true);
     }
diff --git a/src/com/android/settings/location/LocationFooterPreferenceController.java b/src/com/android/settings/location/LocationFooterPreferenceController.java
new file mode 100644
index 0000000..f15d437
--- /dev/null
+++ b/src/com/android/settings/location/LocationFooterPreferenceController.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 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.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.location.LocationManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.util.Log;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.widget.FooterPreference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Preference controller for location footer preference category
+ */
+public class LocationFooterPreferenceController extends LocationBasePreferenceController
+        implements LifecycleObserver, OnPause {
+    private static final String TAG = "LocationFooter";
+    private static final String KEY_LOCATION_FOOTER = "location_footer";
+    private static final Intent INJECT_INTENT =
+            new Intent(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    private Collection<ComponentName> mFooterInjectors;
+
+    public LocationFooterPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, lifecycle);
+        mContext = context;
+        mPackageManager = mContext.getPackageManager();
+        mFooterInjectors = new ArrayList<>();
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_LOCATION_FOOTER;
+    }
+
+    /**
+     * Insert footer preferences. Send a {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION}
+     * broadcast to receivers who have injected a footer
+     */
+    @Override
+    public void updateState(Preference preference) {
+        PreferenceCategory category = (PreferenceCategory) preference;
+        category.removeAll();
+        mFooterInjectors.clear();
+        Collection<FooterData> footerData = getFooterData();
+        for (FooterData data : footerData) {
+            // Generate a footer preference with the given text
+            FooterPreference footerPreference = new FooterPreference(preference.getContext());
+            String footerString;
+            try {
+                footerString =
+                        mPackageManager
+                                .getResourcesForApplication(data.applicationInfo)
+                                .getString(data.footerStringRes);
+            } catch (NameNotFoundException exception) {
+                if (Log.isLoggable(TAG, Log.WARN)) {
+                    Log.w(
+                            TAG,
+                            "Resources not found for application "
+                                    + data.applicationInfo.packageName);
+                }
+                continue;
+            }
+            footerPreference.setTitle(footerString);
+            // Inject the footer
+            category.addPreference(footerPreference);
+            // Send broadcast to the injector announcing a footer has been injected
+            sendBroadcastFooterDisplayed(data.componentName);
+            mFooterInjectors.add(data.componentName);
+        }
+    }
+
+    /**
+     * Do nothing on location mode changes.
+     */
+    @Override
+    public void onLocationModeChanged(int mode, boolean restricted) {}
+
+    /**
+     * Location footer preference group should be displayed if there is at least one footer to
+     * inject.
+     */
+    @Override
+    public boolean isAvailable() {
+        return !getFooterData().isEmpty();
+    }
+
+    /**
+     * Send a {@link LocationManager#SETTINGS_FOOTER_REMOVED_ACTION} broadcast to footer injectors
+     * when LocationFragment is on pause
+     */
+    @Override
+    public void onPause() {
+        // Send broadcast to the footer injectors. Notify them the footer is not visible.
+        for (ComponentName componentName : mFooterInjectors) {
+            final Intent intent = new Intent(LocationManager.SETTINGS_FOOTER_REMOVED_ACTION);
+            intent.setComponent(componentName);
+            mContext.sendBroadcast(intent);
+        }
+    }
+
+    /**
+     * Send a {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION} broadcast to a footer
+     * injector.
+     */
+    @VisibleForTesting
+    void sendBroadcastFooterDisplayed(ComponentName componentName) {
+        Intent intent = new Intent(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
+        intent.setComponent(componentName);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Return a list of strings with text provided by ACTION_INJECT_FOOTER broadcast receivers.
+     */
+    private Collection<FooterData> getFooterData() {
+        // Fetch footer text from system apps
+        final List<ResolveInfo> resolveInfos =
+                mPackageManager.queryBroadcastReceivers(
+                        INJECT_INTENT, PackageManager.GET_META_DATA);
+        if (resolveInfos == null) {
+            if (Log.isLoggable(TAG, Log.ERROR)) {
+                Log.e(TAG, "Unable to resolve intent " + INJECT_INTENT);
+                return Collections.emptyList();
+            }
+        } else if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Found broadcast receivers: " + resolveInfos);
+        }
+
+        final Collection<FooterData> footerDataList = new ArrayList<>(resolveInfos.size());
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            final ActivityInfo activityInfo = resolveInfo.activityInfo;
+            final ApplicationInfo appInfo = activityInfo.applicationInfo;
+
+            // If a non-system app tries to inject footer, ignore it
+            if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                if (Log.isLoggable(TAG, Log.WARN)) {
+                    Log.w(TAG, "Ignoring attempt to inject footer from app not in system image: "
+                            + resolveInfo);
+                    continue;
+                }
+            }
+
+            // Get the footer text resource id from broadcast receiver's metadata
+            if (activityInfo.metaData == null) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "No METADATA in broadcast receiver " + activityInfo.name);
+                    continue;
+                }
+            }
+
+            final int footerTextRes =
+                    activityInfo.metaData.getInt(LocationManager.METADATA_SETTINGS_FOOTER_STRING);
+            if (footerTextRes == 0) {
+                if (Log.isLoggable(TAG, Log.WARN)) {
+                    Log.w(
+                            TAG,
+                            "No mapping of integer exists for "
+                                    + LocationManager.METADATA_SETTINGS_FOOTER_STRING);
+                }
+                continue;
+            }
+            footerDataList.add(
+                    new FooterData(
+                            footerTextRes,
+                            appInfo,
+                            new ComponentName(activityInfo.packageName, activityInfo.name)));
+        }
+        return footerDataList;
+    }
+
+    /**
+     * Contains information related to a footer.
+     */
+    private static class FooterData {
+
+        // The string resource of the footer
+        final int footerStringRes;
+
+        // Application info of receiver injecting this footer
+        final ApplicationInfo applicationInfo;
+
+        // The component that injected the footer. It must be a receiver of broadcast
+        // LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION
+        final ComponentName componentName;
+
+        FooterData(int footerRes, ApplicationInfo appInfo, ComponentName componentName) {
+            this.footerStringRes = footerRes;
+            this.applicationInfo = appInfo;
+            this.componentName = componentName;
+        }
+    }
+}
diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java
index 3cc5b84..510c162 100644
--- a/src/com/android/settings/location/LocationSettings.java
+++ b/src/com/android/settings/location/LocationSettings.java
@@ -131,9 +131,10 @@
         controllers.add(new LocationForWorkPreferenceController(context, lifecycle));
         controllers.add(
                 new RecentLocationRequestPreferenceController(context, fragment, lifecycle));
+        controllers.add(new LocationScanningPreferenceController(context));
         controllers.add(
                 new LocationServicePreferenceController(context, fragment, lifecycle));
-        controllers.add(new LocationScanningPreferenceController(context));
+        controllers.add(new LocationFooterPreferenceController(context, lifecycle));
         return controllers;
     }
 
diff --git a/src/com/android/settings/network/NetworkScorerPicker.java b/src/com/android/settings/network/NetworkScorerPicker.java
index 187c9ce..34accf2 100644
--- a/src/com/android/settings/network/NetworkScorerPicker.java
+++ b/src/com/android/settings/network/NetworkScorerPicker.java
@@ -50,7 +50,6 @@
     @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
         super.onCreatePreferences(savedInstanceState, rootKey);
-        addPreferencesFromResource(R.xml.network_scorer_picker_prefs);
         updateCandidates();
     }
 
@@ -69,6 +68,11 @@
         return view;
     }
 
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.network_scorer_picker_prefs;
+    }
+
     @VisibleForTesting
     public void updateCandidates() {
         final PreferenceScreen screen = getPreferenceScreen();
diff --git a/src/com/android/settings/nfc/AndroidBeam.java b/src/com/android/settings/nfc/AndroidBeam.java
index 707017b..8377f14 100644
--- a/src/com/android/settings/nfc/AndroidBeam.java
+++ b/src/com/android/settings/nfc/AndroidBeam.java
@@ -29,7 +29,7 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.HelpUtils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.ShowAdminSupportDetailsDialog;
@@ -38,7 +38,7 @@
 
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
-public class AndroidBeam extends InstrumentedPreferenceFragment
+public class AndroidBeam extends InstrumentedFragment
         implements SwitchBar.OnSwitchChangeListener {
     private View mView;
     private NfcAdapter mNfcAdapter;
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index 14ccf23..78139dc 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -19,10 +19,8 @@
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.content.Context;
-import android.content.Intent;
 import android.os.AsyncTask;
-import android.os.Bundle;
-import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceCategory;
 import android.support.v7.preference.PreferenceGroup;
@@ -32,9 +30,8 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.applications.AppInfoBase;
-import com.android.settings.widget.MasterSwitchPreference;
+import com.android.settings.widget.MasterCheckBoxPreference;
+import com.android.settingslib.RestrictedSwitchPreference;
 import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.ArrayList;
@@ -179,17 +176,37 @@
                 groupCategory.setKey(group.getId());
                 populateGroupToggle(groupCategory, group);
             }
-
-            final List<NotificationChannel> channels = group.getChannels();
-            Collections.sort(channels, mChannelComparator);
-            int N = channels.size();
-            for (int i = 0; i < N; i++) {
-                final NotificationChannel channel = channels.get(i);
-                populateSingleChannelPrefs(groupCategory, channel, group.isBlocked());
+            if (!group.isBlocked()) {
+                final List<NotificationChannel> channels = group.getChannels();
+                Collections.sort(channels, mChannelComparator);
+                int N = channels.size();
+                for (int i = 0; i < N; i++) {
+                    final NotificationChannel channel = channels.get(i);
+                    populateSingleChannelPrefs(groupCategory, channel, group.isBlocked());
+                }
             }
         }
     }
 
+    protected void populateGroupToggle(final PreferenceGroup parent,
+            NotificationChannelGroup group) {
+        RestrictedSwitchPreference preference = new RestrictedSwitchPreference(getPrefContext());
+        preference.setTitle(R.string.notification_switch_label);
+        preference.setEnabled(mSuspendedAppsAdmin == null
+                && isChannelGroupBlockable(group));
+        preference.setChecked(!group.isBlocked());
+        preference.setOnPreferenceClickListener(preference1 -> {
+            final boolean allowGroup = ((SwitchPreference) preference1).isChecked();
+            group.setBlocked(!allowGroup);
+            mBackend.updateChannelGroup(mAppRow.pkg, mAppRow.uid, group);
+
+            onGroupBlockStateChanged(group);
+            return true;
+        });
+
+        parent.addPreference(preference);
+    }
+
     private Comparator<NotificationChannelGroup> mChannelGroupComparator =
             new Comparator<NotificationChannelGroup>() {
 
@@ -204,4 +221,37 @@
                     return left.getId().compareTo(right.getId());
                 }
             };
+
+    protected void onGroupBlockStateChanged(NotificationChannelGroup group) {
+        if (group == null) {
+            return;
+        }
+        PreferenceGroup groupGroup = (
+                PreferenceGroup) getPreferenceScreen().findPreference(group.getId());
+
+        if (groupGroup != null) {
+            if (group.isBlocked()) {
+                List<Preference> toRemove = new ArrayList<>();
+                int childCount = groupGroup.getPreferenceCount();
+                for (int i = 0; i < childCount; i++) {
+                    Preference pref = groupGroup.getPreference(i);
+                    if (pref instanceof MasterCheckBoxPreference) {
+                        toRemove.add(pref);
+                    }
+                }
+                for (Preference pref : toRemove) {
+                    groupGroup.removePreference(pref);
+                }
+            } else {
+                final List<NotificationChannel> channels = group.getChannels();
+                Collections.sort(channels, mChannelComparator);
+                int N = channels.size();
+                for (int i = 0; i < N; i++) {
+                    final NotificationChannel channel = channels.get(i);
+                    populateSingleChannelPrefs(groupGroup, channel, group.isBlocked());
+                }
+            }
+        }
+    }
+
 }
diff --git a/src/com/android/settings/notification/BlockPreferenceController.java b/src/com/android/settings/notification/BlockPreferenceController.java
index 6b65b0f..8b27098 100644
--- a/src/com/android/settings/notification/BlockPreferenceController.java
+++ b/src/com/android/settings/notification/BlockPreferenceController.java
@@ -103,6 +103,10 @@
                 mChannel.setImportance(importance);
                 saveChannel();
             }
+            if (mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid)) {
+                mAppRow.banned = blocked;
+                mBackend.setNotificationsEnabledForPackage(mAppRow.pkg, mAppRow.uid, !blocked);
+            }
         } else if (mChannelGroup != null && mChannelGroup.getGroup() != null) {
             mChannelGroup.setBlocked(blocked);
             mBackend.updateChannelGroup(mAppRow.pkg, mAppRow.uid, mChannelGroup.getGroup());
diff --git a/src/com/android/settings/notification/EnableZenModeDialog.java b/src/com/android/settings/notification/EnableZenModeDialog.java
deleted file mode 100644
index f683a21..0000000
--- a/src/com/android/settings/notification/EnableZenModeDialog.java
+++ /dev/null
@@ -1,467 +0,0 @@
-package com.android.settings.notification;
-
-/*
- * Copyright (C) 2018 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.
- */
-
-import static android.util.Log.wtf;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlarmManager;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.service.notification.ZenModeConfig;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.R;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
-
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.Objects;
-
-public class EnableZenModeDialog extends InstrumentedDialogFragment {
-
-    private static final String TAG = "EnableZenModeDialog";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS;
-    private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
-    private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
-    private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
-
-    @VisibleForTesting
-    public static final int FOREVER_CONDITION_INDEX = 0;
-    @VisibleForTesting
-    public static final int COUNTDOWN_CONDITION_INDEX = 1;
-    @VisibleForTesting
-    public static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
-    @VisibleForTesting
-    protected Activity mActivity;
-
-    private static final int SECONDS_MS = 1000;
-    private static final int MINUTES_MS = 60 * SECONDS_MS;
-
-    @VisibleForTesting
-    protected Uri mForeverId;
-    private int mBucketIndex = -1;
-
-    private AlarmManager mAlarmManager;
-    private int mUserId;
-    private boolean mAttached;
-
-    @VisibleForTesting
-    protected Context mContext;
-
-    private RadioGroup mZenRadioGroup;
-    @VisibleForTesting
-    protected LinearLayout mZenRadioGroupContent;
-    private int MAX_MANUAL_DND_OPTIONS = 3;
-
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        NotificationManager noMan = (NotificationManager) getContext().
-                getSystemService(Context.NOTIFICATION_SERVICE);
-        mContext = getContext();
-        mForeverId =  Condition.newId(mContext).appendPath("forever").build();
-        mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
-        mUserId = mContext.getUserId();
-        mAttached = false;
-
-        final AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
-                .setTitle(R.string.zen_mode_settings_turn_on_dialog_title)
-                .setNegativeButton(R.string.cancel, null)
-                .setPositiveButton(R.string.zen_mode_enable_dialog_turn_on,
-                        new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int which) {
-                                int checkedId = mZenRadioGroup.getCheckedRadioButtonId();
-                                ConditionTag tag = getConditionTagAt(checkedId);
-
-                                if (isForever(tag.condition)) {
-                                    MetricsLogger.action(getContext(),
-                                            MetricsProto.MetricsEvent.
-                                                    NOTIFICATION_ZEN_MODE_TOGGLE_ON_FOREVER);
-                                } else if (isAlarm(tag.condition)) {
-                                    MetricsLogger.action(getContext(),
-                                            MetricsProto.MetricsEvent.
-                                                    NOTIFICATION_ZEN_MODE_TOGGLE_ON_ALARM);
-                                } else if (isCountdown(tag.condition)) {
-                                    MetricsLogger.action(getContext(),
-                                            MetricsProto.MetricsEvent.
-                                                    NOTIFICATION_ZEN_MODE_TOGGLE_ON_COUNTDOWN);
-                                } else {
-                                    wtf(TAG, "Invalid manual condition: " + tag.condition);
-                                }
-                                // always triggers priority-only dnd with chosen condition
-                                noMan.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
-                                        getRealConditionId(tag.condition), TAG);
-                            }
-                        });
-
-        View contentView = getContentView();
-        bindConditions(forever());
-        builder.setView(contentView);
-        return builder.create();
-    }
-
-    private void hideAllConditions() {
-        final int N = mZenRadioGroupContent.getChildCount();
-        for (int i = 0; i < N; i++) {
-            mZenRadioGroupContent.getChildAt(i).setVisibility(View.GONE);
-        }
-    }
-
-    protected View getContentView() {
-        if (mActivity == null) {
-            mActivity = getActivity();
-        }
-        final LayoutInflater inflater = mActivity.getLayoutInflater();
-        View contentView = inflater.inflate(R.layout.zen_mode_turn_on_dialog_container, null);
-        ScrollView container = (ScrollView) contentView.findViewById(R.id.container);
-
-        mZenRadioGroup = container.findViewById(R.id.zen_radio_buttons);
-        mZenRadioGroupContent = container.findViewById(R.id.zen_radio_buttons_content);
-
-        for (int i = 0; i < MAX_MANUAL_DND_OPTIONS; i++) {
-            final View radioButton = inflater.inflate(R.layout.zen_mode_radio_button,
-                    mZenRadioGroup, false);
-            mZenRadioGroup.addView(radioButton);
-            radioButton.setId(i);
-
-            final View radioButtonContent = inflater.inflate(R.layout.zen_mode_condition,
-                    mZenRadioGroupContent, false);
-            radioButtonContent.setId(i + MAX_MANUAL_DND_OPTIONS);
-            mZenRadioGroupContent.addView(radioButtonContent);
-        }
-        hideAllConditions();
-        return contentView;
-    }
-
-    @Override
-    public int getMetricsCategory() {
-       return MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_ENABLE_DIALOG;
-    }
-
-    @VisibleForTesting
-    protected void bind(final Condition condition, final View row, final int rowId) {
-        if (condition == null) throw new IllegalArgumentException("condition must not be null");
-        final boolean enabled = condition.state == Condition.STATE_TRUE;
-        final ConditionTag tag = row.getTag() != null ? (ConditionTag) row.getTag() :
-                new ConditionTag();
-        row.setTag(tag);
-        final boolean first = tag.rb == null;
-        if (tag.rb == null) {
-            tag.rb = (RadioButton) mZenRadioGroup.getChildAt(rowId);
-        }
-        tag.condition = condition;
-        final Uri conditionId = getConditionId(tag.condition);
-        if (DEBUG) Log.d(TAG, "bind i=" + mZenRadioGroupContent.indexOfChild(row) + " first="
-                + first + " condition=" + conditionId);
-        tag.rb.setEnabled(enabled);
-        tag.rb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                if (isChecked) {
-                    tag.rb.setChecked(true);
-                    if (DEBUG) Log.d(TAG, "onCheckedChanged " + conditionId);
-                    MetricsLogger.action(mContext,
-                            MetricsProto.MetricsEvent.QS_DND_CONDITION_SELECT);
-                    announceConditionSelection(tag);
-                }
-            }
-        });
-
-        updateUi(tag, row, condition, enabled, rowId, conditionId);
-        row.setVisibility(View.VISIBLE);
-    }
-
-    @VisibleForTesting
-    protected ConditionTag getConditionTagAt(int index) {
-        return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag();
-    }
-
-    @VisibleForTesting
-    protected void bindConditions(Condition c) {
-        // forever
-        bind(forever(), mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX),
-                FOREVER_CONDITION_INDEX);
-        if (c == null) {
-            bindGenericCountdown();
-            bindNextAlarm(getTimeUntilNextAlarmCondition());
-        } else if (isForever(c)) {
-            getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
-            bindGenericCountdown();
-            bindNextAlarm(getTimeUntilNextAlarmCondition());
-        } else {
-            if (isAlarm(c)) {
-                bindGenericCountdown();
-                bindNextAlarm(c);
-                getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.setChecked(true);
-            } else if (isCountdown(c)) {
-                bindNextAlarm(getTimeUntilNextAlarmCondition());
-                bind(c, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
-                        COUNTDOWN_CONDITION_INDEX);
-                getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
-            } else {
-                wtf(TAG, "Invalid manual condition: " + c);
-            }
-        }
-    }
-
-    public static Uri getConditionId(Condition condition) {
-        return condition != null ? condition.id : null;
-    }
-
-    public Condition forever() {
-        Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-        return new Condition(foreverId, foreverSummary(mContext), "", "", 0 /*icon*/,
-                Condition.STATE_TRUE, 0 /*flags*/);
-    }
-
-    public long getNextAlarm() {
-        final AlarmManager.AlarmClockInfo info = mAlarmManager.getNextAlarmClock(mUserId);
-        return info != null ? info.getTriggerTime() : 0;
-    }
-
-    @VisibleForTesting
-    protected boolean isAlarm(Condition c) {
-        return c != null && ZenModeConfig.isValidCountdownToAlarmConditionId(c.id);
-    }
-
-    @VisibleForTesting
-    protected boolean isCountdown(Condition c) {
-        return c != null && ZenModeConfig.isValidCountdownConditionId(c.id);
-    }
-
-    private boolean isForever(Condition c) {
-        return c != null && mForeverId.equals(c.id);
-    }
-
-    private Uri getRealConditionId(Condition condition) {
-        return isForever(condition) ? null : getConditionId(condition);
-    }
-
-    private String foreverSummary(Context context) {
-        return context.getString(com.android.internal.R.string.zen_mode_forever);
-    }
-
-    private static void setToMidnight(Calendar calendar) {
-        calendar.set(Calendar.HOUR_OF_DAY, 0);
-        calendar.set(Calendar.MINUTE, 0);
-        calendar.set(Calendar.SECOND, 0);
-        calendar.set(Calendar.MILLISECOND, 0);
-    }
-
-    // Returns a time condition if the next alarm is within the next week.
-    @VisibleForTesting
-    protected Condition getTimeUntilNextAlarmCondition() {
-        GregorianCalendar weekRange = new GregorianCalendar();
-        setToMidnight(weekRange);
-        weekRange.add(Calendar.DATE, 6);
-        final long nextAlarmMs = getNextAlarm();
-        if (nextAlarmMs > 0) {
-            GregorianCalendar nextAlarm = new GregorianCalendar();
-            nextAlarm.setTimeInMillis(nextAlarmMs);
-            setToMidnight(nextAlarm);
-
-            if (weekRange.compareTo(nextAlarm) >= 0) {
-                return ZenModeConfig.toNextAlarmCondition(mContext, nextAlarmMs,
-                        ActivityManager.getCurrentUser());
-            }
-        }
-        return null;
-    }
-
-    @VisibleForTesting
-    protected void bindGenericCountdown() {
-        mBucketIndex = DEFAULT_BUCKET_INDEX;
-        Condition countdown = ZenModeConfig.toTimeCondition(mContext,
-                MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
-        if (!mAttached || getConditionTagAt(COUNTDOWN_CONDITION_INDEX).condition == null) {
-            bind(countdown, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
-                    COUNTDOWN_CONDITION_INDEX);
-        }
-    }
-
-    private void updateUi(ConditionTag tag, View row, Condition condition,
-            boolean enabled, int rowId, Uri conditionId) {
-        if (tag.lines == null) {
-            tag.lines = row.findViewById(android.R.id.content);
-        }
-        if (tag.line1 == null) {
-            tag.line1 = (TextView) row.findViewById(android.R.id.text1);
-        }
-
-        if (tag.line2 == null) {
-            tag.line2 = (TextView) row.findViewById(android.R.id.text2);
-        }
-
-        final String line1 = !TextUtils.isEmpty(condition.line1) ? condition.line1
-                : condition.summary;
-        final String line2 = condition.line2;
-        tag.line1.setText(line1);
-        if (TextUtils.isEmpty(line2)) {
-            tag.line2.setVisibility(View.GONE);
-        } else {
-            tag.line2.setVisibility(View.VISIBLE);
-            tag.line2.setText(line2);
-        }
-        tag.lines.setEnabled(enabled);
-        tag.lines.setAlpha(enabled ? 1 : .4f);
-
-        tag.lines.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                tag.rb.setChecked(true);
-            }
-        });
-
-        // minus button
-        final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
-        button1.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                onClickTimeButton(row, tag, false /*down*/, rowId);
-            }
-        });
-
-        // plus button
-        final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
-        button2.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                onClickTimeButton(row, tag, true /*up*/, rowId);
-            }
-        });
-
-        final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
-        if (rowId == COUNTDOWN_CONDITION_INDEX && time > 0) {
-            button1.setVisibility(View.VISIBLE);
-            button2.setVisibility(View.VISIBLE);
-            if (mBucketIndex > -1) {
-                button1.setEnabled(mBucketIndex > 0);
-                button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1);
-            } else {
-                final long span = time - System.currentTimeMillis();
-                button1.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS);
-                final Condition maxCondition = ZenModeConfig.toTimeCondition(mContext,
-                        MAX_BUCKET_MINUTES, ActivityManager.getCurrentUser());
-                button2.setEnabled(!Objects.equals(condition.summary, maxCondition.summary));
-            }
-
-            button1.setAlpha(button1.isEnabled() ? 1f : .5f);
-            button2.setAlpha(button2.isEnabled() ? 1f : .5f);
-        } else {
-            button1.setVisibility(View.GONE);
-            button2.setVisibility(View.GONE);
-        }
-    }
-
-    @VisibleForTesting
-    protected void bindNextAlarm(Condition c) {
-        View alarmContent = mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX);
-        ConditionTag tag = (ConditionTag) alarmContent.getTag();
-
-        if (c != null && (!mAttached || tag == null || tag.condition == null)) {
-            bind(c, alarmContent, COUNTDOWN_ALARM_CONDITION_INDEX);
-        }
-
-        // hide the alarm radio button if there isn't a "next alarm condition"
-        tag = (ConditionTag) alarmContent.getTag();
-        boolean showAlarm = tag != null && tag.condition != null;
-        mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(
-                showAlarm ? View.VISIBLE : View.GONE);
-        alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.GONE);
-    }
-
-    private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
-        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.QS_DND_TIME, up);
-        Condition newCondition = null;
-        final int N = MINUTE_BUCKETS.length;
-        if (mBucketIndex == -1) {
-            // not on a known index, search for the next or prev bucket by time
-            final Uri conditionId = getConditionId(tag.condition);
-            final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
-            final long now = System.currentTimeMillis();
-            for (int i = 0; i < N; i++) {
-                int j = up ? i : N - 1 - i;
-                final int bucketMinutes = MINUTE_BUCKETS[j];
-                final long bucketTime = now + bucketMinutes * MINUTES_MS;
-                if (up && bucketTime > time || !up && bucketTime < time) {
-                    mBucketIndex = j;
-                    newCondition = ZenModeConfig.toTimeCondition(mContext,
-                            bucketTime, bucketMinutes, ActivityManager.getCurrentUser(),
-                            false /*shortVersion*/);
-                    break;
-                }
-            }
-            if (newCondition == null) {
-                mBucketIndex = DEFAULT_BUCKET_INDEX;
-                newCondition = ZenModeConfig.toTimeCondition(mContext,
-                        MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
-            }
-        } else {
-            // on a known index, simply increment or decrement
-            mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1)));
-            newCondition = ZenModeConfig.toTimeCondition(mContext,
-                    MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
-        }
-        bind(newCondition, row, rowId);
-        tag.rb.setChecked(true);
-        announceConditionSelection(tag);
-    }
-
-    private void announceConditionSelection(ConditionTag tag) {
-        // condition will always be priority-only
-        String modeText = mContext.getString(R.string.zen_interruption_level_priority);
-        if (tag.line1 != null) {
-            mZenRadioGroupContent.announceForAccessibility(mContext.getString(
-                    R.string.zen_mode_and_condition, modeText, tag.line1.getText()));
-        }
-    }
-
-    // used as the view tag on condition rows
-    @VisibleForTesting
-    protected static class ConditionTag {
-        public RadioButton rb;
-        public View lines;
-        public TextView line1;
-        public TextView line2;
-        public Condition condition;
-    }
-}
diff --git a/src/com/android/settings/notification/NotificationAppPreference.java b/src/com/android/settings/notification/NotificationAppPreference.java
new file mode 100644
index 0000000..a6bcdd6
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationAppPreference.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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.notification;
+
+import android.content.Context;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ProgressBar;
+import android.widget.Switch;
+
+import com.android.settings.R;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.TwoTargetPreference;
+
+/**
+ * Shows an app icon, title and summary. Has a second switch touch target.
+ */
+public class NotificationAppPreference extends TwoTargetPreference {
+
+    private int mProgress;
+    private boolean mProgressVisible;
+    private Switch mSwitch;
+    private boolean mChecked;
+    private boolean mEnableSwitch = true;
+
+    public NotificationAppPreference(Context context) {
+        super(context);
+        setLayoutResource(R.layout.preference_app);
+    }
+
+    public NotificationAppPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setLayoutResource(R.layout.preference_app);
+    }
+
+    @Override
+    protected int getSecondTargetResId() {
+        return R.layout.preference_widget_master_switch;
+    }
+
+    public void setProgress(int amount) {
+        mProgress = amount;
+        mProgressVisible = true;
+        notifyChanged();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+
+        view.findViewById(R.id.summary_container)
+                .setVisibility(TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE);
+        final ProgressBar progress = (ProgressBar) view.findViewById(android.R.id.progress);
+        if (mProgressVisible) {
+            progress.setProgress(mProgress);
+            progress.setVisibility(View.VISIBLE);
+        } else {
+            progress.setVisibility(View.GONE);
+        }
+
+        final View widgetView = view.findViewById(android.R.id.widget_frame);
+        if (widgetView != null) {
+            widgetView.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mSwitch != null && !mSwitch.isEnabled()) {
+                        return;
+                    }
+                    setChecked(!mChecked);
+                    if (!callChangeListener(mChecked)) {
+                        setChecked(!mChecked);
+                    } else {
+                        persistBoolean(mChecked);
+                    }
+                }
+            });
+        }
+
+        mSwitch = (Switch) view.findViewById(R.id.switchWidget);
+        if (mSwitch != null) {
+            mSwitch.setContentDescription(getTitle());
+            mSwitch.setChecked(mChecked);
+            mSwitch.setEnabled(mEnableSwitch);
+        }
+    }
+
+    public boolean isChecked() {
+        return mSwitch != null && mChecked;
+    }
+
+    public void setChecked(boolean checked) {
+        mChecked = checked;
+        if (mSwitch != null) {
+            mSwitch.setChecked(checked);
+        }
+    }
+
+    public void setSwitchEnabled(boolean enabled) {
+        mEnableSwitch = enabled;
+        if (mSwitch != null) {
+            mSwitch.setEnabled(enabled);
+        }
+    }
+
+    /**
+     * If admin is not null, disables the switch.
+     * Otherwise, keep it enabled.
+     */
+    public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
+        setSwitchEnabled(admin == null);
+    }
+
+    public Switch getSwitch() {
+        return mSwitch;
+    }
+}
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index e047efa..d205fb4 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -15,6 +15,9 @@
  */
 package com.android.settings.notification;
 
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
 import android.app.INotificationManager;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -101,6 +104,12 @@
 
     public boolean setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
         try {
+            if (onlyHasDefaultChannel(pkg, uid)) {
+                NotificationChannel defaultChannel =
+                        getChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+                defaultChannel.setImportance(enabled ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE);
+                updateChannel(pkg, uid, defaultChannel);
+            }
             sINM.setNotificationsEnabledForPackage(pkg, uid, enabled);
             return true;
         } catch (Exception e) {
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 18b77bc..09789f3 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -74,6 +74,7 @@
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
@@ -252,30 +253,6 @@
         return null;
     }
 
-    protected void populateGroupToggle(final PreferenceGroup parent,
-            NotificationChannelGroup group) {
-        RestrictedSwitchPreference preference = new RestrictedSwitchPreference(getPrefContext());
-        preference.setTitle(R.string.notification_switch_label);
-        preference.setEnabled(mSuspendedAppsAdmin == null
-                && isChannelGroupBlockable(group));
-        preference.setChecked(!group.isBlocked());
-        preference.setOnPreferenceClickListener(preference1 -> {
-            final boolean allowGroup = ((SwitchPreference) preference1).isChecked();
-            group.setBlocked(!allowGroup);
-            mBackend.updateChannelGroup(mAppRow.pkg, mAppRow.uid, group);
-
-            for (int i = 0; i < parent.getPreferenceCount(); i++) {
-                Preference pref = parent.getPreference(i);
-                if (pref instanceof MasterSwitchPreference) {
-                    ((MasterSwitchPreference) pref).setSwitchEnabled(allowGroup);
-                }
-            }
-            return true;
-        });
-
-        parent.addPreference(preference);
-    }
-
     protected Preference populateSingleChannelPrefs(PreferenceGroup parent,
             final NotificationChannel channel, final boolean groupBlocked) {
         MasterCheckBoxPreference channelPref = new MasterCheckBoxPreference(
diff --git a/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java b/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java
index 3240ae0..279dcd3 100644
--- a/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java
+++ b/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.service.notification.NotifyingApp;
 import android.support.annotation.VisibleForTesting;
@@ -32,6 +33,7 @@
 import android.util.ArraySet;
 import android.util.IconDrawableFactory;
 import android.util.Log;
+import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
@@ -40,6 +42,7 @@
 import com.android.settings.applications.InstalledAppCounter;
 import com.android.settings.core.PreferenceControllerMixin;
 import com.android.settings.widget.AppPreference;
+import com.android.settings.widget.MasterSwitchPreference;
 import com.android.settingslib.applications.AppUtils;
 import com.android.settingslib.applications.ApplicationsState;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -197,13 +200,13 @@
 
         // Rebind prefs/avoid adding new prefs if possible. Adding/removing prefs causes jank.
         // Build a cached preference pool
-        final Map<String, Preference> appPreferences = new ArrayMap<>();
+        final Map<String, NotificationAppPreference> appPreferences = new ArrayMap<>();
         int prefCount = mCategory.getPreferenceCount();
         for (int i = 0; i < prefCount; i++) {
             final Preference pref = mCategory.getPreference(i);
             final String key = pref.getKey();
             if (!TextUtils.equals(key, KEY_SEE_ALL)) {
-                appPreferences.put(key, pref);
+                appPreferences.put(key, (NotificationAppPreference) pref);
             }
         }
         final int recentAppsCount = recentApps.size();
@@ -218,9 +221,9 @@
             }
 
             boolean rebindPref = true;
-            Preference pref = appPreferences.remove(pkgName);
+            NotificationAppPreference pref = appPreferences.remove(pkgName);
             if (pref == null) {
-                pref = new AppPreference(prefContext);
+                pref = new NotificationAppPreference(prefContext);
                 rebindPref = false;
             }
             pref.setKey(pkgName);
@@ -229,13 +232,23 @@
             pref.setSummary(Utils.formatRelativeTime(mContext,
                     System.currentTimeMillis() - app.getLastNotified(), false));
             pref.setOrder(i);
-            pref.setOnPreferenceClickListener(preference -> {
-                AppInfoBase.startAppInfoFragment(AppNotificationSettings.class,
-                        R.string.notifications_title, pkgName, appEntry.info.uid, mHost,
-                        1001 /*RequestCode */,
-                        MetricsProto.MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS);
-                    return true;
+            Bundle args = new Bundle();
+            args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkgName);
+            args.putInt(AppInfoBase.ARG_PACKAGE_UID, appEntry.info.uid);
+
+            pref.setIntent(Utils.onBuildStartFragmentIntent(mHost.getActivity(),
+                    AppNotificationSettings.class.getName(), args, null,
+                    R.string.notifications_title, null, false,
+                    MetricsProto.MetricsEvent.MANAGE_APPLICATIONS_NOTIFICATIONS));
+            pref.setOnPreferenceChangeListener((preference, newValue) -> {
+                boolean blocked = !(Boolean) newValue;
+                mNotificationBackend.setNotificationsEnabledForPackage(
+                        pkgName, appEntry.info.uid, !blocked);
+                return true;
             });
+            pref.setChecked(
+                    !mNotificationBackend.getNotificationsBanned(pkgName, appEntry.info.uid));
+
             if (!rebindPref) {
                 mCategory.addPreference(pref);
             }
diff --git a/src/com/android/settings/notification/SettingsEnableZenModeDialog.java b/src/com/android/settings/notification/SettingsEnableZenModeDialog.java
new file mode 100644
index 0000000..dccdc26
--- /dev/null
+++ b/src/com/android/settings/notification/SettingsEnableZenModeDialog.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 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.notification;
+
+import android.app.Dialog;
+import android.os.Bundle;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+public class SettingsEnableZenModeDialog extends InstrumentedDialogFragment {
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+         return new com.android.settingslib.notification.EnableZenModeDialog(
+                 getContext()).createDialog();
+    }
+
+    @Override
+    public int getMetricsCategory() {
+       return MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_ENABLE_DIALOG;
+    }
+}
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index 132dea6..032a5cb 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -180,7 +180,7 @@
             SoundSettings fragment, VolumeSeekBarPreference.Callback callback,
             Lifecycle lifecycle) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
-        controllers.add(new ZenModePreferenceController(context));
+        controllers.add(new ZenModePreferenceController(context, lifecycle));
         controllers.add(new VibrateWhenRingPreferenceController(context));
 
         // === Volumes ===
@@ -264,7 +264,7 @@
                 public List<String> getNonIndexableKeys(Context context) {
                     List<String> keys = super.getNonIndexableKeys(context);
                     // Duplicate results
-                    keys.add((new ZenModePreferenceController(context)).getPreferenceKey());
+                    keys.add((new ZenModePreferenceController(context, null)).getPreferenceKey());
                     return keys;
                 }
             };
diff --git a/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java b/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java
index 39cbf5d..9eab8bb 100644
--- a/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java
+++ b/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java
@@ -24,6 +24,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
+import android.service.notification.ZenModeConfig;
 import android.support.v14.preference.PreferenceFragment;
 import android.support.v7.preference.Preference;
 import android.util.Slog;
@@ -101,6 +102,13 @@
             PackageManager packageManager =  mContext.getPackageManager();
             ApplicationInfo info = packageManager.getApplicationInfo(
                     mRule.getOwner().getPackageName(), 0);
+            if (info.isSystemApp()) {
+                if (ZenModeConfig.isValidScheduleConditionId(mRule.getConditionId())) {
+                    return mContext.getDrawable(R.drawable.ic_timelapse);
+                } else if (ZenModeConfig.isValidEventConditionId(mRule.getConditionId())) {
+                    return mContext.getDrawable(R.drawable.ic_event);
+                }
+            }
             return info.loadIcon(packageManager);
         } catch (PackageManager.NameNotFoundException e) {
            Slog.w(TAG, "Unable to load icon - PackageManager.NameNotFoundException");
diff --git a/src/com/android/settings/notification/ZenAutomaticRuleSwitchPreferenceController.java b/src/com/android/settings/notification/ZenAutomaticRuleSwitchPreferenceController.java
index a684d3e..9294a90 100644
--- a/src/com/android/settings/notification/ZenAutomaticRuleSwitchPreferenceController.java
+++ b/src/com/android/settings/notification/ZenAutomaticRuleSwitchPreferenceController.java
@@ -22,7 +22,6 @@
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 import android.widget.Switch;
-import android.widget.Toast;
 
 import com.android.settings.R;
 import com.android.settings.applications.LayoutPreference;
@@ -36,14 +35,11 @@
     private static final String KEY = "zen_automatic_rule_switch";
     private AutomaticZenRule mRule;
     private String mId;
-    private Toast mEnabledToast;
-    private int mToastTextResource;
     private SwitchBar mSwitchBar;
 
     public ZenAutomaticRuleSwitchPreferenceController(Context context, Fragment parent,
-            int toastTextResource, Lifecycle lifecycle) {
+            Lifecycle lifecycle) {
         super(context, KEY, parent, lifecycle);
-        mToastTextResource = toastTextResource;
     }
 
     @Override
@@ -92,16 +88,5 @@
         if (enabled == mRule.isEnabled()) return;
         mRule.setEnabled(enabled);
         mBackend.setZenRule(mId, mRule);
-        if (enabled) {
-            final int toastText = mToastTextResource;
-            if (toastText != 0) {
-                mEnabledToast = Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT);
-                mEnabledToast.show();
-            }
-        } else {
-            if (mEnabledToast != null) {
-                mEnabledToast.cancel();
-            }
-        }
     }
 }
diff --git a/src/com/android/settings/notification/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/ZenModeButtonPreferenceController.java
index da540f4..d9c9691 100644
--- a/src/com/android/settings/notification/ZenModeButtonPreferenceController.java
+++ b/src/com/android/settings/notification/ZenModeButtonPreferenceController.java
@@ -62,7 +62,7 @@
             mZenButtonOn = (Button) ((LayoutPreference) preference)
                     .findViewById(R.id.zen_mode_settings_turn_on_button);
             mZenButtonOn.setOnClickListener(v ->
-                    new EnableZenModeDialog().show(mFragment, TAG));
+                    new SettingsEnableZenModeDialog().show(mFragment, TAG));
         }
 
         if (null == mZenButtonOff) {
diff --git a/src/com/android/settings/notification/ZenModeEventRuleSettings.java b/src/com/android/settings/notification/ZenModeEventRuleSettings.java
index bb66768..cf6d7b5 100644
--- a/src/com/android/settings/notification/ZenModeEventRuleSettings.java
+++ b/src/com/android/settings/notification/ZenModeEventRuleSettings.java
@@ -82,8 +82,7 @@
         List<AbstractPreferenceController> controllers = new ArrayList<>();
         mHeader = new ZenAutomaticRuleHeaderPreferenceController(context, this,
                 getLifecycle());
-        mSwitch = new ZenAutomaticRuleSwitchPreferenceController(context, this,
-                R.string.zen_event_rule_enabled_toast, getLifecycle());
+        mSwitch = new ZenAutomaticRuleSwitchPreferenceController(context, this, getLifecycle());
         controllers.add(mHeader);
         controllers.add(mSwitch);
         return controllers;
diff --git a/src/com/android/settings/notification/ZenModePreferenceController.java b/src/com/android/settings/notification/ZenModePreferenceController.java
index e11b422..aafda11 100644
--- a/src/com/android/settings/notification/ZenModePreferenceController.java
+++ b/src/com/android/settings/notification/ZenModePreferenceController.java
@@ -16,18 +16,56 @@
 
 package com.android.settings.notification;
 
+import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.Slog;
 
-public class ZenModePreferenceController extends AdjustVolumeRestrictedPreferenceController {
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+public class ZenModePreferenceController extends AdjustVolumeRestrictedPreferenceController
+        implements LifecycleObserver, OnResume, OnPause {
 
     private static final String KEY_ZEN_MODE = "zen_mode";
-
+    private SettingObserver mSettingObserver;
     private ZenModeSettings.SummaryBuilder mSummaryBuilder;
 
-    public ZenModePreferenceController(Context context) {
+    public ZenModePreferenceController(Context context, Lifecycle lifecycle) {
         super(context);
         mSummaryBuilder = new ZenModeSettings.SummaryBuilder(context);
+
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mSettingObserver = new SettingObserver(screen.findPreference(KEY_ZEN_MODE));
+    }
+
+    @Override
+    public void onResume() {
+        if (mSettingObserver != null) {
+            mSettingObserver.register(mContext.getContentResolver());
+        }
+    }
+
+    @Override
+    public void onPause() {
+        if (mSettingObserver != null) {
+            mSettingObserver.unregister(mContext.getContentResolver());
+        }
     }
 
     @Override
@@ -44,7 +82,41 @@
     public void updateState(Preference preference) {
         super.updateState(preference);
         if (preference.isEnabled()) {
-            preference.setSummary(mSummaryBuilder.getAutomaticRulesSummary());
+            preference.setSummary(mSummaryBuilder.getSoundSummary());
+        }
+    }
+
+    class SettingObserver extends ContentObserver {
+        private final Uri ZEN_MODE_URI = Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
+        private final Uri ZEN_MODE_CONFIG_ETAG_URI = Settings.Global.getUriFor(
+                Settings.Global.ZEN_MODE_CONFIG_ETAG);
+
+        private final Preference mPreference;
+
+        public SettingObserver(Preference preference) {
+            super(new Handler());
+            mPreference = preference;
+        }
+
+        public void register(ContentResolver cr) {
+            cr.registerContentObserver(ZEN_MODE_URI, false, this, UserHandle.USER_ALL);
+            cr.registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, this, UserHandle.USER_ALL);
+        }
+
+        public void unregister(ContentResolver cr) {
+            cr.unregisterContentObserver(this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            super.onChange(selfChange, uri);
+            if (ZEN_MODE_URI.equals(uri)) {
+                updateState(mPreference);
+            }
+
+            if (ZEN_MODE_CONFIG_ETAG_URI.equals(uri)) {
+                updateState(mPreference);
+            }
         }
     }
 }
diff --git a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
index ecfe91b..9ef1128 100644
--- a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
+++ b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
@@ -200,8 +200,7 @@
         List<AbstractPreferenceController> controllers = new ArrayList<>();
         mHeader = new ZenAutomaticRuleHeaderPreferenceController(context, this,
                 getLifecycle());
-        mSwitch = new ZenAutomaticRuleSwitchPreferenceController(context, this,
-                R.string.zen_schedule_rule_enabled_toast, getLifecycle());
+        mSwitch = new ZenAutomaticRuleSwitchPreferenceController(context, this, getLifecycle());
 
         controllers.add(mHeader);
         controllers.add(mSwitch);
diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java
index a6145c4..2b48301 100644
--- a/src/com/android/settings/notification/ZenModeSettings.java
+++ b/src/com/android/settings/notification/ZenModeSettings.java
@@ -115,6 +115,26 @@
             return mContext.getString(R.string.zen_mode_behavior_summary_custom);
         }
 
+        String getSoundSummary() {
+            int zenMode = NotificationManager.from(mContext).getZenMode();
+
+            if (zenMode != Settings.Global.ZEN_MODE_OFF) {
+                Policy policy = NotificationManager.from(mContext).getNotificationPolicy();
+                return mContext.getString(R.string.zen_mode_sound_summary_on,
+                        getBehaviorSettingSummary(policy, zenMode));
+            } else {
+                final int count = getEnabledAutomaticRulesCount();
+                if (count > 0) {
+                    return mContext.getString(R.string.zen_mode_sound_summary_off_with_info,
+                            mContext.getResources().getQuantityString(
+                                    R.plurals.zen_mode_sound_summary_summary_off_info,
+                                    count, count));
+                }
+
+                return mContext.getString(R.string.zen_mode_sound_summary_off);
+            }
+        }
+
         String getAutomaticRulesSummary() {
             final int count = getEnabledAutomaticRulesCount();
             return count == 0 ? mContext.getString(R.string.zen_mode_settings_summary_off)
diff --git a/src/com/android/settings/notification/ZenRuleSelectionDialog.java b/src/com/android/settings/notification/ZenRuleSelectionDialog.java
index 0784d5a..9beac7c 100644
--- a/src/com/android/settings/notification/ZenRuleSelectionDialog.java
+++ b/src/com/android/settings/notification/ZenRuleSelectionDialog.java
@@ -119,13 +119,21 @@
             final LinearLayout v = (LinearLayout) LayoutInflater.from(mContext).inflate(
                     R.layout.zen_rule_type, null, false);
 
-            LoadIconTask task = new LoadIconTask((ImageView) v.findViewById(R.id.icon));
-            task.execute(info);
+            ImageView iconView = v.findViewById(R.id.icon);
             ((TextView) v.findViewById(R.id.title)).setText(ri.title);
             if (!ri.isSystem) {
+                LoadIconTask task = new LoadIconTask(iconView);
+                task.execute(info);
+
                 TextView subtitle = (TextView) v.findViewById(R.id.subtitle);
                 subtitle.setText(info.loadLabel(mPm));
                 subtitle.setVisibility(View.VISIBLE);
+            } else {
+                if (ZenModeConfig.isValidScheduleConditionId(ri.defaultConditionId)) {
+                    iconView.setImageDrawable(mContext.getDrawable(R.drawable.ic_timelapse));
+                } else if (ZenModeConfig.isValidEventConditionId(ri.defaultConditionId)) {
+                    iconView.setImageDrawable(mContext.getDrawable(R.drawable.ic_event));
+                }
             }
             v.setOnClickListener(new View.OnClickListener() {
                 @Override
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index 11efec3..a9c5c03 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -64,7 +64,7 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.SetupWizardUtils;
 import com.android.settings.Utils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.notification.RedactionInterstitial;
 import com.android.settings.widget.ImeAwareEditText;
 import com.android.setupwizardlib.GlifLayout;
@@ -168,7 +168,7 @@
         layout.setFitsSystemWindows(false);
     }
 
-    public static class ChooseLockPasswordFragment extends InstrumentedPreferenceFragment
+    public static class ChooseLockPasswordFragment extends InstrumentedFragment
             implements OnClickListener, OnEditorActionListener, TextWatcher,
             SaveAndFinishWorker.Listener {
         private static final String KEY_FIRST_PIN = "first_pin";
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index 972fac8..0df1a11 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -43,7 +43,7 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.SetupWizardUtils;
 import com.android.settings.Utils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.notification.RedactionInterstitial;
 import com.android.setupwizardlib.GlifLayout;
 
@@ -153,7 +153,7 @@
         return super.onKeyDown(keyCode, event);
     }
 
-    public static class ChooseLockPatternFragment extends InstrumentedPreferenceFragment
+    public static class ChooseLockPatternFragment extends InstrumentedFragment
             implements View.OnClickListener, SaveAndFinishWorker.Listener {
 
         public static final int CONFIRM_EXISTING_REQUEST = 55;
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
index 5b18925..3f26750 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
@@ -52,13 +52,13 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.Utils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.fingerprint.FingerprintUiHelper;
 
 /**
  * Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
  */
-public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedPreferenceFragment
+public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFragment
         implements FingerprintUiHelper.Callback {
 
     public static final String PACKAGE = "com.android.settings";
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index 20182cb..dbca2fc 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -114,11 +114,6 @@
         }
 
         @Override
-        public void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-        }
-
-        @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
             final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index 1e88150..c87ff43 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -105,11 +105,6 @@
         }
 
         @Override
-        public void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-        }
-
-        @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
             ConfirmLockPattern activity = (ConfirmLockPattern) getActivity();
diff --git a/src/com/android/settings/search/SearchIndexableResourcesImpl.java b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
index 1edc2de..bcf412c 100644
--- a/src/com/android/settings/search/SearchIndexableResourcesImpl.java
+++ b/src/com/android/settings/search/SearchIndexableResourcesImpl.java
@@ -26,6 +26,7 @@
 import com.android.settings.accessibility.AccessibilitySettings;
 import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
 import com.android.settings.accessibility.MagnificationPreferenceFragment;
+import com.android.settings.accessibility.VibrationSettings;
 import com.android.settings.accounts.AccountDashboardFragment;
 import com.android.settings.applications.AppAndNotificationDashboardFragment;
 import com.android.settings.applications.DefaultAppSettings;
@@ -176,6 +177,7 @@
         addIndex(NightDisplaySettings.class);
         addIndex(SmartBatterySettings.class);
         addIndex(MyDeviceInfoFragment.class);
+        addIndex(VibrationSettings.class);
     }
 
     @Override
diff --git a/src/com/android/settings/security/CryptKeeperSettings.java b/src/com/android/settings/security/CryptKeeperSettings.java
index 7d5ee9d..64f5abb 100644
--- a/src/com/android/settings/security/CryptKeeperSettings.java
+++ b/src/com/android/settings/security/CryptKeeperSettings.java
@@ -157,6 +157,7 @@
                 }
             }
         }
+        activity.setTitle(R.string.crypt_keeper_encrypt_title);
     }
 
     /**
diff --git a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
index 11f1f59..826ed42 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
@@ -39,13 +39,12 @@
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
 
-import java.util.List;
-
 public class WifiTetherPreferenceController extends AbstractPreferenceController
         implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
 
-    public static final IntentFilter WIFI_TETHER_INTENT_FILTER;
     private static final String WIFI_TETHER_SETTINGS = "wifi_tether";
+    private static final IntentFilter AIRPLANE_INTENT_FILTER = new IntentFilter(
+            Intent.ACTION_AIRPLANE_MODE_CHANGED);
 
     private final ConnectivityManager mConnectivityManager;
     private final String[] mWifiRegexs;
@@ -58,12 +57,6 @@
     @VisibleForTesting
     WifiTetherSoftApManager mWifiTetherSoftApManager;
 
-    static {
-        WIFI_TETHER_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
-        WIFI_TETHER_INTENT_FILTER.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
-        WIFI_TETHER_INTENT_FILTER.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-    }
-
     public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) {
         this(context, lifecycle, true /* initSoftApManager */);
     }
@@ -113,7 +106,7 @@
     @Override
     public void onStart() {
         if (mPreference != null) {
-            mContext.registerReceiver(mReceiver, WIFI_TETHER_INTENT_FILTER);
+            mContext.registerReceiver(mReceiver, AIRPLANE_INTENT_FILTER);
             clearSummaryForAirplaneMode();
             if (mWifiTetherSoftApManager != null) {
                 mWifiTetherSoftApManager.registerSoftApCallback();
@@ -140,6 +133,7 @@
                     @Override
                     public void onStateChanged(int state, int failureReason) {
                         mSoftApState = state;
+                        handleWifiApStateChanged(state, failureReason);
                     }
 
                     @Override
@@ -162,34 +156,21 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) {
-                int state = intent.getIntExtra(
-                        WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
-                int reason = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON,
-                        WifiManager.SAP_START_FAILURE_GENERAL);
-                handleWifiApStateChanged(state, reason);
-            } else if (ConnectivityManager.ACTION_TETHER_STATE_CHANGED.equals(action)) {
-                List<String> active = intent.getStringArrayListExtra(
-                        ConnectivityManager.EXTRA_ACTIVE_TETHER);
-                List<String> errored = intent.getStringArrayListExtra(
-                        ConnectivityManager.EXTRA_ERRORED_TETHER);
-                updateTetherState(active.toArray(), errored.toArray());
-            } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
+            if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
                 clearSummaryForAirplaneMode();
             }
         }
     };
 
-    private void handleWifiApStateChanged(int state, int reason) {
+    @VisibleForTesting
+    void handleWifiApStateChanged(int state, int reason) {
         switch (state) {
             case WifiManager.WIFI_AP_STATE_ENABLING:
                 mPreference.setSummary(R.string.wifi_tether_starting);
                 break;
             case WifiManager.WIFI_AP_STATE_ENABLED:
-                /**
-                 * Summary on enable is handled by tether
-                 * broadcast notice
-                 */
+                WifiConfiguration wifiConfig = mWifiManager.getWifiApConfiguration();
+                updateConfigSummary(wifiConfig);
                 break;
             case WifiManager.WIFI_AP_STATE_DISABLING:
                 mPreference.setSummary(R.string.wifi_tether_stopping);
@@ -208,32 +189,6 @@
         }
     }
 
-    private void updateTetherState(Object[] tethered, Object[] errored) {
-        boolean wifiTethered = matchRegex(tethered);
-        boolean wifiErrored = matchRegex(errored);
-
-        if (wifiTethered) {
-            WifiConfiguration wifiConfig = mWifiManager.getWifiApConfiguration();
-            updateConfigSummary(wifiConfig);
-        } else if (wifiErrored) {
-            mPreference.setSummary(R.string.wifi_error);
-        } else {
-            mPreference.setSummary(R.string.wifi_hotspot_off_subtext);
-        }
-    }
-
-    private boolean matchRegex(Object[] tethers) {
-        for (Object o : tethers) {
-            String s = (String) o;
-            for (String regex : mWifiRegexs) {
-                if (s.matches(regex)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     private void updateConfigSummary(WifiConfiguration wifiConfig) {
         final String s = mContext.getString(
                 com.android.internal.R.string.wifi_tether_configure_ssid_default);
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
index 627bf32..699e5ae 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
@@ -21,6 +21,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
@@ -36,12 +37,19 @@
 public class WifiTetherSwitchBarController implements SwitchWidgetController.OnSwitchChangeListener,
         LifecycleObserver, OnStart, OnStop {
 
+    private static final IntentFilter WIFI_INTENT_FILTER;
+
     private final Context mContext;
     private final SwitchWidgetController mSwitchBar;
     private final ConnectivityManager mConnectivityManager;
     private final DataSaverBackend mDataSaverBackend;
     private final WifiManager mWifiManager;
 
+    static {
+        WIFI_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+        WIFI_INTENT_FILTER.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+    }
+
     WifiTetherSwitchBarController(Context context, SwitchWidgetController switchBar) {
         mContext = context;
         mSwitchBar = switchBar;
@@ -56,8 +64,7 @@
     @Override
     public void onStart() {
         mSwitchBar.startListening();
-        mContext.registerReceiver(mReceiver,
-                WifiTetherPreferenceController.WIFI_TETHER_INTENT_FILTER);
+        mContext.registerReceiver(mReceiver, WIFI_INTENT_FILTER);
     }
 
     @Override
diff --git a/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceFragmentTest.java
new file mode 100644
index 0000000..f6a5a82
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/VibrationPreferenceFragmentTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 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.accessibility;
+
+import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_OFF;
+import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_LOW;
+import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_MEDIUM;
+import static com.android.settings.accessibility.VibrationPreferenceFragment.KEY_INTENSITY_HIGH;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.UserManager;
+import android.os.Vibrator;
+import android.provider.Settings;
+
+import com.android.settings.TestConfig;
+import com.android.settings.accessibility.VibrationPreferenceFragment.VibrationIntensityCandidateInfo;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.widget.RadioButtonPickerFragment.CandidateInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class VibrationPreferenceFragmentTest {
+    public static final Map<Integer, String> INTENSITY_TO_KEY = new HashMap<>();
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Activity mActivity;
+    @Mock
+    private UserManager mUserManager;
+
+    private Context mContext;
+    private TestVibrationPreferenceFragment mFragment;
+
+    static {
+        INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_OFF, KEY_INTENSITY_OFF);
+        INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_LOW, KEY_INTENSITY_LOW);
+        INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_MEDIUM, KEY_INTENSITY_MEDIUM);
+        INTENSITY_TO_KEY.put(Vibrator.VIBRATION_INTENSITY_HIGH, KEY_INTENSITY_HIGH);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        FakeFeatureFactory.setupForTest();
+
+        mContext = RuntimeEnvironment.application;
+
+        mFragment = spy(new TestVibrationPreferenceFragment());
+        doReturn(mUserManager).when(mActivity).getSystemService(Context.USER_SERVICE);
+        doReturn(mContext).when(mFragment).getContext();
+        mFragment.onAttach(mActivity);
+    }
+
+    @Test
+    public void changeIntensitySetting_shouldResultInCorrespondingKey() {
+        for (Map.Entry<Integer, String> entry : INTENSITY_TO_KEY.entrySet()) {
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.HAPTIC_FEEDBACK_INTENSITY, entry.getKey());
+            assertThat(mFragment.getDefaultKey()).isEqualTo(entry.getValue());
+        }
+    }
+
+    @Test
+    public void initialDefaultKey_shouldBeMedium() {
+        assertThat(mFragment.getDefaultKey()).isEqualTo(KEY_INTENSITY_MEDIUM);
+    }
+
+    @Test
+    public void candidates_shouldBeSortedByIntensity() {
+        final List<? extends CandidateInfo> candidates = mFragment.getCandidates();
+        assertThat(candidates.size()).isEqualTo(INTENSITY_TO_KEY.size());
+        VibrationIntensityCandidateInfo prevCandidate =
+                (VibrationIntensityCandidateInfo) candidates.get(0);
+        for (int i = 1; i < candidates.size(); i++) {
+            VibrationIntensityCandidateInfo candidate =
+                    (VibrationIntensityCandidateInfo) candidates.get(i);
+            assertThat(candidate.getIntensity()).isLessThan(prevCandidate.getIntensity());
+        }
+    }
+
+    private class TestVibrationPreferenceFragment extends VibrationPreferenceFragment {
+        @Override
+        protected int getPreferenceScreenResId() {
+            return 0;
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return 0;
+        }
+
+        /**
+        * Get the setting string of the vibration intensity setting this preference is dealing with.
+        */
+        @Override
+        protected String getVibrationIntensitySetting() {
+            return Settings.System.HAPTIC_FEEDBACK_INTENSITY;
+        }
+
+        @Override
+        protected int getDefaultVibrationIntensity() {
+            return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+        }
+    }
+}
+
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java
index a1c8d67e..d9c709e 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java
@@ -67,6 +67,8 @@
     private ConditionManager mConditionManager;
     @Mock
     private SummaryLoader mSummaryLoader;
+    @Mock
+    private SuggestionControllerMixin mSuggestionControllerMixin;
 
     private Context mContext;
     private DashboardSummary mSummary;
@@ -111,12 +113,31 @@
 
     @Test
     public void updateCategory_shouldGetCategoryFromFeatureProvider() {
+        ReflectionHelpers.setField(mSummary, "mSuggestionControllerMixin",
+                mSuggestionControllerMixin);
+
+        when(mSuggestionControllerMixin.isSuggestionLoaded()).thenReturn(true);
         doReturn(mock(Activity.class)).when(mSummary).getActivity();
         mSummary.onAttach(mContext);
         mSummary.updateCategory();
 
         verify(mSummaryLoader).updateSummaryToCache(nullable(DashboardCategory.class));
         verify(mDashboardFeatureProvider).getTilesForCategory(CategoryKey.CATEGORY_HOMEPAGE);
+        verify(mAdapter).setCategory(any());
+    }
+
+    @Test
+    public void updateCategory_shouldGetCategoryFromFeatureProvider_evenIfSuggestionDisabled() {
+        when(mFeatureFactory.suggestionsFeatureProvider.isSuggestionEnabled(any(Context.class)))
+                .thenReturn(false);
+
+        doReturn(mock(Activity.class)).when(mSummary).getActivity();
+        mSummary.onAttach(mContext);
+        mSummary.updateCategory();
+
+        verify(mSummaryLoader).updateSummaryToCache(nullable(DashboardCategory.class));
+        verify(mDashboardFeatureProvider).getTilesForCategory(CategoryKey.CATEGORY_HOMEPAGE);
+        verify(mAdapter).setCategory(any());
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
index 30fdccb..0cfe135 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
@@ -142,18 +142,17 @@
 
         mController.handlePreferenceTreeClick(mPreference);
 
-        verify(mController).showDialog();
+        verify(mController).showDialog(false /* restrict */);
     }
 
     @Test
-    public void testHandlePreferenceTreeClick_unRestrictApp_setModeAllowed() {
+    public void testHandlePreferenceTreeClick_unRestrictApp_showDialog() {
         doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager).checkOpNoThrow(anyInt(),
                 anyInt(), anyString());
 
         mController.handlePreferenceTreeClick(mPreference);
 
-        verify(mBatteryUtils).setForceAppStandby(UID_LOW_SDK, LOW_SDK_PACKAGE,
-                AppOpsManager.MODE_ALLOWED);
+        verify(mController).showDialog(true /* restrict */);
     }
 
     @Test
@@ -211,17 +210,4 @@
     public void testIsAvailable_ReturnTrue() {
         assertThat(mController.isAvailable()).isTrue();
     }
-
-    @Test
-    public void testWarningDialog() {
-        BackgroundActivityPreferenceController.WarningDialogFragment dialogFragment =
-                new BackgroundActivityPreferenceController.WarningDialogFragment();
-        dialogFragment.setTargetFragment(mFragment, 0);
-        FragmentTestUtil.startFragment(dialogFragment);
-        final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
-        ShadowAlertDialog shadowDialog = shadowOf(dialog);
-        final Button okButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
-        shadowDialog.clickOn(okButton.getId());
-        verify(mFragment).onLimitBackgroundActivity();
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index fe90751..6bc6ee7 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -523,4 +524,27 @@
     public void testIsLegacyApp_SdkLargerOrEqualThanO_ReturnFalse() {
         assertThat(mBatteryUtils.isLegacyApp(HIGH_SDK_PACKAGE)).isFalse();
     }
+
+    @Test
+    public void testSetForceAppStandby_forcePreOApp_forceTwoRestrictions() {
+        mBatteryUtils.setForceAppStandby(UID, LOW_SDK_PACKAGE, AppOpsManager.MODE_IGNORED);
+
+        // Restrict both OP_RUN_IN_BACKGROUND and OP_RUN_ANY_IN_BACKGROUND
+        verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID, LOW_SDK_PACKAGE,
+                AppOpsManager.MODE_IGNORED);
+        verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID, LOW_SDK_PACKAGE,
+                AppOpsManager.MODE_IGNORED);
+    }
+
+    @Test
+    public void testSetForceAppStandby_forceOApp_forceOneRestriction() {
+        mBatteryUtils.setForceAppStandby(UID, HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED);
+
+        // Don't restrict OP_RUN_IN_BACKGROUND because it is already been restricted for O app
+        verify(mAppOpsManager, never()).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID,
+                HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED);
+        // Restrict OP_RUN_ANY_IN_BACKGROUND
+        verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID,
+                HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
index ddee314..ec72384 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
@@ -27,10 +27,13 @@
 
 import com.android.settings.R;
 import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.testutils.shadow.ShadowRuntimePermissionPresenter;
+import com.android.settings.testutils.shadow.ShadowUtils;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -47,14 +50,18 @@
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
-        shadows = ShadowRuntimePermissionPresenter.class)
+        shadows = ShadowUtils.class)
 public class BatteryTipDialogFragmentTest {
     private static final String PACKAGE_NAME = "com.android.app";
+    private static final String DISPLAY_NAME = "app";
     private static final long SCREEN_TIME_MS = DateUtils.HOUR_IN_MILLIS;
 
     private BatteryTipDialogFragment mDialogFragment;
     private Context mContext;
     private HighUsageTip mHighUsageTip;
+    private RestrictAppTip mRestrictedOneAppTip;
+    private RestrictAppTip mRestrictAppsTip;
+    private UnrestrictAppTip mUnrestrictAppTip;
 
     @Before
     public void setUp() {
@@ -64,9 +71,22 @@
         FakeFeatureFactory.setupForTest();
 
         List<AppInfo> highUsageTips = new ArrayList<>();
-        highUsageTips.add(new AppInfo.Builder().setScreenOnTimeMs(SCREEN_TIME_MS).setPackageName(
-                PACKAGE_NAME).build());
+        final AppInfo appInfo = new AppInfo.Builder()
+                .setScreenOnTimeMs(SCREEN_TIME_MS)
+                .setPackageName(PACKAGE_NAME)
+                .build();
+        highUsageTips.add(appInfo);
         mHighUsageTip = new HighUsageTip(SCREEN_TIME_MS, highUsageTips);
+
+        final List<AppInfo> restrictApps = new ArrayList<>();
+        restrictApps.add(appInfo);
+        mRestrictedOneAppTip = new RestrictAppTip(BatteryTip.StateType.NEW,
+                new ArrayList<>(restrictApps));
+        restrictApps.add(appInfo);
+        mRestrictAppsTip = new RestrictAppTip(BatteryTip.StateType.NEW,
+                new ArrayList<>(restrictApps));
+
+        mUnrestrictAppTip = new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo);
     }
 
     @Test
@@ -82,5 +102,49 @@
                 mContext.getString(R.string.battery_tip_dialog_message, "1h"));
     }
 
+    @Test
+    public void testOnCreateDialog_restrictOneAppTip_fireRestrictOneAppDialog() {
+        mDialogFragment = BatteryTipDialogFragment.newInstance(mRestrictedOneAppTip);
+
+        FragmentTestUtil.startFragment(mDialogFragment);
+
+        final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
+        ShadowAlertDialog shadowDialog = shadowOf(dialog);
+
+        assertThat(shadowDialog.getTitle()).isEqualTo("Restrict app?");
+        assertThat(shadowDialog.getMessage()).isEqualTo(
+                mContext.getString(R.string.battery_tip_restrict_app_dialog_message));
+    }
+
+    @Test
+    public void testOnCreateDialog_restrictAppsTip_fireRestrictAppsDialog() {
+        mDialogFragment = BatteryTipDialogFragment.newInstance(mRestrictAppsTip);
+
+        FragmentTestUtil.startFragment(mDialogFragment);
+
+        final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
+        ShadowAlertDialog shadowDialog = shadowOf(dialog);
+
+        assertThat(shadowDialog.getTitle()).isEqualTo("Restrict 2 apps?");
+        assertThat(shadowDialog.getMessage()).isEqualTo(
+                mContext.getString(R.string.battery_tip_restrict_app_dialog_message));
+        assertThat(shadowDialog.getView()).isNotNull();
+    }
+
+    @Test
+    public void testOnCreateDialog_unRestrictAppTip_fireUnRestrictDialog() {
+        mDialogFragment = BatteryTipDialogFragment.newInstance(mUnrestrictAppTip);
+        ShadowUtils.setApplicationLabel(PACKAGE_NAME, DISPLAY_NAME);
+
+        FragmentTestUtil.startFragment(mDialogFragment);
+
+        final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
+        ShadowAlertDialog shadowDialog = shadowOf(dialog);
+
+        assertThat(shadowDialog.getTitle()).isEqualTo("Remove restriction for app?");
+        assertThat(shadowDialog.getMessage()).isEqualTo(
+                mContext.getString(R.string.battery_tip_unrestrict_app_dialog_message));
+    }
+
 
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
index 83b3225..09e67ed 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -51,6 +51,7 @@
 public class BatteryTipLoaderTest {
     private static final int[] TIP_ORDER = {
             BatteryTip.TipType.SMART_BATTERY_MANAGER,
+            BatteryTip.TipType.APP_RESTRICTION,
             BatteryTip.TipType.HIGH_DEVICE_USAGE,
             BatteryTip.TipType.BATTERY_SAVER,
             BatteryTip.TipType.LOW_BATTERY,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java
new file mode 100644
index 0000000..47785d5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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.fuelgauge.batterytip.actions;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class RestrictAppActionTest {
+    private static final String PACKAGE_NAME_1 = "com.android.app1";
+    private static final String PACKAGE_NAME_2 = "com.android.app2";
+
+    @Mock
+    private BatteryUtils mBatteryUtils;
+    private Context mContext;
+    private RestrictAppAction mRestrictAppAction;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        final List<AppInfo> mAppInfos = new ArrayList<>();
+        mAppInfos.add(new AppInfo.Builder()
+                .setPackageName(PACKAGE_NAME_1)
+                .build());
+        mAppInfos.add(new AppInfo.Builder()
+                .setPackageName(PACKAGE_NAME_2)
+                .build());
+
+        mRestrictAppAction = new RestrictAppAction(mContext, new RestrictAppTip(
+                BatteryTip.StateType.NEW, mAppInfos));
+        mRestrictAppAction.mBatteryUtils = mBatteryUtils;
+    }
+
+    @Test
+    public void testHandlePositiveAction() {
+        mRestrictAppAction.handlePositiveAction();
+
+        verify(mBatteryUtils).setForceAppStandby(anyInt(), eq(PACKAGE_NAME_1),
+                eq(AppOpsManager.MODE_IGNORED));
+        verify(mBatteryUtils).setForceAppStandby(anyInt(), eq(PACKAGE_NAME_2),
+                eq(AppOpsManager.MODE_IGNORED));
+    }
+
+
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
new file mode 100644
index 0000000..e1dea17
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 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.fuelgauge.batterytip.tips;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Parcel;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class RestrictAppTipTest {
+    private static final String PACKAGE_NAME = "com.android.app";
+    private static final String DISPLAY_NAME = "app";
+
+    private Context mContext;
+    private RestrictAppTip mNewBatteryTip;
+    private RestrictAppTip mHandledBatteryTip;
+    private List<AppInfo> mUsageAppList;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
+    @Mock
+    private PackageManager mPackageManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(RuntimeEnvironment.application);
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(PACKAGE_NAME,
+                PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_ANY_USER);
+        doReturn(DISPLAY_NAME).when(mApplicationInfo).loadLabel(mPackageManager);
+
+        mUsageAppList = new ArrayList<>();
+        mUsageAppList.add(new AppInfo.Builder()
+                .setPackageName(PACKAGE_NAME)
+                .build());
+        mNewBatteryTip = new RestrictAppTip(BatteryTip.StateType.NEW, mUsageAppList);
+        mHandledBatteryTip = new RestrictAppTip(BatteryTip.StateType.HANDLED, mUsageAppList);
+    }
+
+    @Test
+    public void testParcelable() {
+        Parcel parcel = Parcel.obtain();
+        mNewBatteryTip.writeToParcel(parcel, mNewBatteryTip.describeContents());
+        parcel.setDataPosition(0);
+
+        final RestrictAppTip parcelTip = new RestrictAppTip(parcel);
+
+        assertThat(parcelTip.getType()).isEqualTo(BatteryTip.TipType.APP_RESTRICTION);
+        assertThat(parcelTip.getState()).isEqualTo(BatteryTip.StateType.NEW);
+        final AppInfo app = parcelTip.getRestrictAppList().get(0);
+        assertThat(app.packageName).isEqualTo(PACKAGE_NAME);
+    }
+
+    @Test
+    public void testGetTitle_stateNew_showRestrictTitle() {
+        assertThat(mNewBatteryTip.getTitle(mContext)).isEqualTo("Restrict 1 app");
+    }
+
+    @Test
+    public void testGetTitle_stateHandled_showHandledTitle() {
+        assertThat(mHandledBatteryTip.getTitle(mContext)).isEqualTo("1 recently restricted");
+    }
+
+    @Test
+    public void testGetSummary_stateNew_showRestrictSummary() {
+        assertThat(mNewBatteryTip.getSummary(mContext)).isEqualTo(
+                "app has high battery usage");
+    }
+
+    @Test
+    public void testGetSummary_stateHandled_showHandledSummary() {
+        assertThat(mHandledBatteryTip.getSummary(mContext)).isEqualTo(
+                "App changes are in progress");
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTipTest.java
new file mode 100644
index 0000000..a83a158
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTipTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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.fuelgauge.batterytip.tips;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class UnrestrictAppTipTest {
+    private static final String PACKAGE_NAME = "com.android.app";
+
+    private UnrestrictAppTip mBatteryTip;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        AppInfo appInfo = new AppInfo.Builder()
+                .setPackageName(PACKAGE_NAME)
+                .build();
+        mBatteryTip = new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo);
+    }
+
+    @Test
+    public void testParcelable() {
+        Parcel parcel = Parcel.obtain();
+        mBatteryTip.writeToParcel(parcel, mBatteryTip.describeContents());
+        parcel.setDataPosition(0);
+
+        final UnrestrictAppTip parcelTip = new UnrestrictAppTip(parcel);
+
+        assertThat(parcelTip.getType()).isEqualTo(BatteryTip.TipType.REMOVE_APP_RESTRICTION);
+        assertThat(parcelTip.getState()).isEqualTo(BatteryTip.StateType.NEW);
+        assertThat(parcelTip.getPackageName()).isEqualTo(PACKAGE_NAME);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/location/LocationFooterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/LocationFooterPreferenceControllerTest.java
new file mode 100644
index 0000000..da00010
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/location/LocationFooterPreferenceControllerTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2018 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.location;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.arch.lifecycle.LifecycleOwner;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+/** Unit tests for {@link LocationFooterPreferenceController} */
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class LocationFooterPreferenceControllerTest {
+
+    @Mock
+    private PreferenceCategory mPreferenceCategory;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private Resources mResources;
+    private Context mContext;
+    private LocationFooterPreferenceController mController;
+    private LifecycleOwner mLifecycleOwner;
+    private Lifecycle mLifecycle;
+    private static final int TEST_RES_ID = 1234;
+    private static final String TEST_TEXT = "text";
+
+    @Before
+    public void setUp() throws NameNotFoundException {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        mLifecycleOwner = () -> mLifecycle;
+        mLifecycle = new Lifecycle(mLifecycleOwner);
+        when(mPreferenceCategory.getContext()).thenReturn(mContext);
+        mController = spy(new LocationFooterPreferenceController(mContext, mLifecycle));
+        when(mPackageManager.getResourcesForApplication(any(ApplicationInfo.class)))
+                .thenReturn(mResources);
+        when(mResources.getString(TEST_RES_ID)).thenReturn(TEST_TEXT);
+        doNothing().when(mPreferenceCategory).removeAll();
+    }
+
+    @Test
+    public void isAvailable_hasValidFooter_returnsTrue() throws NameNotFoundException {
+        final List<ResolveInfo> testResolveInfos = new ArrayList<>();
+        testResolveInfos.add(
+                getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
+        when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+                .thenReturn(testResolveInfos);
+
+        assertThat(mController.isAvailable()).isTrue();
+    }
+
+    @Test
+    public void isAvailable_noSystemApp_returnsFalse() throws NameNotFoundException {
+        final List<ResolveInfo> testResolveInfos = new ArrayList<>();
+        testResolveInfos.add(
+                getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true));
+        when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+                .thenReturn(testResolveInfos);
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void isAvailable_noRequiredMetadata_returnsFalse() throws NameNotFoundException {
+        final List<ResolveInfo> testResolveInfos = new ArrayList<>();
+        testResolveInfos.add(
+                getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ false));
+        when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+                .thenReturn(testResolveInfos);
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void sendBroadcastFooterInject() {
+        ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+        final ActivityInfo activityInfo =
+                getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true).activityInfo;
+        mController.sendBroadcastFooterDisplayed(
+                new ComponentName(activityInfo.packageName, activityInfo.name));
+        verify(mContext).sendBroadcast(intent.capture());
+        assertThat(intent.getValue().getAction())
+                .isEqualTo(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
+    }
+
+    @Test
+    public void updateState_sendBroadcast() throws NameNotFoundException {
+        final List<ResolveInfo> testResolveInfos = new ArrayList<>();
+        testResolveInfos.add(
+                getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
+        when(mPackageManager.queryBroadcastReceivers(any(), anyInt()))
+                .thenReturn(testResolveInfos);
+        mController.updateState(mPreferenceCategory);
+        ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).sendBroadcast(intent.capture());
+        assertThat(intent.getValue().getAction())
+                .isEqualTo(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
+    }
+
+    @Test
+    public void updateState_addPreferences() throws NameNotFoundException {
+        final List<ResolveInfo> testResolveInfos = new ArrayList<>();
+        testResolveInfos.add(
+                getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
+        when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+                .thenReturn(testResolveInfos);
+        mController.updateState(mPreferenceCategory);
+        ArgumentCaptor<Preference> pref = ArgumentCaptor.forClass(Preference.class);
+        verify(mPreferenceCategory).addPreference(pref.capture());
+        assertThat(pref.getValue().getTitle()).isEqualTo(TEST_TEXT);
+    }
+
+    @Test
+    public void updateState_notSystemApp_ignore() throws NameNotFoundException {
+        final List<ResolveInfo> testResolveInfos = new ArrayList<>();
+        testResolveInfos.add(
+                getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true));
+        when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+                .thenReturn(testResolveInfos);
+        mController.updateState(mPreferenceCategory);
+        verify(mPreferenceCategory, never()).addPreference(any(Preference.class));
+        verify(mContext, never()).sendBroadcast(any(Intent.class));
+    }
+
+    @Test
+    public void updateState_thenOnPause_sendBroadcasts() throws NameNotFoundException {
+        final List<ResolveInfo> testResolveInfos = new ArrayList<>();
+        testResolveInfos.add(
+                getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
+        when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+                .thenReturn(testResolveInfos);
+        mController.updateState(mPreferenceCategory);
+        ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).sendBroadcast(intent.capture());
+        assertThat(intent.getValue().getAction())
+                .isEqualTo(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
+
+        mController.onPause();
+        verify(mContext, times(2)).sendBroadcast(intent.capture());
+        assertThat(intent.getValue().getAction())
+                .isEqualTo(LocationManager.SETTINGS_FOOTER_REMOVED_ACTION);
+    }
+
+    @Test
+    public void onPause_doNotSendBroadcast() {
+        mController.onPause();
+        verify(mContext, never()).sendBroadcast(any(Intent.class));
+    }
+
+    /**
+     * Returns a ResolveInfo object for testing
+     * @param isSystemApp If true, the application is a system app.
+     * @param hasRequiredMetaData If true, the broadcast receiver has a valid value for
+     *                            {@link LocationManager#METADATA_SETTINGS_FOOTER_STRING}
+     */
+    private ResolveInfo getTestResolveInfo(boolean isSystemApp, boolean hasRequiredMetaData) {
+        ResolveInfo testResolveInfo = new ResolveInfo();
+        ApplicationInfo testAppInfo = new ApplicationInfo();
+        if (isSystemApp) {
+            testAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        }
+        ActivityInfo testActivityInfo = new ActivityInfo();
+        testActivityInfo.name = "TestActivityName";
+        testActivityInfo.packageName = "TestPackageName";
+        testActivityInfo.applicationInfo = testAppInfo;
+        if (hasRequiredMetaData) {
+            testActivityInfo.metaData = new Bundle();
+            testActivityInfo.metaData.putInt(
+                    LocationManager.METADATA_SETTINGS_FOOTER_STRING, TEST_RES_ID);
+        }
+        testResolveInfo.activityInfo = testActivityInfo;
+        return testResolveInfo;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java
index d6df612..7327d01 100644
--- a/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java
@@ -29,7 +29,9 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -238,19 +240,26 @@
     @Test
     public void testOnSwitchChanged_channel_default() throws Exception {
         NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.pkg = "pkg";
         NotificationChannel channel =
                 new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_UNSPECIFIED);
+        when(mBackend.onlyHasDefaultChannel(anyString(), anyInt())).thenReturn(true);
         mController.onResume(appRow, channel, null, null);
         mController.updateState(mPreference);
 
         mController.onSwitchChanged(null, false);
         assertEquals(IMPORTANCE_NONE, channel.getImportance());
+        assertTrue(appRow.banned);
 
         mController.onSwitchChanged(null, true);
         assertEquals(IMPORTANCE_UNSPECIFIED, channel.getImportance());
+        assertFalse(appRow.banned);
 
         verify(mBackend, times(2)).updateChannel(any(), anyInt(), any());
 
+        // 2 calls for onSwitchChanged + once when calling updateState originally
+        verify(mBackend, times(3)).setNotificationsEnabledForPackage(
+                anyString(), anyInt(), anyBoolean());
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/EnableZenModeDialogTest.java b/tests/robotests/src/com/android/settings/notification/EnableZenModeDialogTest.java
deleted file mode 100644
index 8b5ef79..0000000
--- a/tests/robotests/src/com/android/settings/notification/EnableZenModeDialogTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2018 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.notification;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.content.Context;
-import android.net.Uri;
-import android.service.notification.Condition;
-import android.view.LayoutInflater;
-
-import com.android.settings.TestConfig;
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class EnableZenModeDialogTest {
-    private EnableZenModeDialog mController;
-
-    @Mock
-    private Context mContext;
-    @Mock
-    private Activity mActivity;
-    @Mock
-    private Fragment mFragment;
-
-    private Context mShadowContext;
-    private LayoutInflater mLayoutInflater;
-    private Condition mCountdownCondition;
-    private Condition mAlarmCondition;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mShadowContext = RuntimeEnvironment.application;
-        when(mActivity.getApplicationContext()).thenReturn(mShadowContext);
-        when(mContext.getApplicationContext()).thenReturn(mContext);
-        when(mFragment.getContext()).thenReturn(mShadowContext);
-        mLayoutInflater = LayoutInflater.from(mShadowContext);
-        when(mActivity.getLayoutInflater()).thenReturn(mLayoutInflater);
-
-        mController = spy(new EnableZenModeDialog());
-        mController.mContext = mContext;
-        mController.mActivity = mActivity;
-        mController.mForeverId =  Condition.newId(mContext).appendPath("forever").build();
-        when(mContext.getString(com.android.internal.R.string.zen_mode_forever))
-                .thenReturn("testSummary");
-        mController.getContentView();
-
-        // these methods use static calls to ZenModeConfig which would normally fail in robotests,
-        // so instead do nothing:
-        doNothing().when(mController).bindGenericCountdown();
-        doReturn(null).when(mController).getTimeUntilNextAlarmCondition();
-        doNothing().when(mController).bindNextAlarm(any());
-
-        // as a result of doing nothing above, must bind manually:
-        Uri alarm =  Condition.newId(mContext).appendPath("alarm").build();
-        mAlarmCondition = new Condition(alarm, "alarm", "", "", 0, 0, 0);
-        Uri countdown =  Condition.newId(mContext).appendPath("countdown").build();
-        mCountdownCondition = new Condition(countdown, "countdown", "", "", 0, 0, 0);
-        mController.bind(mCountdownCondition,
-                mController.mZenRadioGroupContent.getChildAt(
-                EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX),
-                EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX);
-        mController.bind(mAlarmCondition,
-                mController.mZenRadioGroupContent.getChildAt(
-                EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX),
-                EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX);
-    }
-
-    @Test
-    public void testForeverChecked() {
-        mController.bindConditions(mController.forever());
-
-        assertTrue(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
-                .isChecked());
-        assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
-                .isChecked());
-        assertFalse(mController.getConditionTagAt(
-                EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
-    }
-
-    @Test
-    public void testNoneChecked() {
-        mController.bindConditions(null);
-        assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
-                .isChecked());
-        assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
-                .isChecked());
-        assertFalse(mController.getConditionTagAt(
-                EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
-    }
-
-    @Test
-    public void testAlarmChecked() {
-        doReturn(false).when(mController).isCountdown(mAlarmCondition);
-        doReturn(true).when(mController).isAlarm(mAlarmCondition);
-
-        mController.bindConditions(mAlarmCondition);
-        assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
-                .isChecked());
-        assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
-                .isChecked());
-        assertTrue(mController.getConditionTagAt(
-                EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
-    }
-
-    @Test
-    public void testCountdownChecked() {
-        doReturn(false).when(mController).isAlarm(mCountdownCondition);
-        doReturn(true).when(mController).isCountdown(mCountdownCondition);
-
-        mController.bindConditions(mCountdownCondition);
-        assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
-                .isChecked());
-        assertTrue(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
-                .isChecked());
-        assertFalse(mController.getConditionTagAt(
-                EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
-    }
-}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationAppPreferenceTest.java b/tests/robotests/src/com/android/settings/notification/NotificationAppPreferenceTest.java
new file mode 100644
index 0000000..dc8ee13
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/NotificationAppPreferenceTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2018 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.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class NotificationAppPreferenceTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    public void createNewPreference_shouldSetLayout() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        assertThat(preference.getWidgetLayoutResource()).isEqualTo(
+                R.layout.preference_widget_master_switch);
+    }
+
+    @Test
+    public void setChecked_shouldUpdateButtonCheckedState() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+                inflater.inflate(R.layout.preference_app, null));
+        final LinearLayout widgetView = holder.itemView.findViewById(android.R.id.widget_frame);
+        inflater.inflate(R.layout.preference_widget_master_switch, widgetView, true);
+        final Switch toggle = (Switch) holder.findViewById(R.id.switchWidget);
+        preference.onBindViewHolder(holder);
+
+        preference.setChecked(true);
+        assertThat(toggle.isChecked()).isTrue();
+
+        preference.setChecked(false);
+        assertThat(toggle.isChecked()).isFalse();
+    }
+
+    @Test
+    public void setSwitchEnabled_shouldUpdateButtonEnabledState() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+                inflater.inflate(R.layout.preference_app, null));
+        final LinearLayout widgetView = holder.itemView.findViewById(android.R.id.widget_frame);
+        inflater.inflate(R.layout.preference_widget_master_switch, widgetView, true);
+        final Switch toggle = (Switch) holder.findViewById(R.id.switchWidget);
+        preference.onBindViewHolder(holder);
+
+        preference.setSwitchEnabled(true);
+        assertThat(toggle.isEnabled()).isTrue();
+
+        preference.setSwitchEnabled(false);
+        assertThat(toggle.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void setSwitchEnabled_shouldUpdateButtonEnabledState_beforeViewBound() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+                inflater.inflate(R.layout.preference_app, null));
+        final LinearLayout widgetView = holder.itemView.findViewById(android.R.id.widget_frame);
+        inflater.inflate(R.layout.preference_widget_master_switch, widgetView, true);
+        final Switch toggle = (Switch) holder.findViewById(R.id.switchWidget);
+
+        preference.setSwitchEnabled(false);
+        preference.onBindViewHolder(holder);
+        assertThat(toggle.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void clickWidgetView_shouldToggleButton() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+                inflater.inflate(R.layout.preference_app, null));
+        final LinearLayout widgetView = holder.itemView.findViewById(android.R.id.widget_frame);
+        inflater.inflate(R.layout.preference_widget_master_switch, widgetView, true);
+        final Switch toggle = (Switch) holder.findViewById(R.id.switchWidget);
+        preference.onBindViewHolder(holder);
+
+        widgetView.performClick();
+        assertThat(toggle.isChecked()).isTrue();
+
+        widgetView.performClick();
+        assertThat(toggle.isChecked()).isFalse();
+    }
+
+    @Test
+    public void clickWidgetView_shouldNotToggleButtonIfDisabled() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+                inflater.inflate(R.layout.preference_app, null));
+        final LinearLayout widgetView = holder.itemView.findViewById(android.R.id.widget_frame);
+        inflater.inflate(R.layout.preference_widget_master_switch, widgetView, true);
+        final Switch toggle = (Switch) holder.findViewById(R.id.switchWidget);
+        preference.onBindViewHolder(holder);
+        toggle.setEnabled(false);
+
+        widgetView.performClick();
+        assertThat(toggle.isChecked()).isFalse();
+    }
+
+    @Test
+    public void clickWidgetView_shouldNotifyPreferenceChanged() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+                LayoutInflater.from(mContext).inflate(R.layout.preference_app, null));
+        final View widgetView = holder.findViewById(android.R.id.widget_frame);
+        final Preference.OnPreferenceChangeListener
+                listener = mock(Preference.OnPreferenceChangeListener.class);
+        preference.setOnPreferenceChangeListener(listener);
+        preference.onBindViewHolder(holder);
+
+        preference.setChecked(false);
+        widgetView.performClick();
+        verify(listener).onPreferenceChange(preference, true);
+
+        preference.setChecked(true);
+        widgetView.performClick();
+        verify(listener).onPreferenceChange(preference, false);
+    }
+
+    @Test
+    public void setDisabledByAdmin_hasEnforcedAdmin_shouldDisableButton() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+                inflater.inflate(R.layout.preference_app, null));
+        final LinearLayout widgetView = holder.itemView.findViewById(android.R.id.widget_frame);
+        inflater.inflate(R.layout.preference_widget_master_switch, widgetView, true);
+        final Switch toggle = (Switch) holder.findViewById(R.id.switchWidget);
+        toggle.setEnabled(true);
+        preference.onBindViewHolder(holder);
+
+        preference.setDisabledByAdmin(mock(RestrictedLockUtils.EnforcedAdmin.class));
+        assertThat(toggle.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void setDisabledByAdmin_noEnforcedAdmin_shouldEnableButton() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+                inflater.inflate(R.layout.preference_app, null));
+        final LinearLayout widgetView = holder.itemView.findViewById(android.R.id.widget_frame);
+        inflater.inflate(R.layout.preference_widget_master_switch, widgetView, true);
+        final Switch toggle = (Switch) holder.findViewById(R.id.switchWidget);
+        toggle.setEnabled(false);
+        preference.onBindViewHolder(holder);
+
+        preference.setDisabledByAdmin(null);
+        assertThat(toggle.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void onBindViewHolder_toggleButtonShouldHaveContentDescription() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
+        final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
+                inflater.inflate(R.layout.preference_app, null));
+        final LinearLayout widgetView = holder.itemView.findViewById(android.R.id.widget_frame);
+        inflater.inflate(R.layout.preference_widget_master_switch, widgetView, true);
+        final Switch toggle = (Switch) holder.findViewById(R.id.switchWidget);
+        final String label = "TestButton";
+        preference.setTitle(label);
+
+        preference.onBindViewHolder(holder);
+
+        assertThat(toggle.getContentDescription()).isEqualTo(label);
+    }
+
+    @Test
+    public void setSummary_showSummaryContainer() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        View rootView = View.inflate(mContext, R.layout.preference_app, null /* parent */);
+        PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(rootView);
+        preference.setSummary("test");
+        preference.onBindViewHolder(holder);
+
+        assertThat(holder.findViewById(R.id.summary_container).getVisibility())
+                .isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void noSummary_hideSummaryContainer() {
+        final NotificationAppPreference preference = new NotificationAppPreference(mContext);
+        View rootView = View.inflate(mContext, R.layout.preference_app, null /* parent */);
+        PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(rootView);
+        preference.setSummary(null);
+        preference.onBindViewHolder(holder);
+
+        assertThat(holder.findViewById(R.id.summary_container).getVisibility())
+                .isEqualTo(View.GONE);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/RecentNotifyingAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/RecentNotifyingAppsPreferenceControllerTest.java
index a25bb00..1aa963d 100644
--- a/tests/robotests/src/com/android/settings/notification/RecentNotifyingAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/RecentNotifyingAppsPreferenceControllerTest.java
@@ -31,7 +31,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Activity;
 import android.app.Application;
+import android.app.Fragment;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -90,6 +92,10 @@
     private ApplicationInfo mApplicationInfo;
     @Mock
     private NotificationBackend mBackend;
+    @Mock
+    private Fragment mHost;
+    @Mock
+    private Activity mActivity;
 
     private Context mContext;
     private RecentNotifyingAppsPreferenceController mController;
@@ -102,7 +108,7 @@
         doReturn(mPackageManager).when(mContext).getPackageManager();
 
         mController = new RecentNotifyingAppsPreferenceController(
-                mContext, mBackend, mAppState, null);
+                mContext, mBackend, mAppState, mHost);
         when(mScreen.findPreference(anyString())).thenReturn(mCategory);
 
         when(mScreen.findPreference(RecentNotifyingAppsPreferenceController.KEY_SEE_ALL))
@@ -110,6 +116,7 @@
         when(mScreen.findPreference(RecentNotifyingAppsPreferenceController.KEY_DIVIDER))
                 .thenReturn(mDivider);
         when(mCategory.getContext()).thenReturn(mContext);
+        when(mHost.getActivity()).thenReturn(mActivity);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java
index 1d71a8a..86f0f2d 100644
--- a/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java
@@ -19,6 +19,7 @@
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.content.Context;
+import android.provider.Settings;
 import android.support.v7.preference.Preference;
 
 import com.android.settings.R;
@@ -63,7 +64,7 @@
         ShadowApplication shadowApplication = ShadowApplication.getInstance();
         shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
         mContext = shadowApplication.getApplicationContext();
-        mController = new ZenModePreferenceController(mContext);
+        mController = new ZenModePreferenceController(mContext, null);
         when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
         mSummaryBuilder = spy(new ZenModeSettings.SummaryBuilder(mContext));
         ReflectionHelpers.setField(mController, "mSummaryBuilder", mSummaryBuilder);
@@ -76,16 +77,16 @@
     }
 
     @Test
-    public void updateState_preferenceEnabled_shouldSetSummary() {
+    public void updateState_automaticRuleEnabled_shouldSetSummary() {
         when(mPreference.isEnabled()).thenReturn(true);
 
         mController.updateState(mPreference);
-        verify(mPreference).setSummary(mContext.getString(R.string.zen_mode_settings_summary_off));
+        verify(mPreference).setSummary(mContext.getResources().getString(
+                R.string.zen_mode_sound_summary_off));
 
         doReturn(1).when(mSummaryBuilder).getEnabledAutomaticRulesCount();
         mController.updateState(mPreference);
-        verify(mPreference).setSummary(mContext.getResources().getQuantityString(
-            R.plurals.zen_mode_settings_summary_on, 1, 1));
+        verify(mPreference).setSummary(mSummaryBuilder.getSoundSummary());
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
index 0691086..b416991 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
@@ -27,6 +27,9 @@
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
+import java.util.HashMap;
+import java.util.Map;
+
 @Implements(Utils.class)
 public class ShadowUtils {
 
@@ -34,6 +37,7 @@
     private static boolean sIsUserAMonkey;
     private static boolean sIsDemoUser;
     private static ComponentName sDeviceOwnerComponentName;
+    private static Map<String, String> sAppNameMap;
 
     @Implementation
     public static int enforceSameOwner(Context context, int userId) {
@@ -89,4 +93,19 @@
     public static int getManagedProfileId(UserManager um, int parentUserId) {
         return UserHandle.USER_NULL;
     }
+
+    @Implementation
+    public static CharSequence getApplicationLabel(Context context, String packageName) {
+        if (sAppNameMap != null) {
+            return sAppNameMap.get(packageName);
+        }
+        return null;
+    }
+
+    public static void setApplicationLabel(String packageName, String appLabel) {
+        if (sAppNameMap == null) {
+            sAppNameMap = new HashMap<>();
+        }
+        sAppNameMap.put(packageName, appLabel);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java
index d489094..6556f1c 100644
--- a/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java
@@ -36,6 +36,8 @@
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
 public class AppPreferenceTest {
 
+    private static final int EXPECTED_APP_ICON_SIZE_DP = 32;
+
     private Context mContext;
     private View mRootView;
     private AppPreference mPref;
@@ -75,4 +77,12 @@
         assertThat(mHolder.findViewById(R.id.summary_container).getVisibility())
                 .isEqualTo(View.GONE);
     }
+
+    @Test
+    public void foobar_testName() {
+        // Can't use isEquals() to compare float. Use isWithIn().of() instead.
+        assertThat(mContext.getResources().getDimension(R.dimen.secondary_app_icon_size))
+                .isWithin(0.01f)
+                .of(EXPECTED_APP_ICON_SIZE_DP);
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
index dca6974..4b18fcf 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
@@ -73,7 +73,8 @@
         })
 public class WifiTetherPreferenceControllerTest {
 
-    @Mock
+    private static final String SSID = "Pixel";
+
     private Context mContext;
     @Mock
     private ConnectivityManager mConnectivityManager;
@@ -81,6 +82,8 @@
     private WifiManager mWifiManager;
     @Mock
     private PreferenceScreen mScreen;
+    @Mock
+    private WifiConfiguration mWifiConfiguration;
 
     private WifiTetherPreferenceController mController;
     private Lifecycle mLifecycle;
@@ -90,6 +93,8 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+
+        mContext = spy(RuntimeEnvironment.application);
         mLifecycleOwner = () -> mLifecycle;
         mLifecycle = new Lifecycle(mLifecycleOwner);
         FakeFeatureFactory.setupForTest();
@@ -98,10 +103,13 @@
                 .thenReturn(mConnectivityManager);
         when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
         when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+        when(mWifiManager.getWifiApConfiguration()).thenReturn(mWifiConfiguration);
+        mWifiConfiguration.SSID = SSID;
 
         when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
         mController = new WifiTetherPreferenceController(mContext, mLifecycle,
                 false /* initSoftApManager */);
+        mController.displayPreference(mScreen);
     }
 
     @After
@@ -127,7 +135,6 @@
     public void startAndStop_shouldRegisterUnregisterReceiver() {
         final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
 
-        mController.displayPreference(mScreen);
         mLifecycle.handleLifecycleEvent(ON_START);
         mLifecycle.handleLifecycleEvent(ON_STOP);
 
@@ -167,48 +174,6 @@
     }
 
     @Test
-    public void testReceiver_apStateChangedToDisabled_shouldUpdatePreferenceSummary() {
-        mController.displayPreference(mScreen);
-        receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_DISABLED);
-        assertThat(mPreference.getSummary().toString()).isEqualTo(
-                RuntimeEnvironment.application.getString(R.string.wifi_hotspot_off_subtext));
-    }
-
-    @Test
-    public void testReceiver_apStateChangedToDisabling_shouldUpdatePreferenceSummary() {
-        mController.displayPreference(mScreen);
-        receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_DISABLING);
-        assertThat(mPreference.getSummary().toString()).isEqualTo(
-                RuntimeEnvironment.application.getString(R.string.wifi_tether_stopping));
-    }
-
-    @Test
-    public void testReceiver_apStateChangedToEnabling_shouldUpdatePreferenceSummary() {
-        mController.displayPreference(mScreen);
-        receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_ENABLING);
-        assertThat(mPreference.getSummary().toString()).isEqualTo(
-                RuntimeEnvironment.application.getString(R.string.wifi_tether_starting));
-    }
-
-    @Test
-    public void testReceiver_apStateChangedToEnabled_shouldNotUpdatePreferenceSummary() {
-        mController.displayPreference(mScreen);
-        receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_DISABLED);
-        assertThat(mPreference.getSummary().toString()).isEqualTo(
-                RuntimeEnvironment.application.getString(R.string.wifi_hotspot_off_subtext));
-
-        // When turning on the hotspot, we receive STATE_ENABLING followed by STATE_ENABLED. The
-        // first should change the status to wifi_tether_starting, and the second should not change
-        // this.
-        receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_ENABLING);
-        assertThat(mPreference.getSummary().toString()).isEqualTo(
-                RuntimeEnvironment.application.getString(R.string.wifi_tether_starting));
-        receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_ENABLED);
-        assertThat(mPreference.getSummary().toString()).isEqualTo(
-                RuntimeEnvironment.application.getString(R.string.wifi_tether_starting));
-    }
-
-    @Test
     public void testReceiver_goingToAirplaneMode_shouldClearPreferenceSummary() {
         final ContentResolver cr = mock(ContentResolver.class);
         when(mContext.getContentResolver()).thenReturn(cr);
@@ -224,22 +189,32 @@
     }
 
     @Test
-    public void testReceiver_tetherEnabled_shouldUpdatePreferenceSummary() {
-        mController.displayPreference(mScreen);
-        final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
-        final Intent broadcast = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
-        final ArrayList<String> activeTethers = new ArrayList<>();
-        activeTethers.add("1");
-        broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, activeTethers);
-        broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
-                new ArrayList<>());
-        final WifiConfiguration configuration = new WifiConfiguration();
-        configuration.SSID = "test-ap";
-        when(mWifiManager.getWifiApConfiguration()).thenReturn(configuration);
+    public void testHandleWifiApStateChanged_stateEnabling_showEnablingSummary() {
+        mController.handleWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0 /* reason */);
 
-        receiver.onReceive(RuntimeEnvironment.application, broadcast);
+        assertThat(mPreference.getSummary()).isEqualTo("Turning hotspot on\u2026");
+    }
 
-        verify(mContext).getString(eq(R.string.wifi_tether_enabled_subtext), any());
+    @Test
+    public void testHandleWifiApStateChanged_stateEnabled_showEnabledSummary() {
+        mController.handleWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0 /* reason */);
+
+        assertThat(mPreference.getSummary()).isEqualTo("Pixel is active");
+    }
+
+    @Test
+    public void testHandleWifiApStateChanged_stateDisabling_showDisablingSummary() {
+        mController.handleWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0 /* reason */);
+
+        assertThat(mPreference.getSummary()).isEqualTo("Turning off hotspot\u2026");
+    }
+
+    @Test
+    public void testHandleWifiApStateChanged_stateDisabled_showDisabledSummary() {
+        mController.handleWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0 /* reason */);
+
+        assertThat(mPreference.getSummary()).isEqualTo(
+                "Not sharing internet or content with other devices");
     }
 
     @Implements(WifiTetherSettings.class)
@@ -285,17 +260,4 @@
             onStopCalled = true;
         }
     }
-
-    /**
-     * Helper to cause the controller to receive a WIFI_AP_STATE_CHANGED_ACTION with a specific
-     * state.
-     *
-     * @param state - the state, as specified by one of the WifiManager.WIFI_AP_STATE_* values
-     */
-    private void receiveApStateChangedBroadcast(int state) {
-        final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
-        final Intent broadcast = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
-        broadcast.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, state);
-        receiver.onReceive(RuntimeEnvironment.application, broadcast);
-    }
 }