Settings: Do not disturb automatic rule updates.

 - Remove FAB for adding rules, move to last preference.
 - Add first-class event condition type and config sub-page.
 - Always show radio buttons when adding rules.
 - Add new data model for event rules.
 - Add stub condition provider for event rules (always false for now).
 - Add rule-type icons to rule preference rows.

Bug: 20064962
Change-Id: Id5acde371eb2e7d22b1f195459897614db5ba80a
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0446178..5ab1422 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -759,6 +759,26 @@
                 android:value="true" />
         </activity>
 
+        <activity android:name="Settings$ZenModeEventRuleSettingsActivity"
+                android:exported="true"
+                android:taskAffinity="">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.ZEN_MODE_EVENT_RULE_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="com.android.settings.SHORTCUT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.notification.ZenModeEventRuleSettings" />
+            <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+                android:resource="@id/notification_settings" />
+            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+                android:value="true" />
+        </activity>
+
         <activity android:name="Settings$ZenModeExternalRuleSettingsActivity"
                 android:exported="true"
                 android:taskAffinity="">
diff --git a/res/drawable/ic_add.xml b/res/drawable/ic_add.xml
new file mode 100644
index 0000000..5939c97
--- /dev/null
+++ b/res/drawable/ic_add.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="?android:attr/colorControlNormal"
+        android:pathData="M38.0,26.0L26.0,26.0l0.0,12.0l-4.0,0.0L22.0,26.0L10.0,26.0l0.0,-4.0l12.0,0.0L22.0,10.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z"/>
+</vector>
diff --git a/res/drawable/ic_event.xml b/res/drawable/ic_event.xml
new file mode 100644
index 0000000..2ca958f
--- /dev/null
+++ b/res/drawable/ic_event.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="?android:attr/colorControlNormal"
+        android:pathData="M34.0,24.0L24.0,24.0l0.0,10.0l10.0,0.0L34.0,24.0zM32.0,2.0l0.0,4.0L16.0,6.0L16.0,2.0l-4.0,0.0l0.0,4.0l-2.0,0.0c-2.21,0.0 -3.98,1.79 -3.98,4.0L6.0,38.0c0.0,2.21 1.79,4.0 4.0,4.0l28.0,0.0c2.21,0.0 4.0,-1.79 4.0,-4.0L42.0,10.0c0.0,-2.21 -1.79,-4.0 -4.0,-4.0l-2.0,0.0L36.0,2.0l-4.0,0.0zm6.0,36.0L10.0,38.0L10.0,16.0l28.0,0.0l0.0,22.0z"/>
+</vector>
diff --git a/res/drawable/ic_label.xml b/res/drawable/ic_label.xml
new file mode 100644
index 0000000..b1ed068
--- /dev/null
+++ b/res/drawable/ic_label.xml
@@ -0,0 +1,24 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="?android:attr/colorControlNormal"
+        android:pathData="M35.27,11.69C34.54,10.67 33.35,10.0 32.0,10.0l-22.0,0.02c-2.21,0.0 -4.0,1.77 -4.0,3.98l0.0,20.0c0.0,2.21 1.79,3.98 4.0,3.98L32.0,38.0c1.35,0.0 2.54,-0.67 3.27,-1.69L44.0,24.0l-8.73,-12.31z"/>
+</vector>
diff --git a/res/drawable/ic_schedule.xml b/res/drawable/ic_schedule.xml
new file mode 100644
index 0000000..c77c50e
--- /dev/null
+++ b/res/drawable/ic_schedule.xml
@@ -0,0 +1,29 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="?android:attr/colorControlNormal"
+        android:pathData="M23.99,4.0C12.94,4.0 4.0,12.95 4.0,24.0s8.94,20.0 19.99,20.0C35.04,44.0 44.0,35.05 44.0,24.0S35.04,4.0 23.99,4.0zM24.0,40.0c-8.84,0.0 -16.0,-7.16 -16.0,-16.0S15.16,8.0 24.0,8.0s16.0,7.16 16.0,16.0 -7.16,16.0 -16.0,16.0z"
+        android:fillAlpha=".9"/>
+    <path
+        android:fillColor="?android:attr/colorControlNormal"
+        android:pathData="M25.0,14.0l-3.0,0.0l0.0,12.0l10.49,6.3L34.0,29.84l-9.0,-5.34z"
+        android:fillAlpha=".9"/>
+</vector>
diff --git a/res/layout/zen_rule_name.xml b/res/layout/zen_rule_name.xml
index a192c83..b7b0415 100755
--- a/res/layout/zen_rule_name.xml
+++ b/res/layout/zen_rule_name.xml
@@ -23,6 +23,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:singleLine="true"
+        android:hint="@string/zen_mode_rule_name_hint"
         android:layout_marginLeft="22dp"
         android:layout_marginRight="22dp" >
 
@@ -32,7 +33,6 @@
 
     <RadioGroup
         android:id="@+id/rule_types"
-        android:visibility="gone"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginLeft="22dp"
@@ -46,9 +46,10 @@
             android:layout_height="wrap_content"
             android:text="@string/zen_schedule_rule_type_name" />
 
-        <RadioButton android:id="@+id/rule_type_2"
+        <RadioButton android:id="@+id/rule_type_event"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
+            android:layout_height="wrap_content"
+            android:text="@string/zen_event_rule_type_name" />
 
         <RadioButton android:id="@+id/rule_type_3"
             android:layout_width="match_parent"
@@ -58,6 +59,10 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
 
+        <RadioButton android:id="@+id/rule_type_5"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
     </RadioGroup>
 
 </LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f7d5900..406fc7d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6059,8 +6059,11 @@
     <!-- [CHAR LIMIT=40] Zen mode settings: Rule name option and edit dialog title -->
     <string name="zen_mode_rule_name">Rule name</string>
 
+    <!-- [CHAR LIMIT=40] Zen mode settings: Rule name hint text -->
+    <string name="zen_mode_rule_name_hint">Enter rule name</string>
+
     <!-- [CHAR LIMIT=40] Zen mode settings: Add rule menu option name -->
-    <string name="zen_mode_time_add_rule">Add rule</string>
+    <string name="zen_mode_add_rule">Add rule</string>
 
     <!-- [CHAR LIMIT=40] Zen mode settings: Delete rule menu option name -->
     <string name="zen_mode_delete_rule">Delete rule</string>
@@ -6081,7 +6084,58 @@
     <string name="zen_mode_configure_rule">Configure rule</string>
 
     <!-- [CHAR LIMIT=40] Zen mode settings: Schedule rule type name -->
-    <string name="zen_schedule_rule_type_name">Schedule rule</string>
+    <string name="zen_schedule_rule_type_name">Time rule</string>
+
+    <!-- [CHAR LIMIT=NONE] Zen mode settings: Schedule rule toast hint when enabled -->
+    <string name="zen_schedule_rule_enabled_toast">Automatic rule set to turn on Do Not Disturb during specified times</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event rule type name -->
+    <string name="zen_event_rule_type_name">Event rule</string>
+
+    <!-- [CHAR LIMIT=NONE] Zen mode settings: Event rule toast hint when enabled -->
+    <string name="zen_event_rule_enabled_toast">Automatic rule set to turn on Do Not Disturb during specified events</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule calendar option title -->
+    <string name="zen_mode_event_rule_calendar">Calendar</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule summary fragment: any calendar -->
+    <string name="zen_mode_event_rule_summary_any_calendar">Any calendar</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule summary fragment: any reply -->
+    <string name="zen_mode_event_rule_summary_any_reply">Any reply</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule summary fragment: any reply except no -->
+    <string name="zen_mode_event_rule_summary_any_reply_except_no">Any reply except no</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule summary fragment: replied yes -->
+    <string name="zen_mode_event_rule_summary_replied_yes">Replied yes</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule calendar option value for any calendar-->
+    <string name="zen_mode_event_rule_calendar_any">Any</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule attendance option title -->
+    <string name="zen_mode_event_rule_attendance">Attendance</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule attendance option value: required or optional -->
+    <string name="zen_mode_event_rule_attendance_required_optional">Required or optional</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule attendance option value: required -->
+    <string name="zen_mode_event_rule_attendance_required">Required</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule attendance option value: optional -->
+    <string name="zen_mode_event_rule_attendance_optional">Optional</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule reply option title -->
+    <string name="zen_mode_event_rule_reply">Reply</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule reply option value: Any -->
+    <string name="zen_mode_event_rule_reply_any">Any</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule reply option value: Any except no-->
+    <string name="zen_mode_event_rule_reply_any_except_no">Any except no</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Event-based rule reply option value: Yes -->
+    <string name="zen_mode_event_rule_reply_yes">Yes</string>
 
     <!-- [CHAR LIMIT=40] Zen mode settings: Text to display if rule isn't found  -->
     <string name="zen_mode_rule_not_found_text">Rule not found.</string>
@@ -6089,7 +6143,7 @@
     <!-- [CHAR LIMIT=40] Zen mode settings: Rule summary template (when enabled)  -->
     <string name="zen_mode_rule_summary_combination"><xliff:g id="description" example="Sun - Thu">%1$s</xliff:g> / <xliff:g id="mode" example="Alarms only">%2$s</xliff:g></string>
 
-    <!-- [CHAR LIMIT=40] Zen mode settings: Timebased rule days option title -->
+    <!-- [CHAR LIMIT=40] Zen mode settings: Time-based rule days option title -->
     <string name="zen_mode_schedule_rule_days">Days</string>
 
     <!-- [CHAR LIMIT=40] Zen mode settings: Downtime days option value, no days set -->
diff --git a/res/xml/zen_mode_event_rule_settings.xml b/res/xml/zen_mode_event_rule_settings.xml
new file mode 100644
index 0000000..acad96c
--- /dev/null
+++ b/res/xml/zen_mode_event_rule_settings.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 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_event_rule_settings" >
+
+    <!-- Rule name -->
+    <Preference
+        android:key="rule_name"
+        android:title="@string/zen_mode_rule_name"
+        android:persistent="false" />
+
+    <!-- Calendar -->
+    <com.android.settings.DropDownPreference
+        android:key="calendar"
+        android:title="@string/zen_mode_event_rule_calendar"
+        android:persistent="false" />
+
+    <!-- Attendance -->
+    <com.android.settings.DropDownPreference
+        android:key="attendance"
+        android:title="@string/zen_mode_event_rule_attendance"
+        android:persistent="false" />
+
+    <!-- Reply -->
+    <com.android.settings.DropDownPreference
+        android:key="reply"
+        android:title="@string/zen_mode_event_rule_reply"
+        android:persistent="false" />
+
+    <!-- Zen mode -->
+    <com.android.settings.DropDownPreference
+        android:key="zen_mode"
+        android:title="@string/zen_mode_settings_title"
+        android:persistent="false" />
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 88c8316..8a85015 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -99,6 +99,7 @@
     public static class ZenModePrioritySettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeAutomationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeScheduleRuleSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class ZenModeEventRuleSettingsActivity extends SettingsActivity { /* empty */ }
     public static class ZenModeExternalRuleSettingsActivity extends SettingsActivity { /* empty */ }
     public static class NotificationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class NotificationAppListActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 5e1acb1..271261b 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -101,6 +101,7 @@
 import com.android.settings.notification.NotificationSettings;
 import com.android.settings.notification.NotificationStation;
 import com.android.settings.notification.OtherSoundSettings;
+import com.android.settings.notification.ZenModeEventRuleSettings;
 import com.android.settings.notification.ZenModeExternalRuleSettings;
 import com.android.settings.notification.ZenModePrioritySettings;
 import com.android.settings.notification.ZenModeSettings;
@@ -341,6 +342,7 @@
             WifiCallingSettings.class.getName(),
             ZenModePrioritySettings.class.getName(),
             ZenModeScheduleRuleSettings.class.getName(),
+            ZenModeEventRuleSettings.class.getName(),
             ZenModeExternalRuleSettings.class.getName(),
             ProcessStatsUi.class.getName(),
     };
diff --git a/src/com/android/settings/notification/ZenModeAutomationSettings.java b/src/com/android/settings/notification/ZenModeAutomationSettings.java
index dab0a0d..e33e32f 100644
--- a/src/com/android/settings/notification/ZenModeAutomationSettings.java
+++ b/src/com/android/settings/notification/ZenModeAutomationSettings.java
@@ -29,21 +29,22 @@
 import android.provider.Settings.Global;
 import android.service.notification.ConditionProviderService;
 import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.EventInfo;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.service.notification.ZenModeConfig.ZenRule;
 import android.text.format.DateFormat;
 import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.R;
 import com.android.settings.notification.ManagedServiceSettings.Config;
+import com.android.settings.notification.ZenModeEventRuleSettings.CalendarInfo;
 import com.android.settings.notification.ZenRuleNameDialog.RuleInfo;
-import com.android.settings.widget.FloatingActionButton;
 
 import java.text.SimpleDateFormat;
+import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Comparator;
 import java.util.List;
 import java.util.TreeSet;
 
@@ -68,21 +69,6 @@
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        final FloatingActionButton fab = getFloatingActionButton();
-        fab.setVisibility(View.VISIBLE);
-        fab.setImageResource(R.drawable.ic_menu_add_white);
-        fab.setContentDescription(getString(R.string.zen_mode_time_add_rule));
-        fab.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                showAddRuleDialog();
-            }
-        });
-    }
-
-    @Override
     public void onDestroy() {
         super.onDestroy();
         mServiceListing.setListening(false);
@@ -133,15 +119,32 @@
                 .putExtra(ZenModeRuleSettingsBase.EXTRA_RULE_ID, ruleId));
     }
 
+    private ZenRuleInfo[] sortedRules() {
+        final ZenRuleInfo[] rt = new ZenRuleInfo[mConfig.automaticRules.size()];
+        for (int i = 0; i < rt.length; i++) {
+            final ZenRuleInfo zri = new ZenRuleInfo();
+            zri.id = mConfig.automaticRules.keyAt(i);
+            zri.rule = mConfig.automaticRules.valueAt(i);
+            rt[i] = zri;
+        }
+        Arrays.sort(rt, RULE_COMPARATOR);
+        return rt;
+    }
+
     private void updateControls() {
         final PreferenceScreen root = getPreferenceScreen();
         root.removeAll();
         if (mConfig == null) return;
-        for (int i = 0; i < mConfig.automaticRules.size(); i++) {
-            final String id = mConfig.automaticRules.keyAt(i);
-            final ZenRule rule = mConfig.automaticRules.valueAt(i);
+        final ZenRuleInfo[] sortedRules = sortedRules();
+        for (int i = 0; i < sortedRules.length; i++) {
+            final String id = sortedRules[i].id;
+            final ZenRule rule = sortedRules[i].rule;
             final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(rule.conditionId);
+            final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.conditionId);
             final Preference p = new Preference(mContext);
+            p.setIcon(isSchedule ? R.drawable.ic_schedule
+                    : isEvent ? R.drawable.ic_event
+                    : R.drawable.ic_label);
             p.setTitle(rule.name);
             p.setSummary(computeRuleSummary(rule));
             p.setPersistent(false);
@@ -149,6 +152,7 @@
                 @Override
                 public boolean onPreferenceClick(Preference preference) {
                     final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION
+                            : isEvent ? ZenModeEventRuleSettings.ACTION
                             : ZenModeExternalRuleSettings.ACTION;
                     showRule(action, null, id, rule.name);
                     return true;
@@ -156,6 +160,18 @@
             });
             root.addPreference(p);
         }
+        final Preference p = new Preference(mContext);
+        p.setIcon(R.drawable.ic_add);
+        p.setTitle(R.string.zen_mode_add_rule);
+        p.setPersistent(false);
+        p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+            @Override
+            public boolean onPreferenceClick(Preference preference) {
+                showAddRuleDialog();
+                return true;
+            }
+        });
+        root.addPreference(p);
     }
 
     @Override
@@ -165,19 +181,69 @@
 
     private String computeRuleSummary(ZenRule rule) {
         if (rule == null || !rule.enabled) return getString(R.string.switch_off_text);
-        final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(rule.conditionId);
         final String mode = ZenModeSettings.computeZenModeCaption(getResources(), rule.zenMode);
         String summary = getString(R.string.switch_on_text);
+        final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(rule.conditionId);
+        final EventInfo event = ZenModeConfig.tryParseEventConditionId(rule.conditionId);
         if (schedule != null) {
-            final String days = computeContiguousDayRanges(schedule.days);
-            final String start = getTime(schedule.startHour, schedule.startMinute);
-            final String end = getTime(schedule.endHour, schedule.endMinute);
-            final String time = getString(R.string.summary_range_verbal_combination, start, end);
-            summary = getString(R.string.zen_mode_rule_summary_combination, days, time);
+            summary = computeScheduleRuleSummary(schedule);
+        } else if (event != null) {
+            summary = computeEventRuleSummary(event);
         }
         return getString(R.string.zen_mode_rule_summary_combination, summary, mode);
     }
 
+    private String computeScheduleRuleSummary(ScheduleInfo schedule) {
+        final String days = computeContiguousDayRanges(schedule.days);
+        final String start = getTime(schedule.startHour, schedule.startMinute);
+        final String end = getTime(schedule.endHour, schedule.endMinute);
+        final String time = getString(R.string.summary_range_verbal_combination, start, end);
+        return getString(R.string.zen_mode_rule_summary_combination, days, time);
+    }
+
+    private String computeEventRuleSummary(EventInfo event) {
+        final String calendar = computeCalendarName(event);
+        final String attendance = getString(computeAttendance(event));
+        final String reply = getString(computeReply(event));
+        return getString(R.string.zen_mode_rule_summary_combination,
+                getString(R.string.zen_mode_rule_summary_combination, calendar, attendance), reply);
+    }
+
+    private String computeCalendarName(EventInfo event) {
+        if (event.calendar != 0) {
+            final CalendarInfo[] calendars = ZenModeEventRuleSettings.getCalendars(mContext);
+            for (int i = 0; i < calendars.length; i++) {
+                final CalendarInfo calendar = calendars[i];
+                if (calendar.id == event.calendar) {
+                    return calendar.name;
+                }
+            }
+        }
+        return getString(R.string.zen_mode_event_rule_summary_any_calendar);
+    }
+
+    private int computeAttendance(EventInfo event) {
+        switch (event.attendance) {
+            case EventInfo.ATTENDANCE_REQUIRED:
+                return R.string.zen_mode_event_rule_attendance_required;
+            case EventInfo.ATTENDANCE_OPTIONAL:
+                return R.string.zen_mode_event_rule_attendance_optional;
+            default:
+                return R.string.zen_mode_event_rule_attendance_required_optional;
+        }
+    }
+
+    private int computeReply(EventInfo event) {
+        switch (event.reply) {
+            case EventInfo.REPLY_ANY_EXCEPT_NO:
+                return R.string.zen_mode_event_rule_summary_any_reply_except_no;
+            case EventInfo.REPLY_YES:
+                return R.string.zen_mode_event_rule_summary_replied_yes;
+            default:
+                return R.string.zen_mode_event_rule_summary_any_reply;
+        }
+    }
+
     private String getTime(int hour, int minute) {
         mCalendar.set(Calendar.HOUR_OF_DAY, hour);
         mCalendar.set(Calendar.MINUTE, minute);
@@ -248,4 +314,24 @@
         }
     };
 
+    private static final Comparator<ZenRuleInfo> RULE_COMPARATOR = new Comparator<ZenRuleInfo>() {
+        @Override
+        public int compare(ZenRuleInfo lhs, ZenRuleInfo rhs) {
+            return key(lhs).compareTo(key(rhs));
+        }
+
+        private String key(ZenRuleInfo zri) {
+            final ZenRule rule = zri.rule;
+            final int type = ZenModeConfig.isValidScheduleConditionId(rule.conditionId) ? 1
+                    : ZenModeConfig.isValidEventConditionId(rule.conditionId) ? 2
+                    : 3;
+            return type + rule.name;
+        }
+    };
+
+    private static class ZenRuleInfo {
+        String id;
+        ZenRule rule;
+    }
+
 }
diff --git a/src/com/android/settings/notification/ZenModeEventRuleSettings.java b/src/com/android/settings/notification/ZenModeEventRuleSettings.java
new file mode 100644
index 0000000..38581b6
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeEventRuleSettings.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2015 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.database.Cursor;
+import android.preference.PreferenceScreen;
+import android.provider.CalendarContract.Calendars;
+import android.provider.Settings;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.EventInfo;
+import android.service.notification.ZenModeConfig.ZenRule;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.DropDownPreference;
+import com.android.settings.R;
+
+public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase {
+    private static final String KEY_CALENDAR = "calendar";
+    private static final String KEY_ATTENDANCE = "attendance";
+    private static final String KEY_REPLY = "reply";
+
+    public static final String ACTION = Settings.ACTION_ZEN_MODE_EVENT_RULE_SETTINGS;
+
+    private DropDownPreference mCalendar;
+    private DropDownPreference mAttendance;
+    private DropDownPreference mReply;
+
+    private EventInfo mEvent;
+    private CalendarInfo[] mCalendars;
+
+    @Override
+    protected boolean setRule(ZenRule rule) {
+        mEvent = rule != null ? ZenModeConfig.tryParseEventConditionId(rule.conditionId)
+                : null;
+        return mEvent != null;
+    }
+
+    @Override
+    protected String getZenModeDependency() {
+        return null;
+    }
+
+    @Override
+    protected int getEnabledToastText() {
+        return R.string.zen_event_rule_enabled_toast;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        reloadCalendar();
+    }
+
+    private void reloadCalendar() {
+        mCalendars = getCalendars(mContext);
+        mCalendar.clearItems();
+        mCalendar.addItem(R.string.zen_mode_event_rule_calendar_any, 0L);
+        for (int i = 0; i < mCalendars.length; i++) {
+            mCalendar.addItem(mCalendars[i].name, mCalendars[i].id);
+        }
+    }
+
+    @Override
+    protected void onCreateInternal() {
+        addPreferencesFromResource(R.xml.zen_mode_event_rule_settings);
+        final PreferenceScreen root = getPreferenceScreen();
+
+        mCalendar = (DropDownPreference) root.findPreference(KEY_CALENDAR);
+        mCalendar.setCallback(new DropDownPreference.Callback() {
+            @Override
+            public boolean onItemSelected(int pos, Object value) {
+                final long calendar = (Long) value;
+                if (calendar == mEvent.calendar) return true;
+                mEvent.calendar = calendar;
+                updateRule(ZenModeConfig.toEventConditionId(mEvent));
+                return true;
+            }
+        });
+
+        mAttendance = (DropDownPreference) root.findPreference(KEY_ATTENDANCE);
+        mAttendance.addItem(R.string.zen_mode_event_rule_attendance_required_optional,
+                EventInfo.ATTENDANCE_REQUIRED_OR_OPTIONAL);
+        mAttendance.addItem(R.string.zen_mode_event_rule_attendance_required,
+                EventInfo.ATTENDANCE_REQUIRED);
+        mAttendance.addItem(R.string.zen_mode_event_rule_attendance_optional,
+                EventInfo.ATTENDANCE_OPTIONAL);
+        mAttendance.setCallback(new DropDownPreference.Callback() {
+            @Override
+            public boolean onItemSelected(int pos, Object value) {
+                final int attendance = (Integer) value;
+                if (attendance == mEvent.attendance) return true;
+                mEvent.attendance = attendance;
+                updateRule(ZenModeConfig.toEventConditionId(mEvent));
+                return true;
+            }
+        });
+
+        mReply = (DropDownPreference) root.findPreference(KEY_REPLY);
+        mReply.addItem(R.string.zen_mode_event_rule_reply_any,
+                EventInfo.REPLY_ANY);
+        mReply.addItem(R.string.zen_mode_event_rule_reply_any_except_no,
+                EventInfo.REPLY_ANY_EXCEPT_NO);
+        mReply.addItem(R.string.zen_mode_event_rule_reply_yes,
+                EventInfo.REPLY_YES);
+        mReply.setCallback(new DropDownPreference.Callback() {
+            @Override
+            public boolean onItemSelected(int pos, Object value) {
+                final int reply = (Integer) value;
+                if (reply == mEvent.reply) return true;
+                mEvent.reply = reply;
+                updateRule(ZenModeConfig.toEventConditionId(mEvent));
+                return true;
+            }
+        });
+
+        reloadCalendar();
+        updateControlsInternal();
+    }
+
+    @Override
+    protected void updateControlsInternal() {
+        mCalendar.setSelectedValue(mEvent.calendar);
+        mAttendance.setSelectedValue(mEvent.attendance);
+        mReply.setSelectedValue(mEvent.reply);
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.NOTIFICATION_ZEN_MODE_EVENT_RULE;
+    }
+
+    public static CalendarInfo[] getCalendars(Context context) {
+        final String primary = "\"primary\"";
+        final String[] projection = { Calendars._ID, Calendars.CALENDAR_DISPLAY_NAME,
+                "(" + Calendars.ACCOUNT_NAME + "=" + Calendars.OWNER_ACCOUNT + ") AS " + primary };
+        final String selection = primary + " = 1";
+        Cursor cursor = null;
+        try {
+            cursor = context.getContentResolver().query(Calendars.CONTENT_URI, projection,
+                    selection, null, null);
+            if (cursor == null) {
+                return new CalendarInfo[0];
+            }
+            final CalendarInfo[] rt = new CalendarInfo[cursor.getCount()];
+            int i = 0;
+            while (cursor.moveToNext()) {
+                final CalendarInfo ci = new CalendarInfo();
+                ci.id = cursor.getLong(0);
+                ci.name = cursor.getString(1);
+                rt[i++] = ci;
+            }
+            return rt;
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    public static class CalendarInfo {
+        public long id;
+        public String name;
+    }
+
+}
diff --git a/src/com/android/settings/notification/ZenModeExternalRuleSettings.java b/src/com/android/settings/notification/ZenModeExternalRuleSettings.java
index 9f9dc8a..8a24e02 100644
--- a/src/com/android/settings/notification/ZenModeExternalRuleSettings.java
+++ b/src/com/android/settings/notification/ZenModeExternalRuleSettings.java
@@ -58,6 +58,11 @@
     }
 
     @Override
+    protected int getEnabledToastText() {
+        return 0;
+    }
+
+    @Override
     protected void onCreateInternal() {
         addPreferencesFromResource(R.xml.zen_mode_external_rule_settings);
         final PreferenceScreen root = getPreferenceScreen();
diff --git a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
index f6bc75f..cf66da8 100644
--- a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
+++ b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
@@ -59,11 +59,13 @@
     private Preference mRuleName;
     private SwitchBar mSwitchBar;
     private DropDownPreference mZenMode;
+    private Toast mEnabledToast;
 
     abstract protected void onCreateInternal();
     abstract protected boolean setRule(ZenRule rule);
     abstract protected String getZenModeDependency();
     abstract protected void updateControlsInternal();
+    abstract protected int getEnabledToastText();
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -154,6 +156,17 @@
         mRule.enabled = enabled;
         mRule.snoozing = false;
         setZenModeConfig(mConfig);
+        if (enabled) {
+            final int toastText = getEnabledToastText();
+            if (toastText != 0) {
+                mEnabledToast = Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT);
+                mEnabledToast.show();
+            }
+        } else {
+            if (mEnabledToast != null) {
+                mEnabledToast.cancel();
+            }
+        }
     }
 
     protected void updateRule(Uri newConditionId) {
@@ -247,10 +260,10 @@
         updateRuleName();
         updateControlsInternal();
         mZenMode.setSelectedValue(mRule.zenMode);
-        mDisableListeners = false;
         if (mSwitchBar != null) {
             mSwitchBar.setChecked(mRule.enabled);
         }
+        mDisableListeners = false;
     }
 
 }
diff --git a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
index fef3175..b6b87b5 100644
--- a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
+++ b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
@@ -74,6 +74,11 @@
     }
 
     @Override
+    protected int getEnabledToastText() {
+        return R.string.zen_schedule_rule_enabled_toast;
+    }
+
+    @Override
     protected void onCreateInternal() {
         addPreferencesFromResource(R.xml.zen_mode_schedule_rule_settings);
         final PreferenceScreen root = getPreferenceScreen();
diff --git a/src/com/android/settings/notification/ZenRuleNameDialog.java b/src/com/android/settings/notification/ZenRuleNameDialog.java
index 8b44e46..cd5c766 100644
--- a/src/com/android/settings/notification/ZenRuleNameDialog.java
+++ b/src/com/android/settings/notification/ZenRuleNameDialog.java
@@ -24,6 +24,7 @@
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.EventInfo;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.text.Editable;
 import android.text.TextUtils;
@@ -50,25 +51,30 @@
     private final ArraySet<String> mExistingNames;
     private final ServiceListing mServiceListing;
     private final RuleInfo[] mExternalRules = new RuleInfo[3];
+    private final boolean mIsNew;
 
     public ZenRuleNameDialog(Context context, ServiceListing serviceListing, String ruleName,
             ArraySet<String> existingNames) {
         mServiceListing = serviceListing;
+        mIsNew = ruleName == null;
         final View v = LayoutInflater.from(context).inflate(R.layout.zen_rule_name, null, false);
         mEditText = (EditText) v.findViewById(R.id.rule_name);
-        if (ruleName != null) {
+        if (!mIsNew) {
             mEditText.setText(ruleName);
         }
         mEditText.setSelectAllOnFocus(true);
         mTypes = (RadioGroup) v.findViewById(R.id.rule_types);
         if (mServiceListing != null) {
             bindType(R.id.rule_type_schedule, defaultNewSchedule());
+            bindType(R.id.rule_type_event, defaultNewEvent());
             bindExternalRules();
             mServiceListing.addCallback(mServiceListingCallback);
             mServiceListing.reload();
+        } else {
+            mTypes.setVisibility(View.GONE);
         }
         mDialog = new AlertDialog.Builder(context)
-                .setTitle(R.string.zen_mode_rule_name)
+                .setTitle(mIsNew ? R.string.zen_mode_add_rule : R.string.zen_mode_rule_name)
                 .setView(v)
                 .setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
                     @Override
@@ -157,12 +163,21 @@
         return rt;
     }
 
+    private static RuleInfo defaultNewEvent() {
+        final EventInfo event = new EventInfo();
+        event.calendar = 0; // any
+        event.attendance = EventInfo.ATTENDANCE_REQUIRED_OR_OPTIONAL;
+        event.reply = EventInfo.REPLY_ANY_EXCEPT_NO;
+        final RuleInfo rt = new RuleInfo();
+        rt.settingsAction = ZenModeEventRuleSettings.ACTION;
+        rt.defaultConditionId = ZenModeConfig.toEventConditionId(event);
+        return rt;
+    }
+
     private void bindExternalRules() {
-        bindType(R.id.rule_type_2, mExternalRules[0]);
-        bindType(R.id.rule_type_3, mExternalRules[1]);
-        bindType(R.id.rule_type_4, mExternalRules[2]);
-        // show radio group if we have at least one external rule type
-        mTypes.setVisibility(mExternalRules[0] != null ? View.VISIBLE : View.GONE);
+        bindType(R.id.rule_type_3, mExternalRules[0]);
+        bindType(R.id.rule_type_4, mExternalRules[1]);
+        bindType(R.id.rule_type_5, mExternalRules[2]);
     }
 
     private final ServiceListing.Callback mServiceListingCallback = new ServiceListing.Callback() {