Added zen settings messages and calls preferences.

- Made ZenModeSettings and ZenModeBehaviorSettings a DashboardFragment
- Switches in ZenModeBehaviorSettings all have their own preference controllers
- Instead of a dropdown, messages & calls have their own pages & preference controllers
- Added basic turn on/off DND button in settings (dialog not yet implemented)

Bug: 63077372
Fixes: 69057767
Test: make -j40 RunSettingsRoboTests
Change-Id: I1c70f77053713f66f873ee578477f23cfd7985bb
diff --git a/res/layout/zen_mode_button.xml b/res/layout/zen_mode_button.xml
new file mode 100644
index 0000000..af24fce
--- /dev/null
+++ b/res/layout/zen_mode_button.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:gravity="bottom"
+    android:paddingTop="4dp"
+    android:paddingStart="72dp"
+    android:paddingEnd="72dp"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/zen_mode_button"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:paddingEnd="8dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/zen_mode_settings_button.xml b/res/layout/zen_mode_settings_button.xml
new file mode 100644
index 0000000..4d4b7d6
--- /dev/null
+++ b/res/layout/zen_mode_settings_button.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:gravity="bottom"
+    android:paddingTop="4dp"
+    android:paddingStart="72dp"
+    android:paddingEnd="72dp"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button
+        android:id="@+id/zen_mode_settings_turn_on_button"
+        style="@style/ActionPrimaryButton"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="@string/zen_mode_button_turn_on"
+        android:paddingEnd="8dp" />
+
+    <Button
+        android:id="@+id/zen_mode_settings_turn_off_button"
+        style="@style/ActionSecondaryButton"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="@string/zen_mode_button_turn_off"
+        android:paddingEnd="8dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 84f3722..aa8a586 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1031,4 +1031,18 @@
         <item>never</item>
     </string-array>
 
+    <string-array name="zen_mode_contacts_entries" translatable="false">
+        <item>@string/zen_mode_from_anyone</item>
+        <item>@string/zen_mode_from_contacts</item>
+        <item>@string/zen_mode_from_starred</item>
+        <item>@string/zen_mode_from_none</item>
+    </string-array>
+
+    <string-array name="zen_mode_contacts_values" translatable="false">
+        <item>zen_mode_from_anyone</item>
+        <item>zen_mode_from_contacts</item>
+        <item>zen_mode_from_starred</item>
+        <item>zen_mode_from_none</item>
+    </string-array>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3b474ab..3654d85 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6754,6 +6754,27 @@
     <!--  Do not disturb: Button to add new automatic rule to DND. [CHAR LIMIT=30] -->
     <string name="zen_mode_add">Add</string>
 
+    <!--  Do not disturb: Label for button that will turn on zen mode. [CHAR LIMIT=30] -->
+    <string name="zen_mode_button_turn_on">TURN ON NOW</string>
+
+    <!--  Do not disturb: Label for button that will turn off zen mode. [CHAR LIMIT=30] -->
+    <string name="zen_mode_button_turn_off">TURN OFF NOW</string>
+
+    <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer showing end time of DND -->
+    <string name="zen_mode_settings_dnd_manual_end_time_next_day">Do Not Disturb is on until <xliff:g id="formatted_time" example="7:00 AM">%s</xliff:g></string>
+
+    <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer showing length of DND -->
+    <string name="zen_mode_settings_dnd_manual_indefinite">Do Not Disturb will stay on until you turn it off.</string>
+
+    <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer showing how DND was triggered by an automatic DND rule -->
+    <string name="zen_mode_settings_dnd_automatic_rule">Do Not Disturb was automatically turned on by a rule <xliff:g id="rule_name" example="Weeknights">%s</xliff:g></string>
+
+    <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer how DND was triggered by an app -->
+    <string name="zen_mode_settings_dnd_automatic_rule_app">Do Not Disturb was automatically turned on by an app <xliff:g id="app_name" example="Pixel Services">%s</xliff:g></string>
+
+    <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer how DND was triggered by multiple rules and/or apps -->
+    <string name="zen_mode_settings_dnd_automatic_rule_multiple">Do Not Disturb was automatically turned on by a rule or app</string>
+
     <!-- Work Sounds: Work sound settings section header.  [CHAR LIMIT=50] -->
     <string name="sound_work_settings">Work profile sounds</string>
 
diff --git a/res/xml/zen_mode_automation_settings.xml b/res/xml/zen_mode_automation_settings.xml
index 33842ec..99826ea 100644
--- a/res/xml/zen_mode_automation_settings.xml
+++ b/res/xml/zen_mode_automation_settings.xml
@@ -19,5 +19,15 @@
                   android:key="zen_mode_automation_settings_page"
                   android:title="@string/zen_mode_automation_settings_page_title" >
 
-    <!-- Rules added at runtime -->
+
+    <PreferenceCategory
+        android:key="zen_mode_automatic_rules">
+        <!-- Rules added at runtime -->
+    </PreferenceCategory>
+
+    <Preference
+        android:key="zen_mode_add_automatic_rule"
+        android:icon="@drawable/ic_menu_add"
+        android:title="@string/zen_mode_add_rule"/>
+
 </PreferenceScreen>
diff --git a/res/xml/zen_mode_behavior_settings.xml b/res/xml/zen_mode_behavior_settings.xml
index 999d6b4..7b5ed91 100644
--- a/res/xml/zen_mode_behavior_settings.xml
+++ b/res/xml/zen_mode_behavior_settings.xml
@@ -46,17 +46,16 @@
            android:key="zen_mode_events"
            android:title="@string/zen_mode_events"/>
 
-       <!-- Messages -->
-       <DropDownPreference
+       <Preference
            android:key="zen_mode_messages"
            android:title="@string/zen_mode_messages"
-           android:summary="%s" />
+           android:fragment="com.android.settings.notification.ZenModeMessagesSettings" />
 
        <!-- Calls -->
-       <DropDownPreference
+       <Preference
            android:key="zen_mode_calls"
            android:title="@string/zen_mode_calls"
-           android:summary="%s" />
+           android:fragment="com.android.settings.notification.ZenModeCallsSettings" />
 
        <!-- Repeat callers -->
        <SwitchPreference
diff --git a/res/xml/zen_mode_calls_settings.xml b/res/xml/zen_mode_calls_settings.xml
new file mode 100644
index 0000000..aa84216
--- /dev/null
+++ b/res/xml/zen_mode_calls_settings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:key="zen_mode_calls_settings"
+    android:title="@string/zen_mode_calls" />
diff --git a/res/xml/zen_mode_messages_settings.xml b/res/xml/zen_mode_messages_settings.xml
new file mode 100644
index 0000000..4b4a1e2
--- /dev/null
+++ b/res/xml/zen_mode_messages_settings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:key="zen_mode_messages_settings"
+    android:title="@string/zen_mode_messages" />
diff --git a/res/xml/zen_mode_settings.xml b/res/xml/zen_mode_settings.xml
index 92e2b7f..82bef4d 100644
--- a/res/xml/zen_mode_settings.xml
+++ b/res/xml/zen_mode_settings.xml
@@ -19,6 +19,7 @@
     android:key="zen_mode_settings"
     android:title="@string/zen_mode_settings_title">
 
+    <!-- Priority behavior settings -->
     <Preference
             android:key="zen_mode_behavior_settings"
             android:title="@string/zen_mode_behavior_settings_title"
@@ -29,4 +30,14 @@
         android:key="zen_mode_automation_settings"
         android:title="@string/zen_mode_automation_settings_title"
         android:fragment="com.android.settings.notification.ZenModeAutomationSettings" />
+
+    <!-- Turn on DND button -->
+    <!-- Layout preference doesn't obey allowDividerAbove, so put it in a PreferenceCategory -->
+    <PreferenceCategory>
+        <com.android.settings.applications.LayoutPreference
+            android:key="zen_mode_settings_button_container"
+            android:selectable="false"
+            android:layout="@layout/zen_mode_settings_button" />
+    </PreferenceCategory>
+
 </PreferenceScreen>
diff --git a/src/com/android/settings/notification/AbstractZenModeAutomaticRulePreferenceController.java b/src/com/android/settings/notification/AbstractZenModeAutomaticRulePreferenceController.java
new file mode 100644
index 0000000..ec9cf2a
--- /dev/null
+++ b/src/com/android/settings/notification/AbstractZenModeAutomaticRulePreferenceController.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.AutomaticZenRule;
+import android.app.Fragment;
+import android.app.NotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.provider.Settings;
+import android.service.notification.ConditionProviderService;
+import android.service.notification.ZenModeConfig;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Set;
+
+abstract public class AbstractZenModeAutomaticRulePreferenceController extends
+        AbstractPreferenceController implements PreferenceControllerMixin {
+
+    private static final String TAG = "ZenModeAutomaticRule";
+    protected ZenModeBackend mBackend;
+    protected Fragment mParent;
+    protected Set<Map.Entry<String, AutomaticZenRule>> mRules;
+    protected PackageManager mPm;
+
+    public AbstractZenModeAutomaticRulePreferenceController(Context context, Fragment parent) {
+        super(context);
+        mBackend = ZenModeBackend.getInstance(context);
+        mParent = parent;
+        mPm = mContext.getPackageManager();
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+        mRules = getZenModeRules();
+    }
+
+    private Set<Map.Entry<String, AutomaticZenRule>> getZenModeRules() {
+        Map<String, AutomaticZenRule> ruleMap =
+                NotificationManager.from(mContext).getAutomaticZenRules();
+        return ruleMap.entrySet();
+    }
+
+    protected void showNameRuleDialog(final ZenRuleInfo ri) {
+        new ZenRuleNameDialog(mContext, null, ri.defaultConditionId) {
+            @Override
+            public void onOk(String ruleName) {
+                AutomaticZenRule rule = new AutomaticZenRule(ruleName, ri.serviceComponent,
+                        ri.defaultConditionId, NotificationManager.INTERRUPTION_FILTER_PRIORITY,
+                        true);
+                String savedRuleId = mBackend.addZenRule(rule);
+                if (savedRuleId != null) {
+                    mParent.startActivity(getRuleIntent(ri.settingsAction, null, savedRuleId));
+                }
+            }
+        }.show();
+    }
+
+    protected Map.Entry<String, AutomaticZenRule>[] sortedRules() {
+        if (mRules == null) {
+            mRules = getZenModeRules();
+        }
+        final Map.Entry<String, AutomaticZenRule>[] rt =
+                mRules.toArray(new Map.Entry[mRules.size()]);
+        Arrays.sort(rt, RULE_COMPARATOR);
+        return rt;
+    }
+
+    protected static Intent getRuleIntent(String settingsAction,
+            ComponentName configurationActivity, String ruleId) {
+        final Intent intent = new Intent()
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+                .putExtra(ConditionProviderService.EXTRA_RULE_ID, ruleId);
+        if (configurationActivity != null) {
+            intent.setComponent(configurationActivity);
+        } else {
+            intent.setAction(settingsAction);
+        }
+        return intent;
+    }
+
+    private static final Comparator<Map.Entry<String, AutomaticZenRule>> RULE_COMPARATOR =
+            new Comparator<Map.Entry<String, AutomaticZenRule>>() {
+                @Override
+                public int compare(Map.Entry<String, AutomaticZenRule> lhs,
+                        Map.Entry<String, AutomaticZenRule> rhs) {
+                    int byDate = Long.compare(lhs.getValue().getCreationTime(),
+                            rhs.getValue().getCreationTime());
+                    if (byDate != 0) {
+                        return byDate;
+                    } else {
+                        return key(lhs.getValue()).compareTo(key(rhs.getValue()));
+                    }
+                }
+
+                private String key(AutomaticZenRule rule) {
+                    final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId())
+                            ? 1 : ZenModeConfig.isValidEventConditionId(rule.getConditionId())
+                            ? 2 : 3;
+                    return type + rule.getName().toString();
+                }
+            };
+
+    public static ZenRuleInfo getRuleInfo(PackageManager pm, ServiceInfo si) {
+        if (si == null || si.metaData == null) {
+            return null;
+        }
+        final String ruleType = si.metaData.getString(ConditionProviderService.META_DATA_RULE_TYPE);
+        final ComponentName configurationActivity = getSettingsActivity(si);
+        if (ruleType != null && !ruleType.trim().isEmpty() && configurationActivity != null) {
+            final ZenRuleInfo ri = new ZenRuleInfo();
+            ri.serviceComponent = new ComponentName(si.packageName, si.name);
+            ri.settingsAction = Settings.ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS;
+            ri.title = ruleType;
+            ri.packageName = si.packageName;
+            ri.configurationActivity = getSettingsActivity(si);
+            ri.packageLabel = si.applicationInfo.loadLabel(pm);
+            ri.ruleInstanceLimit =
+                    si.metaData.getInt(ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
+            return ri;
+        }
+        return null;
+    }
+
+    protected static ComponentName getSettingsActivity(ServiceInfo si) {
+        if (si == null || si.metaData == null) {
+            return null;
+        }
+        final String configurationActivity =
+                si.metaData.getString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY);
+        if (configurationActivity != null) {
+            return ComponentName.unflattenFromString(configurationActivity);
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/settings/notification/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/AbstractZenModePreferenceController.java
new file mode 100644
index 0000000..ec275b2
--- /dev/null
+++ b/src/com/android/settings/notification/AbstractZenModePreferenceController.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+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;
+
+abstract public class AbstractZenModePreferenceController extends
+        AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver,
+        OnResume, OnPause {
+
+    private SettingObserver mSettingObserver;
+    private final String KEY;
+    final private NotificationManager mNotificationManager;
+
+    public AbstractZenModePreferenceController(Context context, String key,
+            Lifecycle lifecycle) {
+        super(context);
+        if (lifecycle != null) {
+            lifecycle.addObserver(this);
+        }
+        KEY = key;
+        mNotificationManager = (NotificationManager) context.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mSettingObserver = new SettingObserver(screen.findPreference(KEY));
+    }
+
+    @Override
+    public void onResume() {
+        if (mSettingObserver != null) {
+            mSettingObserver.register(mContext.getContentResolver());
+        }
+    }
+
+    @Override
+    public void onPause() {
+        if (mSettingObserver != null) {
+            mSettingObserver.unregister(mContext.getContentResolver());
+        }
+    }
+
+    protected NotificationManager.Policy getPolicy() {
+        return mNotificationManager.getNotificationPolicy();
+    }
+
+    protected int getZenMode() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ZEN_MODE, 0);
+    }
+
+    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);
+            cr.registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, this);
+        }
+
+        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/ZenModeAddAutomaticRulePreferenceController.java b/src/com/android/settings/notification/ZenModeAddAutomaticRulePreferenceController.java
new file mode 100644
index 0000000..a15536c
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeAddAutomaticRulePreferenceController.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.utils.ZenServiceListing;
+
+public class ZenModeAddAutomaticRulePreferenceController extends
+        AbstractZenModeAutomaticRulePreferenceController implements
+        Preference.OnPreferenceClickListener {
+
+    private final String KEY_ADD_RULE;
+    private final ZenServiceListing mZenServiceListing;
+
+    public ZenModeAddAutomaticRulePreferenceController(Context context, String key,
+            Fragment parent, ZenServiceListing serviceListing) {
+        super(context, parent);
+        KEY_ADD_RULE = key;
+        mZenServiceListing = serviceListing;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_ADD_RULE;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        Preference pref = screen.findPreference(KEY_ADD_RULE);
+        pref.setPersistent(false);
+        pref.setOnPreferenceClickListener(this);
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        new ZenRuleSelectionDialog(mContext, mZenServiceListing) {
+            @Override
+            public void onSystemRuleSelected(ZenRuleInfo ri) {
+                showNameRuleDialog(ri);
+            }
+
+            @Override
+            public void onExternalRuleSelected(ZenRuleInfo ri) {
+                Intent intent = new Intent().setComponent(ri.configurationActivity);
+                mParent.startActivity(intent);
+            }
+        }.show();
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeAlarmsPreferenceController.java b/src/com/android/settings/notification/ZenModeAlarmsPreferenceController.java
new file mode 100644
index 0000000..df9966d
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeAlarmsPreferenceController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager.Policy;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeAlarmsPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener{
+
+    protected static final String KEY = "zen_mode_alarms";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeAlarmsPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+                pref.setEnabled(false);
+                pref.setChecked(false);
+                break;
+            case Settings.Global.ZEN_MODE_ALARMS:
+                pref.setEnabled(false);
+                pref.setChecked(true);
+                break;
+            default:
+                pref.setEnabled(true);
+                pref.setChecked(mBackend.isPriorityCategoryEnabled(
+                        Policy.PRIORITY_CATEGORY_ALARMS));
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean allowAlarms = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) {
+            Log.d(TAG, "onPrefChange allowAlarms=" + allowAlarms);
+        }
+        mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_ALARMS, allowAlarms);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceController.java b/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceController.java
new file mode 100644
index 0000000..0ec5d69
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.AutomaticZenRule;
+import android.app.Fragment;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceScreen;
+
+import java.util.Map;
+
+public class ZenModeAutomaticRulesPreferenceController extends
+        AbstractZenModeAutomaticRulePreferenceController  {
+
+    private final String KEY_AUTOMATIC_RULES;
+    private PreferenceCategory mPreferenceCategory;
+    Map.Entry<String, AutomaticZenRule>[] mSortedRules;
+
+    public ZenModeAutomaticRulesPreferenceController(Context context, String key,
+            Fragment parent) {
+        super(context, parent);
+        KEY_AUTOMATIC_RULES = key;
+        mSortedRules = sortedRules();
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_AUTOMATIC_RULES;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreferenceCategory = (PreferenceCategory) screen.findPreference(getPreferenceKey());
+        mPreferenceCategory.setPersistent(false);
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        // no need to update AutomaticRule if a rule was deleted
+        // (on rule deletion, the preference removes itself from its parent)
+        int oldRuleLength = mSortedRules.length;
+        mSortedRules = sortedRules();
+        if  (!wasRuleDeleted(oldRuleLength)) {
+            updateAutomaticRules();
+        }
+    }
+
+    private boolean wasRuleDeleted(int oldRuleLength) {
+        int newRuleLength = mSortedRules.length;
+        int prefCount =  mPreferenceCategory.getPreferenceCount();
+
+        return (prefCount == oldRuleLength -1) && (prefCount == newRuleLength);
+    }
+
+    private void updateAutomaticRules() {
+        for (Map.Entry<String, AutomaticZenRule> sortedRule : mSortedRules) {
+            ZenRulePreference currPref = (ZenRulePreference)
+                    mPreferenceCategory.findPreference(sortedRule.getKey());
+            if (currPref != null && currPref.appExists) {
+                // rule already exists in preferences, update it
+                currPref.setAttributes(sortedRule.getValue());
+            } else {
+                // rule doesn't exist in preferences, add it
+                ZenRulePreference pref = new ZenRulePreference(mPreferenceCategory.getContext(),
+                        sortedRule, mPreferenceCategory);
+                if (pref.appExists) {
+                    mPreferenceCategory.addPreference(pref);
+                }
+            }
+        }
+
+    }
+}
+
+
+
diff --git a/src/com/android/settings/notification/ZenModeAutomationPreferenceController.java b/src/com/android/settings/notification/ZenModeAutomationPreferenceController.java
new file mode 100644
index 0000000..aa46d4e
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeAutomationPreferenceController.java
@@ -0,0 +1,34 @@
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public class ZenModeAutomationPreferenceController extends
+        AbstractPreferenceController implements PreferenceControllerMixin {
+
+    protected static final String KEY_ZEN_MODE_AUTOMATION = "zen_mode_automation_settings";
+    private final ZenModeSettings.SummaryBuilder mSummaryBuilder;
+
+    public ZenModeAutomationPreferenceController(Context context) {
+        super(context);
+        mSummaryBuilder = new ZenModeSettings.SummaryBuilder(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_ZEN_MODE_AUTOMATION;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        preference.setSummary(mSummaryBuilder.getAutomaticRulesSummary());
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeAutomationSettings.java b/src/com/android/settings/notification/ZenModeAutomationSettings.java
index 07e9228..ae75798 100644
--- a/src/com/android/settings/notification/ZenModeAutomationSettings.java
+++ b/src/com/android/settings/notification/ZenModeAutomationSettings.java
@@ -16,167 +16,47 @@
 
 package com.android.settings.notification;
 
-import android.app.AlertDialog;
-import android.app.AutomaticZenRule;
-import android.app.NotificationManager;
-import android.content.ComponentName;
+import android.app.Fragment;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.provider.SearchIndexableResource;
-import android.provider.Settings;
 import android.service.notification.ConditionProviderService;
-import android.service.notification.ZenModeConfig;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.Preference.OnPreferenceClickListener;
-import android.support.v7.preference.PreferenceScreen;
-import android.support.v7.preference.PreferenceViewHolder;
-import android.view.View;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
-import com.android.settings.utils.ManagedServiceSettings.Config;
+import com.android.settings.utils.ManagedServiceSettings;
 import com.android.settings.utils.ZenServiceListing;
-import com.android.settingslib.TwoTargetPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
 
-import java.util.Arrays;
-import java.util.Comparator;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
-public class ZenModeAutomationSettings extends ZenModeSettingsBase implements Indexable {
-
-    static final Config CONFIG = getConditionProviderConfig();
-
-    private PackageManager mPm;
-    private ZenServiceListing mServiceListing;
+public class ZenModeAutomationSettings extends ZenModeSettingsBase {
+    private static final String KEY_ADD_RULE = "zen_mode_add_automatic_rule";
+    private static final String KEY_AUTOMATIC_RULES = "zen_mode_automatic_rules";
+    protected final ManagedServiceSettings.Config CONFIG = getConditionProviderConfig();
 
     @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        addPreferencesFromResource(R.xml.zen_mode_automation_settings);
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        ZenServiceListing serviceListing = new ZenServiceListing(getContext(), CONFIG);
+        serviceListing.reloadApprovedServices();
+        return buildPreferenceControllers(context, this, serviceListing);
+    }
 
-        mPm = mContext.getPackageManager();
-        mServiceListing = new ZenServiceListing(mContext, CONFIG);
-        mServiceListing.reloadApprovedServices();
+    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
+            Fragment parent, ZenServiceListing serviceListing) {
+        List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new ZenModeAddAutomaticRulePreferenceController(context, KEY_ADD_RULE,
+                parent, serviceListing));
+        controllers.add(new ZenModeAutomaticRulesPreferenceController(context,
+                KEY_AUTOMATIC_RULES, parent));
+
+        return controllers;
     }
 
     @Override
-    protected void onZenModeChanged() {
-        // don't care
-    }
-
-    @Override
-    protected void onZenModeConfigChanged() {
-        updateControls();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (isUiRestricted()) {
-            return;
-        }
-        updateControls();
-    }
-
-    private void showAddRuleDialog() {
-        new ZenRuleSelectionDialog(mContext, mServiceListing) {
-            @Override
-            public void onSystemRuleSelected(ZenRuleInfo ri) {
-                showNameRuleDialog(ri);
-            }
-
-            @Override
-            public void onExternalRuleSelected(ZenRuleInfo ri) {
-                Intent intent = new Intent().setComponent(ri.configurationActivity);
-                startActivity(intent);
-            }
-        }.show();
-    }
-
-    private void showNameRuleDialog(final ZenRuleInfo ri) {
-        new ZenRuleNameDialog(mContext, null, ri.defaultConditionId) {
-            @Override
-            public void onOk(String ruleName) {
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ADD_RULE_OK);
-                AutomaticZenRule rule = new AutomaticZenRule(ruleName, ri.serviceComponent,
-                        ri.defaultConditionId, NotificationManager.INTERRUPTION_FILTER_PRIORITY,
-                        true);
-                String savedRuleId = addZenRule(rule);
-                if (savedRuleId != null) {
-                    startActivity(getRuleIntent(ri.settingsAction, null, savedRuleId));
-                }
-            }
-        }.show();
-    }
-
-    private void showDeleteRuleDialog(final String ruleId, final CharSequence ruleName) {
-        new AlertDialog.Builder(mContext)
-                .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, ruleName))
-                .setNegativeButton(R.string.cancel, null)
-                .setPositiveButton(R.string.zen_mode_delete_rule_button,
-                        new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int which) {
-                                mMetricsFeatureProvider.action(mContext,
-                                        MetricsEvent.ACTION_ZEN_DELETE_RULE_OK);
-                                removeZenRule(ruleId);
-                            }
-                        })
-                .show();
-    }
-
-    private Intent getRuleIntent(String settingsAction, ComponentName configurationActivity,
-            String ruleId) {
-        final Intent intent = new Intent()
-                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
-                .putExtra(ConditionProviderService.EXTRA_RULE_ID, ruleId);
-        if (configurationActivity != null) {
-            intent.setComponent(configurationActivity);
-        } else {
-            intent.setAction(settingsAction);
-        }
-        return intent;
-    }
-
-    private Map.Entry<String,AutomaticZenRule>[] sortedRules() {
-        final Map.Entry<String,AutomaticZenRule>[] rt =
-                mRules.toArray(new Map.Entry[mRules.size()]);
-        Arrays.sort(rt, RULE_COMPARATOR);
-        return rt;
-    }
-
-    private void updateControls() {
-        final PreferenceScreen root = getPreferenceScreen();
-        root.removeAll();
-        final Map.Entry<String,AutomaticZenRule>[] sortedRules = sortedRules();
-        for (Map.Entry<String,AutomaticZenRule> sortedRule : sortedRules) {
-            ZenRulePreference pref = new ZenRulePreference(getPrefContext(), sortedRule);
-            if (pref.appExists) {
-                root.addPreference(pref);
-            }
-        }
-        final Preference p = new Preference(getPrefContext());
-        p.setIcon(R.drawable.ic_menu_add);
-        p.setTitle(R.string.zen_mode_add_rule);
-        p.setPersistent(false);
-        p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-            @Override
-            public boolean onPreferenceClick(Preference preference) {
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ADD_RULE);
-                showAddRuleDialog();
-                return true;
-            }
-        });
-        root.addPreference(p);
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_automation_settings;
     }
 
     @Override
@@ -184,18 +64,8 @@
         return MetricsEvent.NOTIFICATION_ZEN_MODE_AUTOMATION;
     }
 
-    private String computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule,
-            CharSequence providerLabel) {
-        final String mode = computeZenModeCaption(getResources(), rule.getInterruptionFilter());
-        final String ruleState = (rule == null || !rule.isEnabled())
-                ? getString(R.string.switch_off_text)
-                : getString(R.string.zen_mode_rule_summary_enabled_combination, mode);
-
-        return ruleState;
-    }
-
-    private static Config getConditionProviderConfig() {
-        final Config c = new Config();
+    protected static ManagedServiceSettings.Config getConditionProviderConfig() {
+        final ManagedServiceSettings.Config c = new ManagedServiceSettings.Config();
         c.tag = TAG;
         c.intentAction = ConditionProviderService.SERVICE_INTERFACE;
         c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
@@ -203,146 +73,22 @@
         return c;
     }
 
-    private static String computeZenModeCaption(Resources res, int zenMode) {
-        switch (zenMode) {
-            case NotificationManager.INTERRUPTION_FILTER_ALARMS:
-                return res.getString(R.string.zen_mode_option_alarms);
-            case NotificationManager.INTERRUPTION_FILTER_PRIORITY:
-                return res.getString(R.string.zen_mode_option_important_interruptions);
-            case NotificationManager.INTERRUPTION_FILTER_NONE:
-                return res.getString(R.string.zen_mode_option_no_interruptions);
-            default:
-                return null;
-        }
-    }
-
-    public static ZenRuleInfo getRuleInfo(PackageManager pm, ServiceInfo si) {
-        if (si == null || si.metaData == null) {
-            return null;
-        }
-        final String ruleType = si.metaData.getString(ConditionProviderService.META_DATA_RULE_TYPE);
-        final ComponentName configurationActivity = getSettingsActivity(si);
-        if (ruleType != null && !ruleType.trim().isEmpty() && configurationActivity != null) {
-            final ZenRuleInfo ri = new ZenRuleInfo();
-            ri.serviceComponent = new ComponentName(si.packageName, si.name);
-            ri.settingsAction = Settings.ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS;
-            ri.title = ruleType;
-            ri.packageName = si.packageName;
-            ri.configurationActivity = getSettingsActivity(si);
-            ri.packageLabel = si.applicationInfo.loadLabel(pm);
-            ri.ruleInstanceLimit =
-                    si.metaData.getInt(ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
-            return ri;
-        }
-        return null;
-    }
-
-    private static ComponentName getSettingsActivity(ServiceInfo si) {
-        if (si == null || si.metaData == null) {
-            return null;
-        }
-        final String configurationActivity =
-                si.metaData.getString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY);
-        if (configurationActivity != null) {
-            return ComponentName.unflattenFromString(configurationActivity);
-        }
-        return null;
-    }
-
-    private static final Comparator<Map.Entry<String,AutomaticZenRule>> RULE_COMPARATOR =
-            new Comparator<Map.Entry<String,AutomaticZenRule>>() {
-                @Override
-                public int compare(Map.Entry<String,AutomaticZenRule> lhs,
-                        Map.Entry<String,AutomaticZenRule> rhs) {
-                    int byDate = Long.compare(lhs.getValue().getCreationTime(),
-                            rhs.getValue().getCreationTime());
-                    if (byDate != 0) {
-                        return byDate;
-                    } else {
-                        return key(lhs.getValue()).compareTo(key(rhs.getValue()));
-                    }
-                }
-
-                private String key(AutomaticZenRule rule) {
-                    final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId())
-                            ? 1 : ZenModeConfig.isValidEventConditionId(rule.getConditionId())
-                            ? 2 : 3;
-                    return type + rule.getName().toString();
-                }
-            };
-
-    private class ZenRulePreference extends TwoTargetPreference {
-        final CharSequence mName;
-        final String mId;
-        final boolean appExists;
-
-        public ZenRulePreference(Context context,
-                final Map.Entry<String, AutomaticZenRule> ruleEntry) {
-            super(context);
-
-            final AutomaticZenRule rule = ruleEntry.getValue();
-            mName = rule.getName();
-            mId = ruleEntry.getKey();
-
-            final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(
-                    rule.getConditionId());
-            final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.getConditionId());
-            final boolean isSystemRule = isSchedule || isEvent;
-
-            try {
-                ApplicationInfo info = mPm.getApplicationInfo(rule.getOwner().getPackageName(), 0);
-                setSummary(computeRuleSummary(rule, isSystemRule, info.loadLabel(mPm)));
-            } catch (PackageManager.NameNotFoundException e) {
-                appExists = false;
-                return;
-            }
-
-            appExists = true;
-            setTitle(rule.getName());
-            setPersistent(false);
-
-            final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION
-                    : isEvent ? ZenModeEventRuleSettings.ACTION : "";
-            ServiceInfo si = mServiceListing.findService(rule.getOwner());
-            ComponentName settingsActivity = getSettingsActivity(si);
-            setIntent(getRuleIntent(action, settingsActivity, mId));
-            setSelectable(settingsActivity != null || isSystemRule);
-        }
-
-        @Override
-        protected int getSecondTargetResId() {
-            return R.layout.zen_rule_widget;
-        }
-
-        @Override
-        public void onBindViewHolder(PreferenceViewHolder view) {
-            super.onBindViewHolder(view);
-
-            View v = view.findViewById(R.id.delete_zen_rule);
-            if (v != null) {
-                v.setOnClickListener(mDeleteListener);
-            }
-        }
-
-        private final View.OnClickListener mDeleteListener = new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                showDeleteRuleDialog(mId, mName);
-            }
-        };
-    }
-
     /**
      * For Search.
      */
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
-                @Override
-                public List<SearchIndexableResource> getXmlResourcesToIndex(
-                        Context context, boolean enabled) {
-                    final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.zen_mode_automation_settings;
-                    return Arrays.asList(sir);
-                }
-            };
+        @Override
+        public List<String> getNonIndexableKeys(Context context) {
+            final List<String> keys = super.getNonIndexableKeys(context);
+            keys.add(KEY_ADD_RULE);
+            keys.add(KEY_AUTOMATIC_RULES);
+            return keys;
+        }
+
+        @Override
+        public List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+            return buildPreferenceControllers(context, null, null);
+        }
+    };
 }
diff --git a/src/com/android/settings/notification/ZenModeBackend.java b/src/com/android/settings/notification/ZenModeBackend.java
new file mode 100644
index 0000000..945da0b
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeBackend.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.AutomaticZenRule;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.service.notification.ZenModeConfig;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.settings.R;
+
+public class ZenModeBackend {
+    @VisibleForTesting
+    protected static final String ZEN_MODE_FROM_ANYONE = "zen_mode_from_anyone";
+    @VisibleForTesting
+    protected static final String ZEN_MODE_FROM_CONTACTS = "zen_mode_from_contacts";
+    @VisibleForTesting
+    protected static final String ZEN_MODE_FROM_STARRED = "zen_mode_from_starred";
+    @VisibleForTesting
+    protected static final String ZEN_MODE_FROM_NONE = "zen_mode_from_none";
+    protected static final int SOURCE_NONE = -1;
+
+    private static ZenModeBackend sInstance;
+
+    protected int mZenMode;
+    /** gets policy last set by updatePolicy **/
+    protected NotificationManager.Policy mPolicy;
+    private final NotificationManager mNotificationManager;
+
+    private String TAG = "ZenModeSettingsBackend";
+    private final Context mContext;
+
+    public static ZenModeBackend getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new ZenModeBackend(context);
+        }
+        return sInstance;
+    }
+
+    public ZenModeBackend(Context context) {
+        mContext = context;
+        mNotificationManager = (NotificationManager) context.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+        updateZenMode();
+        updatePolicy();
+    }
+
+    protected void updatePolicy() {
+        if (mNotificationManager != null) {
+            mPolicy = mNotificationManager.getNotificationPolicy();
+        }
+    }
+
+    protected void updateZenMode() {
+        mZenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ZEN_MODE, mZenMode);
+    }
+
+    protected boolean setZenRule(String id, AutomaticZenRule rule) {
+        return NotificationManager.from(mContext).updateAutomaticZenRule(id, rule);
+    }
+
+    protected void setZenMode(int zenMode) {
+        NotificationManager.from(mContext).setZenMode(zenMode, null, TAG);
+        mZenMode = zenMode;
+    }
+
+    /** gets last zen mode set by setZenMode or updateZenMode **/
+    protected int getZenMode() {
+        mZenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ZEN_MODE, mZenMode);
+        return mZenMode;
+    }
+
+    protected boolean isPriorityCategoryEnabled(int categoryType) {
+        return (mPolicy.priorityCategories & categoryType) != 0;
+    }
+
+    protected int getNewPriorityCategories(boolean allow, int categoryType) {
+        int priorityCategories = mPolicy.priorityCategories;
+        if (allow) {
+            priorityCategories |= categoryType;
+        } else {
+            priorityCategories &= ~categoryType;
+        }
+        return priorityCategories;
+    }
+
+    protected int getPriorityCallSenders() {
+        return mPolicy.priorityCallSenders;
+    }
+
+    protected int getPriorityMessageSenders() {
+        return mPolicy.priorityMessageSenders;
+    }
+
+    protected void saveVisualEffectsPolicy(int category, boolean canBypass) {
+        int suppressedEffects = getNewSuppressedEffects(!canBypass, category);
+        savePolicy(mPolicy.priorityCategories, mPolicy.priorityCallSenders,
+                mPolicy.priorityMessageSenders, suppressedEffects);
+    }
+
+    protected void saveSoundPolicy(int category, boolean allow) {
+        int priorityCategories = getNewPriorityCategories(allow, category);
+        savePolicy(priorityCategories, mPolicy.priorityCallSenders,
+                mPolicy.priorityMessageSenders, mPolicy.suppressedVisualEffects);
+    }
+
+    protected void savePolicy(int priorityCategories, int priorityCallSenders,
+            int priorityMessageSenders, int suppressedVisualEffects) {
+        mPolicy = new NotificationManager.Policy(priorityCategories, priorityCallSenders,
+                priorityMessageSenders,
+                suppressedVisualEffects);
+        mNotificationManager.setNotificationPolicy(mPolicy);
+    }
+
+    protected int getNewSuppressedEffects(boolean suppress, int effectType) {
+        int effects = mPolicy.suppressedVisualEffects;
+        if (suppress) {
+            effects |= effectType;
+        } else {
+            effects &= ~effectType;
+        }
+        return effects;
+    }
+
+    protected boolean isEffectAllowed(int effect) {
+        return (mPolicy.suppressedVisualEffects & effect) == 0;
+    }
+
+    protected void saveSenders(int category, int val) {
+        int priorityCallSenders = getPriorityCallSenders();
+        int priorityMessagesSenders = getPriorityMessageSenders();
+        int categorySenders = getPrioritySenders(category);
+
+        final boolean allowSenders = val != SOURCE_NONE;
+        final int allowSendersFrom = val == SOURCE_NONE ? categorySenders : val;
+
+        String stringCategory = "";
+        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
+            stringCategory = "Calls";
+            priorityCallSenders = allowSendersFrom;
+        }
+
+        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
+            stringCategory = "Messages";
+            priorityMessagesSenders = allowSendersFrom;
+        }
+
+        savePolicy(getNewPriorityCategories(allowSenders, category),
+            priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects);
+
+        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allow=" +
+                stringCategory + allowSenders + " allow" + stringCategory + "From="
+                + ZenModeConfig.sourceToString(allowSendersFrom));
+    }
+
+    protected String getSendersKey(int category) {
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                return getKeyFromSetting(SOURCE_NONE);
+            default:
+                int prioritySenders = getPrioritySenders(category);
+                return getKeyFromSetting(isPriorityCategoryEnabled(category)
+                        ? prioritySenders : SOURCE_NONE);
+            }
+    }
+
+    private int getPrioritySenders(int category) {
+        int categorySenders = -1;
+
+        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
+            return getPriorityCallSenders();
+        }
+
+        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
+            return getPriorityMessageSenders();
+        }
+
+        return categorySenders;
+    }
+
+    protected static String getKeyFromSetting(int contactType) {
+        switch (contactType) {
+            case NotificationManager.Policy.PRIORITY_SENDERS_ANY:
+                return ZEN_MODE_FROM_ANYONE;
+            case NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS:
+                return ZEN_MODE_FROM_CONTACTS;
+            case NotificationManager.Policy.PRIORITY_SENDERS_STARRED:
+                return ZEN_MODE_FROM_STARRED;
+            case SOURCE_NONE:
+            default:
+                return ZEN_MODE_FROM_NONE;
+        }
+    }
+
+    protected int getContactsSummary(int category) {
+        int contactType = -1;
+
+        // SOURCE_NONE can be used when in total silence or alarms only
+        // (policy is based on user's preferences but the UI displayed is based on zenMode)
+        if (category == SOURCE_NONE) {
+            return R.string.zen_mode_from_none;
+        }
+
+        if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
+            if (isPriorityCategoryEnabled(category)) {
+                contactType = getPriorityMessageSenders();
+            }
+        } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
+            if (isPriorityCategoryEnabled(category)) {
+                contactType = getPriorityCallSenders();
+            }
+        }
+
+        switch (contactType) {
+            case NotificationManager.Policy.PRIORITY_SENDERS_ANY:
+                return R.string.zen_mode_from_anyone;
+            case NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS:
+                return  R.string.zen_mode_from_contacts;
+            case NotificationManager.Policy.PRIORITY_SENDERS_STARRED:
+                return  R.string.zen_mode_from_starred;
+            case SOURCE_NONE:
+            default:
+                return R.string.zen_mode_from_none;
+        }
+    }
+
+    protected static int getSettingFromPrefKey(String key) {
+        switch (key) {
+            case ZEN_MODE_FROM_ANYONE:
+                return NotificationManager.Policy.PRIORITY_SENDERS_ANY;
+            case ZEN_MODE_FROM_CONTACTS:
+                return NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
+            case ZEN_MODE_FROM_STARRED:
+                return NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+            case ZEN_MODE_FROM_NONE:
+            default:
+                return SOURCE_NONE;
+        }
+    }
+
+    public boolean removeZenRule(String ruleId) {
+        return NotificationManager.from(mContext).removeAutomaticZenRule(ruleId);
+    }
+
+    protected String addZenRule(AutomaticZenRule rule) {
+        try {
+            String id = NotificationManager.from(mContext).addAutomaticZenRule(rule);
+            NotificationManager.from(mContext).getAutomaticZenRule(id);
+            return id;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeBehaviorPreferenceController.java b/src/com/android/settings/notification/ZenModeBehaviorPreferenceController.java
new file mode 100644
index 0000000..0e1f066
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeBehaviorPreferenceController.java
@@ -0,0 +1,37 @@
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeBehaviorPreferenceController extends
+        AbstractZenModePreferenceController implements PreferenceControllerMixin {
+
+    protected static final String KEY_BEHAVIOR_SETTINGS = "zen_mode_behavior_settings";
+    private final ZenModeSettings.SummaryBuilder mSummaryBuilder;
+
+    public ZenModeBehaviorPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY_BEHAVIOR_SETTINGS, lifecycle);
+        mSummaryBuilder = new ZenModeSettings.SummaryBuilder(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_BEHAVIOR_SETTINGS;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        preference.setSummary(mSummaryBuilder.getBehaviorSettingSummary(getPolicy(),
+                getZenMode()));
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeBehaviorSettings.java b/src/com/android/settings/notification/ZenModeBehaviorSettings.java
index 6fc97cf..dfe6786 100644
--- a/src/com/android/settings/notification/ZenModeBehaviorSettings.java
+++ b/src/com/android/settings/notification/ZenModeBehaviorSettings.java
@@ -16,303 +16,43 @@
 
 package com.android.settings.notification;
 
-import android.app.NotificationManager;
-import android.app.NotificationManager.Policy;
 import android.content.Context;
-import android.os.Bundle;
-import android.provider.SearchIndexableResource;
-import android.provider.Settings;
-import android.service.notification.ZenModeConfig;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.DropDownPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.Preference.OnPreferenceChangeListener;
-import android.support.v7.preference.PreferenceScreen;
-import android.util.Log;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
 
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.List;
 
 public class ZenModeBehaviorSettings extends ZenModeSettingsBase implements Indexable {
-    private static final String KEY_ALARMS = "zen_mode_alarms";
-    private static final String KEY_MEDIA = "zen_mode_media";
-    private static final String KEY_REMINDERS = "zen_mode_reminders";
-    private static final String KEY_EVENTS = "zen_mode_events";
-    private static final String KEY_MESSAGES = "zen_mode_messages";
-    private static final String KEY_CALLS = "zen_mode_calls";
-    private static final String KEY_REPEAT_CALLERS = "zen_mode_repeat_callers";
-    private static final String KEY_SCREEN_OFF = "zen_mode_screen_off";
-    private static final String KEY_SCREEN_ON = "zen_mode_screen_on";
-
-    private SwitchPreference mScreenOff;
-    private SwitchPreference mScreenOn;
-
-    private static final int SOURCE_NONE = -1;
-
-    private boolean mDisableListeners;
-    private SwitchPreference mReminders;
-    private SwitchPreference mEvents;
-    private DropDownPreference mMessages;
-    private DropDownPreference mCalls;
-    private SwitchPreference mRepeatCallers;
-    private SwitchPreference mAlarms;
-    private SwitchPreference mMediaSystemOther;
-
-    private Policy mPolicy;
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        addPreferencesFromResource(R.xml.zen_mode_behavior_settings);
-        final PreferenceScreen root = getPreferenceScreen();
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return buildPreferenceControllers(context, getLifecycle());
+    }
 
-        mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
-
-        mReminders = (SwitchPreference) root.findPreference(KEY_REMINDERS);
-        mReminders.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean val = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_REMINDERS,
-                        val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowReminders=" + val);
-                savePolicy(getNewPriorityCategories(val, Policy.PRIORITY_CATEGORY_REMINDERS),
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mEvents = (SwitchPreference) root.findPreference(KEY_EVENTS);
-        mEvents.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean val = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_EVENTS, val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowEvents=" + val);
-                savePolicy(getNewPriorityCategories(val, Policy.PRIORITY_CATEGORY_EVENTS),
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mMessages = (DropDownPreference) root.findPreference(KEY_MESSAGES);
-        addSources(mMessages);
-        mMessages.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return false;
-                final int val = Integer.parseInt((String) newValue);
-                final boolean allowMessages = val != SOURCE_NONE;
-                final int allowMessagesFrom =
-                        val == SOURCE_NONE ? mPolicy.priorityMessageSenders : val;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_MESSAGES, val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowMessages=" + allowMessages
-                        + " allowMessagesFrom=" + ZenModeConfig.sourceToString(allowMessagesFrom));
-                savePolicy(
-                        getNewPriorityCategories(allowMessages, Policy.PRIORITY_CATEGORY_MESSAGES),
-                        mPolicy.priorityCallSenders, allowMessagesFrom,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mCalls = (DropDownPreference) root.findPreference(KEY_CALLS);
-        addSources(mCalls);
-        mCalls.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return false;
-                final int val = Integer.parseInt((String) newValue);
-                final boolean allowCalls = val != SOURCE_NONE;
-                final int allowCallsFrom = val == SOURCE_NONE ? mPolicy.priorityCallSenders : val;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_CALLS, val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowCalls=" + allowCalls
-                        + " allowCallsFrom=" + ZenModeConfig.sourceToString(allowCallsFrom));
-                savePolicy(getNewPriorityCategories(allowCalls, Policy.PRIORITY_CATEGORY_CALLS),
-                        allowCallsFrom, mPolicy.priorityMessageSenders,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mRepeatCallers = (SwitchPreference) root.findPreference(KEY_REPEAT_CALLERS);
-        mRepeatCallers.setSummary(mContext.getString(R.string.zen_mode_repeat_callers_summary,
-                mContext.getResources().getInteger(com.android.internal.R.integer
-                        .config_zen_repeat_callers_threshold)));
-        mRepeatCallers.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean val = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_REPEAT_CALLS,
-                        val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowRepeatCallers=" + val);
-                int priorityCategories = getNewPriorityCategories(val,
-                        Policy.PRIORITY_CATEGORY_REPEAT_CALLERS);
-                savePolicy(priorityCategories, mPolicy.priorityCallSenders,
-                        mPolicy.priorityMessageSenders, mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mAlarms = (SwitchPreference) root.findPreference(KEY_ALARMS);
-        mAlarms.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean val = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_ALARMS, val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowAlarms=" + val);
-                savePolicy(getNewPriorityCategories(val, Policy.PRIORITY_CATEGORY_ALARMS),
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mMediaSystemOther = (SwitchPreference) root.findPreference(KEY_MEDIA);
-        mMediaSystemOther.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean val = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ALLOW_MEDIA, val);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowMediaSystemOther=" + val);
-                savePolicy(getNewPriorityCategories(val,
-                        Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER),
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        mPolicy.suppressedVisualEffects);
-                return true;
-            }
-        });
-
-        mScreenOff = (SwitchPreference) root.findPreference(KEY_SCREEN_OFF);
-        if (!getResources()
-                .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
-            mScreenOff.setSummary(R.string.zen_mode_screen_off_summary_no_led);
-        }
-
-        mScreenOff.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean bypass = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext,
-                        MetricsEvent.ACTION_ZEN_ALLOW_WHEN_SCREEN_OFF, !bypass);
-                if (DEBUG) Log.d(TAG, "onPrefChange suppressWhenScreenOff=" + !bypass);
-                savePolicy(mPolicy.priorityCategories,
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        getNewSuppressedEffects(!bypass, Policy.SUPPRESSED_EFFECT_SCREEN_OFF));
-                return true;
-            }
-        });
-
-        mScreenOn = (SwitchPreference) root.findPreference(KEY_SCREEN_ON);
-        mScreenOn.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                if (mDisableListeners) return true;
-                final boolean bypass = (Boolean) newValue;
-                mMetricsFeatureProvider.action(mContext,
-                        MetricsEvent.ACTION_ZEN_ALLOW_WHEN_SCREEN_ON, bypass);
-                if (DEBUG) Log.d(TAG, "onPrefChange allowWhenScreenOn=" + !bypass);
-                savePolicy(mPolicy.priorityCategories,
-                        mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders,
-                        getNewSuppressedEffects(!bypass, Policy.SUPPRESSED_EFFECT_SCREEN_ON));
-                return true;
-            }
-        });
-
-        updateControls();
+    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
+            Lifecycle lifecycle) {
+        List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new ZenModeAlarmsPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeMediaSystemOtherPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeEventsPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeRemindersPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeMessagesPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeCallsPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeRepeatCallersPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeScreenOnPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeScreenOffPreferenceController(context, lifecycle));
+        return controllers;
     }
 
     @Override
-    protected void onZenModeChanged() {
-        updateControls();
-    }
-
-    @Override
-    protected void onZenModeConfigChanged() {
-        mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
-        updateControls();
-    }
-
-    private void updateControlsPolicy() {
-        if (mCalls != null) {
-            mCalls.setValue(Integer.toString(
-                    isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS)
-                            ? mPolicy.priorityCallSenders : SOURCE_NONE));
-        }
-        mMessages.setValue(Integer.toString(
-                isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES)
-                        ? mPolicy.priorityMessageSenders : SOURCE_NONE));
-        mAlarms.setChecked(isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_ALARMS));
-        mMediaSystemOther.setChecked(isPriorityCategoryEnabled(
-                Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER));
-        mReminders.setChecked(isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS));
-        mEvents.setChecked(isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_EVENTS));
-        mRepeatCallers.setChecked(
-                isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS));
-        mRepeatCallers.setVisible(!isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS)
-                || mPolicy.priorityCallSenders != Policy.PRIORITY_SENDERS_ANY);
-
-    }
-
-    private void updateControls() {
-        mDisableListeners = true;
-        switch(mZenMode) {
-            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
-                toggleBasicNoInterruptions();
-                mAlarms.setChecked(false);
-                mMediaSystemOther.setChecked(false);
-                setTogglesEnabled(false);
-                break;
-            case Settings.Global.ZEN_MODE_ALARMS:
-                toggleBasicNoInterruptions();
-                mAlarms.setChecked(true);
-                mMediaSystemOther.setChecked(true);
-                setTogglesEnabled(false);
-                break;
-            default:
-                updateControlsPolicy();
-                setTogglesEnabled(true);
-        }
-
-        mScreenOff.setChecked(isEffectAllowed(Policy.SUPPRESSED_EFFECT_SCREEN_OFF));
-        mScreenOn.setChecked(isEffectAllowed(Policy.SUPPRESSED_EFFECT_SCREEN_ON));
-
-        mDisableListeners = false;
-    }
-
-    private void toggleBasicNoInterruptions() {
-        if (mCalls != null) {
-            mCalls.setValue(Integer.toString(SOURCE_NONE));
-        }
-        mMessages.setValue(Integer.toString(SOURCE_NONE));
-        mReminders.setChecked(false);
-        mEvents.setChecked(false);
-        mRepeatCallers.setChecked(false);
-    }
-
-    private void setTogglesEnabled(boolean enable) {
-        if (mCalls != null) {
-            mCalls.setEnabled(enable);
-        }
-        mMessages.setEnabled(enable);
-        mReminders.setEnabled(enable);
-        mEvents.setEnabled(enable);
-        mRepeatCallers.setEnabled(enable);
-        mAlarms.setEnabled(enable);
-        mMediaSystemOther.setEnabled(enable);
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_behavior_settings;
     }
 
     @Override
@@ -320,67 +60,29 @@
         return MetricsEvent.NOTIFICATION_ZEN_MODE_PRIORITY;
     }
 
-    private static void addSources(DropDownPreference pref) {
-        pref.setEntries(new CharSequence[]{
-                pref.getContext().getString(R.string.zen_mode_from_anyone),
-                pref.getContext().getString(R.string.zen_mode_from_contacts),
-                pref.getContext().getString(R.string.zen_mode_from_starred),
-                pref.getContext().getString(R.string.zen_mode_from_none),
-        });
-        pref.setEntryValues(new CharSequence[] {
-                Integer.toString(Policy.PRIORITY_SENDERS_ANY),
-                Integer.toString(Policy.PRIORITY_SENDERS_CONTACTS),
-                Integer.toString(Policy.PRIORITY_SENDERS_STARRED),
-                Integer.toString(SOURCE_NONE),
-        });
-    }
-
-    private boolean isPriorityCategoryEnabled(int categoryType) {
-        return (mPolicy.priorityCategories & categoryType) != 0;
-    }
-
-    private int getNewPriorityCategories(boolean allow, int categoryType) {
-        int priorityCategories = mPolicy.priorityCategories;
-        if (allow) {
-            priorityCategories |= categoryType;
-        } else {
-            priorityCategories &= ~categoryType;
-        }
-        return priorityCategories;
-    }
-
-    private void savePolicy(int priorityCategories, int priorityCallSenders,
-            int priorityMessageSenders, int suppressedVisualEffects) {
-        mPolicy = new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
-                suppressedVisualEffects);
-        NotificationManager.from(mContext).setNotificationPolicy(mPolicy);
-    }
-
     /**
      * For Search.
      */
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
                 @Override
-                public List<SearchIndexableResource> getXmlResourcesToIndex(
-                        Context context, boolean enabled) {
-                    final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.zen_mode_behavior_settings;
-                    return Arrays.asList(sir);
+                public List<String> getNonIndexableKeys(Context context) {
+                    final List<String> keys = super.getNonIndexableKeys(context);
+                    keys.add(ZenModeAlarmsPreferenceController.KEY);
+                    keys.add(ZenModeMediaSystemOtherPreferenceController.KEY);
+                    keys.add(ZenModeEventsPreferenceController.KEY);
+                    keys.add(ZenModeRemindersPreferenceController.KEY);
+                    keys.add(ZenModeMessagesPreferenceController.KEY);
+                    keys.add(ZenModeCallsPreferenceController.KEY);
+                    keys.add(ZenModeRepeatCallersPreferenceController.KEY);
+                    keys.add(ZenModeScreenOnPreferenceController.KEY);
+                    keys.add(ZenModeScreenOffPreferenceController.KEY);
+                    return keys;
                 }
-            };
 
-    private int getNewSuppressedEffects(boolean suppress, int effectType) {
-        int effects = mPolicy.suppressedVisualEffects;
-        if (suppress) {
-            effects |= effectType;
-        } else {
-            effects &= ~effectType;
-        }
-        return effects;
-    }
-
-    private boolean isEffectAllowed(int effect) {
-        return (mPolicy.suppressedVisualEffects & effect) == 0;
-    }
+            @Override
+            public List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+                return buildPreferenceControllers(context, null);
+            }
+        };
 }
diff --git a/src/com/android/settings/notification/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/ZenModeButtonPreferenceController.java
new file mode 100644
index 0000000..79115f2
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeButtonPreferenceController.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController
+        implements PreferenceControllerMixin {
+
+    protected static final String KEY = "zen_mode_settings_button_container";
+    private Button mZenButtonOn;
+    private Button mZenButtonOff;
+    private ZenModeBackend mBackend;
+
+    public ZenModeButtonPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        if (null == mZenButtonOn) {
+            mZenButtonOn = (Button) ((LayoutPreference) preference)
+                    .findViewById(R.id.zen_mode_settings_turn_on_button);
+            mZenButtonOn.setOnClickListener(v ->
+                    mBackend.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+        }
+
+        if (null == mZenButtonOff) {
+            mZenButtonOff = (Button) ((LayoutPreference) preference)
+                    .findViewById(R.id.zen_mode_settings_turn_off_button);
+            mZenButtonOff.setOnClickListener(v ->
+                    mBackend.setZenMode(Settings.Global.ZEN_MODE_OFF));
+        }
+
+        updateButtons();
+    }
+
+    private void updateButtons() {
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_ALARMS:
+            case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+                mZenButtonOff.setVisibility(View.VISIBLE);
+                mZenButtonOn.setVisibility(View.GONE);
+                break;
+            case Settings.Global.ZEN_MODE_OFF:
+            default:
+                mZenButtonOff.setVisibility(View.GONE);
+                mZenButtonOn.setVisibility(View.VISIBLE);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/notification/ZenModeCallsPreferenceController.java b/src/com/android/settings/notification/ZenModeCallsPreferenceController.java
new file mode 100644
index 0000000..48f9d1c
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeCallsPreferenceController.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeCallsPreferenceController extends
+        AbstractZenModePreferenceController {
+
+    protected static final String KEY = "zen_mode_calls";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeCallsPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                preference.setEnabled(false);
+                preference.setSummary(mBackend.getContactsSummary(mBackend.SOURCE_NONE));
+                break;
+            default:
+                preference.setEnabled(true);
+                preference.setSummary(mBackend.getContactsSummary(
+                        NotificationManager.Policy.PRIORITY_CATEGORY_CALLS));
+        }
+
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeCallsSettings.java b/src/com/android/settings/notification/ZenModeCallsSettings.java
new file mode 100644
index 0000000..6874dc0
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeCallsSettings.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.widget.RadioButtonPickerFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ZenModeCallsSettings extends RadioButtonPickerFragment {
+    private ZenModeBackend mBackend;
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_CALLS;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_calls_settings;
+    }
+
+    @Override
+    protected List<? extends RadioButtonPickerFragment.CandidateInfo> getCandidates() {
+        final String[] entries = entries();
+        final String[] values = keys();
+        final List<CallsCandidateInfo> candidates = new ArrayList<>();
+
+        if (entries == null || entries.length <= 0) return null;
+        if (values == null || values.length != entries.length) {
+            throw new IllegalArgumentException("Entries and values must be of the same length.");
+        }
+
+        for (int i = 0; i < entries.length; i++) {
+            candidates.add(new CallsCandidateInfo(entries[i], values[i]));
+        }
+
+        return candidates;
+    }
+
+    private String[] entries() {
+        return getResources().getStringArray(R.array.zen_mode_contacts_entries);
+    }
+
+    private String[] keys() {
+        return getResources().getStringArray(R.array.zen_mode_contacts_values);
+    }
+
+    @Override
+    protected String getDefaultKey() {
+        return mBackend.getSendersKey(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS);
+    }
+
+    @Override
+    protected boolean setDefaultKey(String key) {
+        mBackend.saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
+                mBackend.getSettingFromPrefKey(key));
+        return true;
+    }
+
+    private static final class CallsCandidateInfo extends RadioButtonPickerFragment.CandidateInfo {
+        private final String name;
+        private final String key;
+
+        CallsCandidateInfo(String title, String value) {
+            super(true);
+
+            name = title;
+            key = value;
+        }
+
+        @Override
+        public CharSequence loadLabel() {
+            return name;
+        }
+
+        @Override
+        public Drawable loadIcon() {
+            return null;
+        }
+
+        @Override
+        public String getKey() {
+            return key;
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeEventRuleSettings.java b/src/com/android/settings/notification/ZenModeEventRuleSettings.java
index 3361734..aa2cc3f 100644
--- a/src/com/android/settings/notification/ZenModeEventRuleSettings.java
+++ b/src/com/android/settings/notification/ZenModeEventRuleSettings.java
@@ -33,6 +33,7 @@
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -81,6 +82,16 @@
         mCreate = false;
     }
 
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_event_rule_settings;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return null;
+    }
+
     private void reloadCalendar() {
         mCalendars = getCalendars(mContext);
         ArrayList<CharSequence> entries = new ArrayList<>();
@@ -107,7 +118,6 @@
     @Override
     protected void onCreateInternal() {
         mCreate = true;
-        addPreferencesFromResource(R.xml.zen_mode_event_rule_settings);
         final PreferenceScreen root = getPreferenceScreen();
 
         mCalendar = (DropDownPreference) root.findPreference(KEY_CALENDAR);
@@ -243,5 +253,4 @@
         public String name;
         public int userId;
     }
-
 }
diff --git a/src/com/android/settings/notification/ZenModeEventsPreferenceController.java b/src/com/android/settings/notification/ZenModeEventsPreferenceController.java
new file mode 100644
index 0000000..335002c
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeEventsPreferenceController.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager.Policy;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeEventsPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener{
+
+    protected static final String KEY = "zen_mode_events";
+        private final ZenModeBackend mBackend;
+
+    public ZenModeEventsPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                pref.setEnabled(false);
+                pref.setChecked(false);
+                break;
+            default:
+                pref.setChecked(mBackend.isPriorityCategoryEnabled(
+                        Policy.PRIORITY_CATEGORY_EVENTS));
+                pref.setEnabled(true);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean allowEvents = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) {
+            Log.d(TAG, "onPrefChange allowEvents="
+                    + allowEvents);
+        }
+        mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_EVENTS, allowEvents);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeMediaSystemOtherPreferenceController.java b/src/com/android/settings/notification/ZenModeMediaSystemOtherPreferenceController.java
new file mode 100644
index 0000000..8893ff5
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeMediaSystemOtherPreferenceController.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager.Policy;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeMediaSystemOtherPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener{
+
+    protected static final String KEY = "zen_mode_media";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeMediaSystemOtherPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+                pref.setEnabled(false);
+                pref.setChecked(false);
+                break;
+            case Settings.Global.ZEN_MODE_ALARMS:
+                pref.setEnabled(false);
+                pref.setChecked(true);
+                break;
+            default:
+                pref.setEnabled(true);
+                pref.setChecked(mBackend.isPriorityCategoryEnabled(
+                        Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER));
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean allowMedia = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) {
+            Log.d(TAG,
+                    "onPrefChange allowMediaSystemOther=" + allowMedia);
+        }
+        mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, allowMedia);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeMessagesPreferenceController.java b/src/com/android/settings/notification/ZenModeMessagesPreferenceController.java
new file mode 100644
index 0000000..257a7c9
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeMessagesPreferenceController.java
@@ -0,0 +1,47 @@
+package com.android.settings.notification;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeMessagesPreferenceController extends
+        AbstractZenModePreferenceController {
+
+    protected static final String KEY = "zen_mode_messages";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeMessagesPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                preference.setEnabled(false);
+                preference.setSummary(mBackend.getContactsSummary(mBackend.SOURCE_NONE));
+                break;
+            default:
+                preference.setEnabled(true);
+                preference.setSummary(mBackend.getContactsSummary(
+                        NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES));
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeMessagesSettings.java b/src/com/android/settings/notification/ZenModeMessagesSettings.java
new file mode 100644
index 0000000..9cbf248
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeMessagesSettings.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.widget.RadioButtonPickerFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ZenModeMessagesSettings extends RadioButtonPickerFragment {
+    private ZenModeBackend mBackend;
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+    @Override
+    public int getMetricsCategory() {
+        return MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_MESSAGES;
+    }
+
+    @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_messages_settings;
+    }
+
+    @Override
+    protected List<? extends RadioButtonPickerFragment.CandidateInfo> getCandidates() {
+        final String[] entries = entries();
+        final String[] values = keys();
+        final List<MessagesCandidateInfo> candidates = new ArrayList<>();
+
+        if (entries == null || entries.length <= 0) return null;
+        if (values == null || values.length != entries.length) {
+            throw new IllegalArgumentException("Entries and values must be of the same length.");
+        }
+
+        for (int i = 0; i < entries.length; i++) {
+            candidates.add(new MessagesCandidateInfo(entries[i], values[i]));
+        }
+
+        return candidates;
+    }
+
+    private String[] entries() {
+        return getResources().getStringArray(R.array.zen_mode_contacts_entries);
+    }
+
+    private String[] keys() {
+        return getResources().getStringArray(R.array.zen_mode_contacts_values);
+    }
+
+    @Override
+    protected String getDefaultKey() {
+        return mBackend.getSendersKey(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES);
+    }
+
+    @Override
+    protected boolean setDefaultKey(String key) {
+        mBackend.saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+                mBackend.getSettingFromPrefKey(key));
+        return true;
+    }
+
+    private final class MessagesCandidateInfo extends RadioButtonPickerFragment.CandidateInfo {
+        private final String name;
+        private final String key;
+
+        MessagesCandidateInfo(String title, String value) {
+            super(true);
+
+            name = title;
+            key = value;
+        }
+
+        @Override
+        public CharSequence loadLabel() {
+            return name;
+        }
+
+        @Override
+        public Drawable loadIcon() {
+            return null;
+        }
+
+        @Override
+        public String getKey() {
+            return key;
+        }
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeRemindersPreferenceController.java b/src/com/android/settings/notification/ZenModeRemindersPreferenceController.java
new file mode 100644
index 0000000..a8fe220
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeRemindersPreferenceController.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeRemindersPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener{
+
+    protected static final String KEY = "zen_mode_reminders";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeRemindersPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                pref.setEnabled(false);
+                pref.setChecked(false);
+                break;
+            default:
+                pref.setEnabled(true);
+                pref.setChecked(mBackend.isPriorityCategoryEnabled(
+                        NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS));
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean allowReminders = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowReminders="
+                + allowReminders);
+        mBackend.saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS,
+                allowReminders);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceController.java b/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceController.java
new file mode 100644
index 0000000..eb52d55
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceController.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager.Policy;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeRepeatCallersPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener{
+
+
+    protected static final String KEY = "zen_mode_repeat_callers";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeRepeatCallersPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+
+        switch (getZenMode()) {
+            case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+            case Settings.Global.ZEN_MODE_ALARMS:
+                pref.setEnabled(false);
+                pref.setChecked(false);
+                break;
+            default:
+                boolean anyCallersCanBypassDnd = (mBackend.isPriorityCategoryEnabled(
+                        Policy.PRIORITY_CATEGORY_CALLS)
+                        && mBackend.getPriorityCallSenders() == Policy.PRIORITY_SENDERS_ANY);
+                // if any caller can bypass dnd then repeat callers preference is disabled
+                if (anyCallersCanBypassDnd) {
+                    pref.setEnabled(false);
+                    pref.setChecked(true);
+                } else {
+                    pref.setEnabled(true);
+                    pref.setChecked(mBackend.isPriorityCategoryEnabled(
+                            Policy.PRIORITY_CATEGORY_REPEAT_CALLERS));
+                }
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean allowRepeatCallers = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowRepeatCallers="
+                + allowRepeatCallers);
+        mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, allowRepeatCallers);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
index 83c6753..b0715dc 100644
--- a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
+++ b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.provider.SearchIndexableResource;
 import android.service.notification.ConditionProviderService;
 import android.support.v7.preference.DropDownPreference;
 import android.support.v7.preference.Preference;
@@ -43,8 +44,13 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
 import com.android.settings.widget.SwitchBar;
 
+import java.util.Arrays;
+import java.util.List;
+
 public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase
         implements SwitchBar.OnSwitchChangeListener {
     protected static final String TAG = ZenModeSettingsBase.TAG;
@@ -52,6 +58,8 @@
 
     private static final String KEY_RULE_NAME = "rule_name";
     private static final String KEY_ZEN_MODE = "zen_mode";
+    private static final String KEY_EVENT_RULE_SETTINGS = "zen_mode_event_rule_settings";
+    private static final String KEY_SCHEDULE_RULE_SETTINGS = "zen_mode_schedule_rule_settings";
 
     protected Context mContext;
     protected boolean mDisableListeners;
@@ -72,8 +80,6 @@
 
     @Override
     public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
         mContext = getActivity();
 
         final Intent intent = getActivity().getIntent();
@@ -96,6 +102,8 @@
             return;
         }
 
+        super.onCreate(icicle);
+
         setHasOptionsMenu(true);
 
         onCreateInternal();
@@ -129,7 +137,7 @@
                 if (zenMode == mRule.getInterruptionFilter()) return false;
                 if (DEBUG) Log.d(TAG, "onPrefChange zenMode=" + zenMode);
                 mRule.setInterruptionFilter(zenMode);
-                setZenRule(mId, mRule);
+                mBackend.setZenRule(mId, mRule);
                 return true;
             }
         });
@@ -172,7 +180,7 @@
         mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ENABLE_RULE, enabled);
         if (DEBUG) Log.d(TAG, "onSwitchChanged enabled=" + enabled);
         mRule.setEnabled(enabled);
-        setZenRule(mId, mRule);
+        mBackend.setZenRule(mId, mRule);
         if (enabled) {
             final int toastText = getEnabledToastText();
             if (toastText != 0) {
@@ -188,16 +196,12 @@
 
     protected void updateRule(Uri newConditionId) {
         mRule.setConditionId(newConditionId);
-        setZenRule(mId, mRule);
-    }
-
-    @Override
-    protected void onZenModeChanged() {
-        // noop
+        mBackend.setZenRule(mId, mRule);
     }
 
     @Override
     protected void onZenModeConfigChanged() {
+        super.onZenModeConfigChanged();
         if (!refreshRuleOrFinish()) {
             updateControls();
         }
@@ -225,7 +229,7 @@
             @Override
             public void onOk(String ruleName) {
                 mRule.setName(ruleName);
-                setZenRule(mId, mRule);
+                mBackend.setZenRule(mId, mRule);
             }
         }.show();
     }
@@ -250,7 +254,7 @@
                         mMetricsFeatureProvider.action(mContext,
                                 MetricsEvent.ACTION_ZEN_DELETE_RULE_OK);
                         mDeleting = true;
-                        removeZenRule(mId);
+                        mBackend.removeZenRule(mId);
                     }
                 })
                 .show();
@@ -294,4 +298,25 @@
         mDisableListeners = false;
     }
 
+    /**
+     * For Search.
+     */
+    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(
+                        Context context, boolean enabled) {
+                    final SearchIndexableResource sir = new SearchIndexableResource(context);
+                    // not indexable
+                    return Arrays.asList(sir);
+                }
+
+                @Override
+                public List<String> getNonIndexableKeys(Context context) {
+                    final List<String> keys = super.getNonIndexableKeys(context);
+                    keys.add(KEY_SCHEDULE_RULE_SETTINGS);
+                    keys.add(KEY_EVENT_RULE_SETTINGS);
+                    return keys;
+                }
+            };
 }
diff --git a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
index 72f6567..ab0349e 100644
--- a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
+++ b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
@@ -19,7 +19,6 @@
 import android.app.AlertDialog;
 import android.app.AutomaticZenRule;
 import android.app.Dialog;
-import android.app.DialogFragment;
 import android.app.FragmentManager;
 import android.app.TimePickerDialog;
 import android.content.Context;
@@ -40,10 +39,12 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.core.AbstractPreferenceController;
 
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Calendar;
+import java.util.List;
 
 public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase {
     private static final String KEY_DAYS = "days";
@@ -71,6 +72,16 @@
     }
 
     @Override
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_schedule_rule_settings;
+    }
+
+    @Override
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return null;
+    }
+
+    @Override
     protected String getZenModeDependency() {
         return mDays.getKey();
     }
@@ -82,7 +93,6 @@
 
     @Override
     protected void onCreateInternal() {
-        addPreferencesFromResource(R.xml.zen_mode_schedule_rule_settings);
         final PreferenceScreen root = getPreferenceScreen();
 
         mDays = root.findPreference(KEY_DAYS);
@@ -306,5 +316,4 @@
             boolean onSetTime(int hour, int minute);
         }
     }
-
 }
diff --git a/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java b/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java
new file mode 100644
index 0000000..8099691
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager.Policy;
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeScreenOffPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener{
+
+    protected static final String KEY = "zen_mode_screen_off";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeScreenOffPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        pref.setChecked(mBackend.isEffectAllowed(Policy.SUPPRESSED_EFFECT_SCREEN_OFF));
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean bypass = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowWhenScreenOff="
+                + !bypass);
+        mBackend.saveVisualEffectsPolicy(Policy.SUPPRESSED_EFFECT_SCREEN_OFF, bypass);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java b/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java
new file mode 100644
index 0000000..fac9bfd
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.NotificationManager.Policy;
+import android.content.Context;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+public class ZenModeScreenOnPreferenceController extends
+        AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener{
+
+    protected static final String KEY = "zen_mode_screen_on";
+    private final ZenModeBackend mBackend;
+
+    public ZenModeScreenOnPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context, KEY, lifecycle);
+        mBackend = ZenModeBackend.getInstance(context);
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        super.updateState(preference);
+
+        SwitchPreference pref = (SwitchPreference) preference;
+        pref.setChecked(mBackend.isEffectAllowed(Policy.SUPPRESSED_EFFECT_SCREEN_ON));
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        final boolean bypass = (Boolean) newValue;
+        if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowWhenScreenOn="
+                + !bypass);
+
+        mBackend.saveVisualEffectsPolicy(Policy.SUPPRESSED_EFFECT_SCREEN_ON, bypass);
+        return true;
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java
index 2699cfd..6cdf00b 100644
--- a/src/com/android/settings/notification/ZenModeSettings.java
+++ b/src/com/android/settings/notification/ZenModeSettings.java
@@ -20,56 +20,28 @@
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.content.Context;
-import android.os.Bundle;
 import android.provider.SearchIndexableResource;
 import android.provider.Settings;
-import android.service.notification.ZenModeConfig;
 import android.support.annotation.VisibleForTesting;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.search.Indexable;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
-public class ZenModeSettings extends ZenModeSettingsBase implements Indexable {
-
-    private static final String KEY_BEHAVIOR_SETTINGS = "zen_mode_behavior_settings";
-    private static final String KEY_AUTOMATION_SETTINGS = "zen_mode_automation_settings";
-
-    private Preference mBehaviorSettings;
-    private Preference mAutomationSettings;
-    private Policy mPolicy;
-    private SummaryBuilder mSummaryBuilder;
+public class ZenModeSettings extends ZenModeSettingsBase {
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        addPreferencesFromResource(R.xml.zen_mode_settings);
-        final PreferenceScreen root = getPreferenceScreen();
-
-        mBehaviorSettings = root.findPreference(KEY_BEHAVIOR_SETTINGS);
-        mAutomationSettings = root.findPreference(KEY_AUTOMATION_SETTINGS);
-        mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
-        mSummaryBuilder = new SummaryBuilder(getContext());
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (isUiRestricted()) {
-            return;
-        }
-        updateControls();
+    protected int getPreferenceScreenResId() {
+        return R.xml.zen_mode_settings;
     }
 
     @Override
@@ -78,27 +50,8 @@
     }
 
     @Override
-    protected void onZenModeChanged() {
-        updateControls();
-    }
-
-    @Override
-    protected void onZenModeConfigChanged() {
-        mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
-        updateControls();
-    }
-
-    private void updateControls() {
-        updateBehaviorSettingsSummary();
-        updateAutomationSettingsSummary();
-    }
-
-    private void updateBehaviorSettingsSummary() {
-        mBehaviorSettings.setSummary(mSummaryBuilder.getBehaviorSettingSummary(mPolicy, mZenMode));
-    }
-
-    private void updateAutomationSettingsSummary() {
-        mAutomationSettings.setSummary(mSummaryBuilder.getAutomaticRulesSummary());
+    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+        return buildPreferenceControllers(context, getLifecycle());
     }
 
     @Override
@@ -106,6 +59,16 @@
         return R.string.help_uri_interruptions;
     }
 
+
+    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
+            Lifecycle lifecycle) {
+        List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new ZenModeBehaviorPreferenceController(context, lifecycle));
+        controllers.add(new ZenModeAutomationPreferenceController(context));
+        controllers.add(new ZenModeButtonPreferenceController(context, lifecycle));
+        return controllers;
+    }
+
     public static class SummaryBuilder {
 
         private Context mContext;
@@ -227,42 +190,29 @@
         }
     }
 
-    private static final Comparator<Entry<String,AutomaticZenRule>> RULE_COMPARATOR =
-            new Comparator<Map.Entry<String,AutomaticZenRule>>() {
-                @Override
-                public int compare(Map.Entry<String,AutomaticZenRule> lhs,
-                        Map.Entry<String,AutomaticZenRule> rhs) {
-                    int byDate = Long.compare(lhs.getValue().getCreationTime(),
-                            rhs.getValue().getCreationTime());
-                    if (byDate != 0) {
-                        return byDate;
-                    } else {
-                        return key(lhs.getValue()).compareTo(key(rhs.getValue()));
-                    }
-                }
-
-                private String key(AutomaticZenRule rule) {
-                    final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId())
-                            ? 1
-                            : ZenModeConfig.isValidEventConditionId(rule.getConditionId())
-                                    ? 2
-                                    : 3;
-                    return type + rule.getName().toString();
-                }
-            };
-
     /**
      * For Search.
      */
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider() {
-                @Override
-                public List<SearchIndexableResource> getXmlResourcesToIndex(
-                        Context context, boolean enabled) {
-                    final SearchIndexableResource sir = new SearchIndexableResource(context);
-                    sir.xmlResId = R.xml.zen_mode_settings;
-                    return Arrays.asList(sir);
-                }
-            };
+        @Override
+        public List<SearchIndexableResource> getXmlResourcesToIndex(
+                Context context, boolean enabled) {
+            final SearchIndexableResource sir = new SearchIndexableResource(context);
+            sir.xmlResId = R.xml.zen_mode_settings;
+            return Arrays.asList(sir);
+        }
 
+        @Override
+        public List<String> getNonIndexableKeys(Context context) {
+            List<String> keys = super.getNonIndexableKeys(context);
+            keys.add(ZenModeButtonPreferenceController.KEY);
+            return keys;
+        }
+
+        @Override
+        public List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+            return buildPreferenceControllers(context, null);
+        }
+    };
 }
diff --git a/src/com/android/settings/notification/ZenModeSettingsBase.java b/src/com/android/settings/notification/ZenModeSettingsBase.java
index 6a9431e..2aecae4 100644
--- a/src/com/android/settings/notification/ZenModeSettingsBase.java
+++ b/src/com/android/settings/notification/ZenModeSettingsBase.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.notification;
 
-import android.app.AutomaticZenRule;
-import android.app.NotificationManager;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -26,17 +24,11 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.provider.Settings.Global;
-import android.service.notification.ZenModeConfig;
 import android.util.Log;
 
-import com.android.settings.RestrictedSettingsFragment;
+import com.android.settings.dashboard.RestrictedDashboardFragment;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-abstract public class ZenModeSettingsBase extends RestrictedSettingsFragment {
+abstract public class ZenModeSettingsBase extends RestrictedDashboardFragment {
     protected static final String TAG = "ZenModeSettings";
     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -44,30 +36,33 @@
     private final SettingsObserver mSettingsObserver = new SettingsObserver();
 
     protected Context mContext;
-    protected Set<Map.Entry<String, AutomaticZenRule>> mRules;
     protected int mZenMode;
 
-    abstract protected void onZenModeChanged();
-    abstract protected void onZenModeConfigChanged();
+    protected ZenModeBackend mBackend;
+
+    protected void onZenModeConfigChanged() {};
 
     public ZenModeSettingsBase() {
         super(UserManager.DISALLOW_ADJUST_VOLUME);
     }
 
     @Override
+    protected String getLogTag() {
+        return TAG;
+    }
+
+    @Override
     public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
         mContext = getActivity();
+        mBackend = ZenModeBackend.getInstance(mContext);
+        super.onCreate(icicle);
         updateZenMode(false /*fireChanged*/);
-        maybeRefreshRules(true, false /*fireChanged*/);
-        if (DEBUG) Log.d(TAG, "Loaded mRules=" + mRules);
     }
 
     @Override
     public void onResume() {
         super.onResume();
         updateZenMode(true /*fireChanged*/);
-        maybeRefreshRules(true, true /*fireChanged*/);
         mSettingsObserver.register();
         if (isUiRestricted()) {
             if (isUiRestrictedByOnlyAdmin()) {
@@ -89,56 +84,7 @@
         final int zenMode = Settings.Global.getInt(getContentResolver(), Global.ZEN_MODE, mZenMode);
         if (zenMode == mZenMode) return;
         mZenMode = zenMode;
-        if (DEBUG) Log.d(TAG, "updateZenMode mZenMode=" + mZenMode);
-        if (fireChanged) {
-            onZenModeChanged();
-        }
-    }
-
-    protected String addZenRule(AutomaticZenRule rule) {
-        try {
-            String id = NotificationManager.from(mContext).addAutomaticZenRule(rule);
-            final AutomaticZenRule savedRule =
-                    NotificationManager.from(mContext).getAutomaticZenRule(id);
-            maybeRefreshRules(savedRule != null, true);
-            return id;
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    protected boolean setZenRule(String id, AutomaticZenRule rule) {
-        final boolean success =
-                NotificationManager.from(mContext).updateAutomaticZenRule(id, rule);
-        maybeRefreshRules(success, true);
-        return success;
-    }
-
-    protected boolean removeZenRule(String id) {
-        final boolean success =
-                NotificationManager.from(mContext).removeAutomaticZenRule(id);
-        maybeRefreshRules(success, true);
-        return success;
-    }
-
-    protected void maybeRefreshRules(boolean success, boolean fireChanged) {
-        if (success) {
-            mRules = getZenModeRules();
-            if (DEBUG) Log.d(TAG, "Refreshed mRules=" + mRules);
-            if (fireChanged) {
-                onZenModeConfigChanged();
-            }
-        }
-    }
-
-    protected void setZenMode(int zenMode, Uri conditionId) {
-        NotificationManager.from(mContext).setZenMode(zenMode, conditionId, TAG);
-    }
-
-    private Set<Map.Entry<String, AutomaticZenRule>> getZenModeRules() {
-        Map<String, AutomaticZenRule> ruleMap
-                = NotificationManager.from(mContext).getAutomaticZenRules();
-        return ruleMap.entrySet();
+        if (DEBUG) Log.d(TAG, "updateZenMode mZenMode=" + mZenMode + " " + fireChanged);
     }
 
     private final class SettingsObserver extends ContentObserver {
@@ -165,7 +111,8 @@
                 updateZenMode(true /*fireChanged*/);
             }
             if (ZEN_MODE_CONFIG_ETAG_URI.equals(uri)) {
-                maybeRefreshRules(true, true /*fireChanged*/);
+                mBackend.updatePolicy();
+                onZenModeConfigChanged();
             }
         }
     }
diff --git a/src/com/android/settings/notification/ZenRulePreference.java b/src/com/android/settings/notification/ZenRulePreference.java
new file mode 100644
index 0000000..a838f29
--- /dev/null
+++ b/src/com/android/settings/notification/ZenRulePreference.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.app.AlertDialog;
+import android.app.AutomaticZenRule;
+import android.app.NotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.service.notification.ZenModeConfig;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.utils.ManagedServiceSettings;
+import com.android.settings.utils.ZenServiceListing;
+import com.android.settingslib.TwoTargetPreference;
+
+import java.util.Map;
+
+public class ZenRulePreference extends TwoTargetPreference {
+    protected final ManagedServiceSettings.Config CONFIG =
+            ZenModeAutomationSettings.getConditionProviderConfig();
+    final CharSequence mName;
+    final String mId;
+    boolean appExists;
+    final PreferenceCategory mParent;
+    final Preference mPref;
+    final Context mContext;
+    final ZenModeBackend mBackend;
+    final ZenServiceListing mServiceListing;
+    final PackageManager mPm;
+
+    public ZenRulePreference(Context context,
+            final Map.Entry<String, AutomaticZenRule> ruleEntry,
+            PreferenceCategory prefCategory) {
+        super(context);
+
+        mBackend = ZenModeBackend.getInstance(context);
+        mContext = context;
+        final AutomaticZenRule rule = ruleEntry.getValue();
+        mName = rule.getName();
+        mId = ruleEntry.getKey();
+        mParent = prefCategory;
+        mPm = mContext.getPackageManager();
+        mServiceListing = new ZenServiceListing(mContext, CONFIG);
+        mServiceListing.reloadApprovedServices();
+        mPref = this;
+
+        setAttributes(rule);
+    }
+
+    @Override
+    protected int getSecondTargetResId() {
+        return R.layout.zen_rule_widget;
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+
+        View v = view.findViewById(R.id.delete_zen_rule);
+        if (v != null) {
+            v.setOnClickListener(mDeleteListener);
+        }
+    }
+
+    private final View.OnClickListener mDeleteListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            showDeleteRuleDialog(mId, mName, mParent, mPref);
+        }
+    };
+
+    private void showDeleteRuleDialog(final String ruleId, final CharSequence ruleName,
+            PreferenceCategory parent, Preference pref) {
+        new AlertDialog.Builder(mContext)
+                .setMessage(mContext.getResources().getString(
+                        R.string.zen_mode_delete_rule_confirmation, ruleName))
+                .setNegativeButton(R.string.cancel, null)
+                .setPositiveButton(R.string.zen_mode_delete_rule_button,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                mBackend.removeZenRule(ruleId);
+                                parent.removePreference(pref);
+                            }
+                        })
+                .show();
+    }
+
+    protected void setAttributes(AutomaticZenRule rule) {
+        final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(
+                rule.getConditionId());
+        final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.getConditionId());
+        final boolean isSystemRule = isSchedule || isEvent;
+
+        try {
+            ApplicationInfo info = mPm.getApplicationInfo(rule.getOwner().getPackageName(), 0);
+            setSummary(computeRuleSummary(rule, isSystemRule, info.loadLabel(mPm)));
+        } catch (PackageManager.NameNotFoundException e) {
+            appExists = false;
+            return;
+        }
+
+        appExists = true;
+        setTitle(rule.getName());
+        setPersistent(false);
+
+        final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION
+                : isEvent ? ZenModeEventRuleSettings.ACTION : "";
+        ServiceInfo si = mServiceListing.findService(rule.getOwner());
+        ComponentName settingsActivity = AbstractZenModeAutomaticRulePreferenceController.
+                getSettingsActivity(si);
+        setIntent(AbstractZenModeAutomaticRulePreferenceController.getRuleIntent(action,
+                settingsActivity, mId));
+        setSelectable(settingsActivity != null || isSystemRule);
+        setKey(mId);
+    }
+
+    private String computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule,
+            CharSequence providerLabel) {
+        final String mode = computeZenModeCaption(mContext.getResources(),
+                rule.getInterruptionFilter());
+        final String ruleState = (rule == null || !rule.isEnabled())
+                ? mContext.getResources().getString(R.string.switch_off_text)
+                : mContext.getResources().getString(
+                        R.string.zen_mode_rule_summary_enabled_combination, mode);
+
+        return ruleState;
+    }
+
+    private static String computeZenModeCaption(Resources res, int zenMode) {
+        switch (zenMode) {
+            case NotificationManager.INTERRUPTION_FILTER_ALARMS:
+                return res.getString(R.string.zen_mode_option_alarms);
+            case NotificationManager.INTERRUPTION_FILTER_PRIORITY:
+                return res.getString(R.string.zen_mode_option_important_interruptions);
+            case NotificationManager.INTERRUPTION_FILTER_NONE:
+                return res.getString(R.string.zen_mode_option_no_interruptions);
+            default:
+                return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/notification/ZenRuleSelectionDialog.java b/src/com/android/settings/notification/ZenRuleSelectionDialog.java
index 3c49dcb..0c725ed 100644
--- a/src/com/android/settings/notification/ZenRuleSelectionDialog.java
+++ b/src/com/android/settings/notification/ZenRuleSelectionDialog.java
@@ -164,7 +164,8 @@
             if (DEBUG) Log.d(TAG, "Services reloaded: count=" + services.size());
             Set<ZenRuleInfo> externalRuleTypes = new TreeSet<>(RULE_TYPE_COMPARATOR);
             for (ServiceInfo serviceInfo : services) {
-                final ZenRuleInfo ri = ZenModeAutomationSettings.getRuleInfo(mPm, serviceInfo);
+                final ZenRuleInfo ri = AbstractZenModeAutomaticRulePreferenceController.
+                        getRuleInfo(mPm, serviceInfo);
                 if (ri != null && ri.configurationActivity != null
                         && mNm.isNotificationPolicyAccessGrantedForPackage(ri.packageName)
                         && (ri.ruleInstanceLimit <= 0 || ri.ruleInstanceLimit
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 46e693d..0aa2ab7 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -69,6 +69,8 @@
 import com.android.settings.notification.SoundSettings;
 import com.android.settings.notification.ZenModeAutomationSettings;
 import com.android.settings.notification.ZenModeBehaviorSettings;
+import com.android.settings.notification.ZenModeEventRuleSettings;
+import com.android.settings.notification.ZenModeScheduleRuleSettings;
 import com.android.settings.notification.ZenModeSettings;
 import com.android.settings.print.PrintSettingsFragment;
 import com.android.settings.security.EncryptionAndCredential;
@@ -168,6 +170,8 @@
         addIndex(LockscreenDashboardFragment.class);
         addIndex(ZenModeBehaviorSettings.class);
         addIndex(ZenModeAutomationSettings.class);
+        addIndex(ZenModeEventRuleSettings.class);
+        addIndex(ZenModeScheduleRuleSettings.class);
     }
 
     private SearchIndexableResources() {
diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable
index 8c44112..46414c7 100644
--- a/tests/robotests/assets/grandfather_not_implementing_indexable
+++ b/tests/robotests/assets/grandfather_not_implementing_indexable
@@ -3,7 +3,6 @@
 com.android.settings.deviceinfo.PrivateVolumeForget
 com.android.settings.inputmethod.SpellCheckersSettings
 com.android.settings.inputmethod.KeyboardLayoutPickerFragment
-com.android.settings.notification.ZenModeEventRuleSettings
 com.android.settings.fuelgauge.InactiveApps
 com.android.settings.accessibility.CaptionPropertiesFragment
 com.android.settings.accessibility.AccessibilitySettingsForSetupWizard
@@ -30,7 +29,6 @@
 com.android.settings.accessibility.ToggleAutoclickPreferenceFragment
 com.android.settings.applications.AppLaunchSettings
 com.android.settings.applications.ProcessStatsUi
-com.android.settings.notification.ZenModeScheduleRuleSettings
 com.android.settings.datausage.BillingCycleSettings
 com.android.settings.notification.NotificationStation
 com.android.settings.print.PrintJobSettingsFragment
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeAlarmsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeAlarmsPreferenceControllerTest.java
new file mode 100644
index 0000000..06ca70a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeAlarmsPreferenceControllerTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeAlarmsPreferenceControllerTest {
+    private ZenModeAlarmsPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private Context mContext;
+    private ContentResolver mContentResolver;
+
+    private final boolean ALARMS_SETTINGS = true;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+        mController = new ZenModeAlarmsPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(true);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS)).thenReturn(ALARMS_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setChecked(ALARMS_SETTINGS);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableAlarms() {
+        boolean allowAlarms = true;
+        mController.onPreferenceChange(mockPref, allowAlarms);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS,
+                allowAlarms);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableAlarms() {
+        boolean allowAlarms = false;
+        mController.onPreferenceChange(mockPref, allowAlarms);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS,
+                allowAlarms);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeButtonPreferenceControllerTest.java
new file mode 100644
index 0000000..a1b4dab
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeButtonPreferenceControllerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_OFF;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeButtonPreferenceControllerTest {
+    private ZenModeButtonPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private Preference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private Button mZenButtonOn;
+    @Mock
+    private Button mZenButtonOff;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        mController = new ZenModeButtonPreferenceController(mContext, mock(Lifecycle.class));
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+        ReflectionHelpers.setField(mController, "mZenButtonOn", mZenButtonOn);
+        ReflectionHelpers.setField(mController, "mZenButtonOff", mZenButtonOff);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mZenButtonOn).setVisibility(View.GONE);
+        verify(mZenButtonOff).setVisibility(View.VISIBLE);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mZenButtonOn).setVisibility(View.GONE);
+        verify(mZenButtonOff).setVisibility(View.VISIBLE);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mZenButtonOn).setVisibility(View.GONE);
+        verify(mZenButtonOff).setVisibility(View.VISIBLE);
+    }
+
+    @Test
+    public void updateState_ZenOff() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_OFF);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mZenButtonOn).setVisibility(View.VISIBLE);
+        verify(mZenButtonOff).setVisibility(View.GONE);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeCallsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeCallsPreferenceControllerTest.java
new file mode 100644
index 0000000..ea7e9f5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeCallsPreferenceControllerTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeCallsPreferenceControllerTest {
+    private ZenModeCallsPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private Preference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    private final boolean CALLS_SETTINGS = true;
+    private final int MOCK_CALLS_SENDERS = NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+    private final int SUMMARY_ID_MOCK_CALLS_SENDERS = R.string.zen_mode_from_starred;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+        when(mBackend.getContactsSummary(ZenModeBackend.SOURCE_NONE))
+                .thenCallRealMethod();
+        when(mBackend.getContactsSummary(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenCallRealMethod();
+
+        mController = new ZenModeCallsPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenReturn(false);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setSummary(R.string.zen_mode_from_none);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setSummary(R.string.zen_mode_from_none);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenReturn(CALLS_SETTINGS);
+        when(mBackend.getPriorityCallSenders()).thenReturn(MOCK_CALLS_SENDERS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setSummary(SUMMARY_ID_MOCK_CALLS_SENDERS);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeCallsTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeCallsTest.java
new file mode 100644
index 0000000..3cc87a8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeCallsTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ZenModeCallsTest {
+    private ZenModeCallsSettings mCalls;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private ZenModeBackend mBackend;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Activity mActivity;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private NotificationManager mNotificationManager;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mActivity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mActivity.getSystemService(Context.NOTIFICATION_SERVICE))
+                .thenReturn(mNotificationManager);
+        FakeFeatureFactory.setupForTest(mActivity);
+
+        mCalls = new ZenModeCallsSettings();
+        mCalls.onAttach((Context)mActivity);
+
+        ReflectionHelpers.setField(mCalls, "mBackend", mBackend);
+    }
+
+    @Test
+    public void getDefaultKeyReturnsBasedOnZen() {
+        when(mBackend.getSendersKey(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenCallRealMethod();
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
+        assertThat(mCalls.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(mBackend.SOURCE_NONE));
+
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_ALARMS);
+        assertThat(mCalls.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(mBackend.SOURCE_NONE));
+
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenReturn(true);
+        when(mBackend.getPriorityMessageSenders())
+                .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+        assertThat(mCalls.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(
+                        NotificationManager.Policy.PRIORITY_SENDERS_ANY));
+    }
+
+    @Test
+    public void setAnySender() {
+        String key = mBackend.getKeyFromSetting(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+        mCalls.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setNoSender() {
+        String key = mBackend.getKeyFromSetting(ZenModeBackend.SOURCE_NONE);
+        mCalls.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setStarredSenders() {
+        String key = mBackend.getKeyFromSetting(
+                NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
+        mCalls.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setContactsOnlySenders() {
+        String key = mBackend.getKeyFromSetting(
+                NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS);
+        mCalls.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS,
+                mBackend.getSettingFromPrefKey(key));
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeEventsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeEventsPreferenceControllerTest.java
new file mode 100644
index 0000000..b527abf
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeEventsPreferenceControllerTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.shadows.ShadowApplication;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeEventsPreferenceControllerTest {
+    private ZenModeEventsPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    private final boolean EVENTS_SETTINGS = true;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeEventsPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS)).thenReturn(EVENTS_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setChecked(EVENTS_SETTINGS);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableEvents() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS,
+                allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableEvents() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS,
+                allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeMediaPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeMediaPreferenceControllerTest.java
new file mode 100644
index 0000000..976d6d4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeMediaPreferenceControllerTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeMediaPreferenceControllerTest {
+    private ZenModeMediaSystemOtherPreferenceController mController;
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Context mContext;
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+
+    private final boolean MEDIA_SETTINGS = true;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeMediaSystemOtherPreferenceController(mContext,
+                mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(true);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER)).
+                thenReturn(MEDIA_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setChecked(MEDIA_SETTINGS);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableEvents() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableEvents() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER, allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeMessagesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeMessagesPreferenceControllerTest.java
new file mode 100644
index 0000000..c06f93f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeMessagesPreferenceControllerTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeMessagesPreferenceControllerTest {
+    private ZenModeMessagesPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private Preference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    private final boolean MESSAGES_SETTINGS = true;
+    private final int MOCK_MESSAGES_SENDERS = NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+    private final int SUMMARY_ID_MOCK_MESSAGES_SENDERS = R.string.zen_mode_from_starred;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        when(mBackend.getPriorityMessageSenders()).thenReturn(MOCK_MESSAGES_SENDERS);
+        when(mBackend.getContactsSummary(ZenModeBackend.SOURCE_NONE))
+                .thenCallRealMethod();
+        when(mBackend.getContactsSummary(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenCallRealMethod();
+
+        mController = new ZenModeMessagesPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenReturn(false);
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setSummary(R.string.zen_mode_from_none);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final Preference mockPref = mock(Preference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setSummary(R.string.zen_mode_from_none);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenReturn(MESSAGES_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setSummary(SUMMARY_ID_MOCK_MESSAGES_SENDERS);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeMessagesTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeMessagesTest.java
new file mode 100644
index 0000000..fe92570
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeMessagesTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+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.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ZenModeMessagesTest {
+    private ZenModeMessagesSettings mMessages;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private ZenModeBackend mBackend;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Activity mActivity;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private NotificationManager mNotificationManager;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mActivity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mActivity.getSystemService(Context.NOTIFICATION_SERVICE))
+                .thenReturn(mNotificationManager);
+        FakeFeatureFactory.setupForTest(mActivity);
+
+        mMessages = new ZenModeMessagesSettings();
+        mMessages.onAttach((Context)mActivity);
+
+        ReflectionHelpers.setField(mMessages, "mBackend", mBackend);
+    }
+
+    @Test
+    public void getDefaultKeyReturnsBasedOnZen() {
+        when(mBackend.getSendersKey(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenCallRealMethod();
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
+        assertThat(mMessages.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(mBackend.SOURCE_NONE));
+
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_ALARMS);
+        assertThat(mMessages.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(mBackend.SOURCE_NONE));
+
+        when(mBackend.getZenMode()).thenReturn(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES))
+                .thenReturn(true);
+        when(mBackend.getPriorityMessageSenders())
+                .thenReturn(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+        assertThat(mMessages.getDefaultKey())
+                .isEqualTo(mBackend.getKeyFromSetting(
+                        NotificationManager.Policy.PRIORITY_SENDERS_ANY));
+    }
+
+    @Test
+    public void setAnySender() {
+        String key = mBackend.getKeyFromSetting(NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+        mMessages.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setNoSender() {
+        String key = mBackend.getKeyFromSetting(ZenModeBackend.SOURCE_NONE);
+        mMessages.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setStarredSenders() {
+        String key = mBackend.getKeyFromSetting(
+                NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
+        mMessages.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+                mBackend.getSettingFromPrefKey(key));
+    }
+
+    @Test
+    public void setContactsOnlySenders() {
+        String key = mBackend.getKeyFromSetting(
+                NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS);
+        mMessages.setDefaultKey(key);
+        verify(mBackend).saveSenders(NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+                mBackend.getSettingFromPrefKey(key));
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java
index 1d71a8a..0a28673 100644
--- a/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/ZenModePreferenceControllerTest.java
@@ -43,7 +43,7 @@
 import static org.mockito.Mockito.when;
 
 @RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
 public class ZenModePreferenceControllerTest {
 
     @Mock
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeRemindersPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeRemindersPreferenceControllerTest.java
new file mode 100644
index 0000000..9d8b011
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeRemindersPreferenceControllerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeRemindersPreferenceControllerTest {
+    private ZenModeRemindersPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private ContentResolver mContentResolver;
+    private Context mContext;
+    private final boolean REMINDERS_SETTINGS = true;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeRemindersPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS)).
+                thenReturn(REMINDERS_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setChecked(REMINDERS_SETTINGS);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableReminders() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS,
+                allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableReminders() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS,
+                allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceControllerTest.java
new file mode 100644
index 0000000..d4ee9bc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceControllerTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static android.provider.Settings.Global.ZEN_MODE;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeRepeatCallersPreferenceControllerTest {
+    private ZenModeRepeatCallersPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+    private ContentResolver mContentResolver;
+    private Context mContext;
+
+    private final boolean REPEAT_CALLERS_SETTINGS = true;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        mContentResolver = RuntimeEnvironment.application.getContentResolver();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeRepeatCallersPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+
+        when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn(
+                mockPref);
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void updateState_TotalSilence() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_NO_INTERRUPTIONS);
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_AlarmsOnly() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_ALARMS);
+
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(false);
+    }
+
+    @Test
+    public void updateState_Priority() {
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)).
+                thenReturn(REPEAT_CALLERS_SETTINGS);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(true);
+        verify(mockPref).setChecked(REPEAT_CALLERS_SETTINGS);
+    }
+
+    @Test
+    public void updateState_Priority_anyCallers() {
+        boolean mockPriorityState = false;
+        Settings.Global.putInt(mContentResolver, ZEN_MODE, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        when(mBackend.isPriorityCategoryEnabled(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS))
+                .thenReturn(true);
+        when(mBackend.getPriorityCallSenders()).thenReturn(
+                NotificationManager.Policy.PRIORITY_SENDERS_ANY);
+        when(mBackend.isPriorityCategoryEnabled(
+                NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS))
+                .thenReturn(mockPriorityState);
+
+        mController.updateState(mockPref);
+
+        verify(mockPref).setEnabled(false);
+        verify(mockPref).setChecked(true);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableRepeatCallers() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(
+                NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableRepeatCallers() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveSoundPolicy(
+                NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java
index f8e5775..89b3f2a 100644
--- a/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeScheduleRuleSettingsTest.java
@@ -105,11 +105,6 @@
         protected Object getSystemService(final String name) {
             return null;
         }
-
-        @Override
-        protected void maybeRefreshRules(boolean success, boolean fireChanged) {
-            //do nothing
-        }
     }
 
 }
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeScreenOffPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeScreenOffPreferenceControllerTest.java
new file mode 100644
index 0000000..3fe1eab
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeScreenOffPreferenceControllerTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeScreenOffPreferenceControllerTest {
+    private ZenModeScreenOffPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+
+    private Context mContext;
+    private final boolean MOCK_PRIORITY_SCREEN_OFF_SETTING = false;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeScreenOffPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+    }
+
+    @Test
+    public void updateState() {
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        when(mBackend.isEffectAllowed(NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF))
+                .thenReturn(MOCK_PRIORITY_SCREEN_OFF_SETTING);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setChecked(MOCK_PRIORITY_SCREEN_OFF_SETTING);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableScreenOff() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveVisualEffectsPolicy(
+                NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF, allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableScreenOff() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveVisualEffectsPolicy(
+                NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF, allow);
+    }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeScreenOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeScreenOnPreferenceControllerTest.java
new file mode 100644
index 0000000..24e3ce3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenModeScreenOnPreferenceControllerTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O)
+public class ZenModeScreenOnPreferenceControllerTest {
+    private ZenModeScreenOnPreferenceController mController;
+
+    @Mock
+    private ZenModeBackend mBackend;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private SwitchPreference mockPref;
+    @Mock
+    private NotificationManager.Policy mPolicy;
+
+    private Context mContext;
+    private final boolean MOCK_PRIORITY_SCREEN_ON_SETTING = false;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowApplication = ShadowApplication.getInstance();
+        shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager);
+
+        mContext = shadowApplication.getApplicationContext();
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
+
+        mController = new ZenModeScreenOnPreferenceController(mContext, mock(Lifecycle.class));
+        ReflectionHelpers.setField(mController, "mBackend", mBackend);
+    }
+
+    @Test
+    public void updateState() {
+        final SwitchPreference mockPref = mock(SwitchPreference.class);
+        when(mBackend.isEffectAllowed(NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON))
+                .thenReturn(MOCK_PRIORITY_SCREEN_ON_SETTING);
+        mController.updateState(mockPref);
+
+        verify(mockPref).setChecked(MOCK_PRIORITY_SCREEN_ON_SETTING);
+    }
+
+    @Test
+    public void onPreferenceChanged_EnableScreenOn() {
+        boolean allow = true;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveVisualEffectsPolicy(
+                NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON, allow);
+    }
+
+    @Test
+    public void onPreferenceChanged_DisableScreenOn() {
+        boolean allow = false;
+        mController.onPreferenceChange(mockPref, allow);
+
+        verify(mBackend).saveVisualEffectsPolicy(
+                NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON, allow);
+    }
+}
\ No newline at end of file