Merge "Add activities to support voice settings."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7112f13..4269f5a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -707,6 +707,10 @@
                 <action android:name="android.settings.ZEN_MODE_AUTOMATION_SETTINGS" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.ACTION_CONDITION_PROVIDER_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" />
@@ -740,6 +744,26 @@
                 android:value="true" />
         </activity>
 
+        <activity android:name="Settings$ZenModeExternalRuleSettingsActivity"
+                android:exported="true"
+                android:taskAffinity="">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.ZEN_MODE_EXTERNAL_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.ZenModeExternalRuleSettings" />
+            <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$HomeSettingsActivity"
                 android:label="@string/home_settings"
                 android:taskAffinity="">
@@ -1320,10 +1344,12 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="ConfirmLockPattern"/>
+        <activity android:name="ConfirmLockPattern"
+            android:theme="@style/Theme.ConfirmDeviceCredentials"/>
 
         <activity android:name="ConfirmLockPassword"
-            android:windowSoftInputMode="stateVisible|adjustResize"/>
+            android:windowSoftInputMode="stateVisible|adjustResize"
+            android:theme="@style/Theme.ConfirmDeviceCredentials"/>
 
         <activity android:name="FingerprintSettings" android:exported="false"/>
         <activity android:name="FingerprintEnroll" android:exported="false"/>
diff --git a/res/drawable/ic_fingerprint.xml b/res/drawable/ic_fingerprint.xml
new file mode 100644
index 0000000..2f27753
--- /dev/null
+++ b/res/drawable/ic_fingerprint.xml
@@ -0,0 +1,36 @@
+<!--
+  ~ 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="32dp"
+        android:height="32dp"
+        android:viewportWidth="32.0"
+        android:viewportHeight="32.0">
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M23.7,5.9c-0.1,0.0 -0.2,0.0 -0.3,-0.1C21.0,4.5 18.6,3.9 16.0,3.9c-2.5,0.0 -4.6,0.6 -6.9,1.9C8.8,6.0 8.3,5.9 8.1,5.5C7.9,5.2 8.0,4.7 8.4,4.5c2.5,-1.4 4.9,-2.1 7.7,-2.1c2.8,0.0 5.4,0.7 8.0,2.1c0.4,0.2 0.5,0.6 0.3,1.0C24.2,5.7 24.0,5.9 23.7,5.9z"/>
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3,-1.9 2.9,-3.4 4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8 -0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0 -12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z"/>
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M13.3,29.6c-0.2,0.0 -0.4,-0.1 -0.5,-0.2c-1.1,-1.2 -1.7,-2.0 -2.6,-3.6c-0.9,-1.7 -1.4,-3.7 -1.4,-5.9c0.0,-4.1 3.3,-7.4 7.4,-7.4c4.1,0.0 7.4,3.3 7.4,7.4c0.0,0.4 -0.3,0.7 -0.7,0.7s-0.7,-0.3 -0.7,-0.7c0.0,-3.3 -2.7,-5.9 -5.9,-5.9c-3.3,0.0 -5.9,2.7 -5.9,5.9c0.0,2.0 0.4,3.8 1.2,5.2c0.8,1.6 1.4,2.2 2.4,3.3c0.3,0.3 0.3,0.8 0.0,1.0C13.7,29.5 13.5,29.6 13.3,29.6z"/>
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M22.6,27.1c-1.6,0.0 -2.9,-0.4 -4.1,-1.2c-1.9,-1.4 -3.1,-3.6 -3.1,-6.0c0.0,-0.4 0.3,-0.7 0.7,-0.7s0.7,0.3 0.7,0.7c0.0,1.9 0.9,3.7 2.5,4.8c0.9,0.6 1.9,1.0 3.2,1.0c0.3,0.0 0.8,0.0 1.3,-0.1c0.4,-0.1 0.8,0.2 0.8,0.6c0.1,0.4 -0.2,0.8 -0.6,0.8C23.4,27.1 22.8,27.1 22.6,27.1z"/>
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M20.0,29.9c-0.1,0.0 -0.1,0.0 -0.2,0.0c-2.1,-0.6 -3.4,-1.4 -4.8,-2.9c-1.8,-1.9 -2.8,-4.4 -2.8,-7.1c0.0,-2.2 1.8,-4.1 4.1,-4.1c2.2,0.0 4.1,1.8 4.1,4.1c0.0,1.4 1.2,2.6 2.6,2.6c1.4,0.0 2.6,-1.2 2.6,-2.6c0.0,-5.1 -4.2,-9.3 -9.3,-9.3c-3.6,0.0 -6.9,2.1 -8.4,5.4C7.3,17.1 7.0,18.4 7.0,19.8c0.0,1.1 0.1,2.7 0.9,4.9c0.1,0.4 -0.1,0.8 -0.4,0.9c-0.4,0.1 -0.8,-0.1 -0.9,-0.4c-0.6,-1.8 -0.9,-3.6 -0.9,-5.4c0.0,-1.6 0.3,-3.1 0.9,-4.4c1.7,-3.8 5.6,-6.3 9.8,-6.3c5.9,0.0 10.7,4.8 10.7,10.7c0.0,2.2 -1.8,4.1 -4.1,4.1s-4.0,-1.8 -4.0,-4.1c0.0,-1.4 -1.2,-2.6 -2.6,-2.6c-1.4,0.0 -2.6,1.2 -2.6,2.6c0.0,2.3 0.9,4.5 2.4,6.1c1.2,1.3 2.4,2.0 4.2,2.5c0.4,0.1 0.6,0.5 0.5,0.9C20.6,29.7 20.3,29.9 20.0,29.9z"/>
+</vector>
diff --git a/res/drawable/ic_fingerprint_error.xml b/res/drawable/ic_fingerprint_error.xml
new file mode 100644
index 0000000..d00280d
--- /dev/null
+++ b/res/drawable/ic_fingerprint_error.xml
@@ -0,0 +1,30 @@
+<!--
+  ~ 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="32dp"
+        android:height="32dp"
+        android:viewportWidth="32.0"
+        android:viewportHeight="32.0">
+    <path
+        android:fillColor="@color/warning"
+        android:pathData="M15.99,2.5C8.53,2.5 2.5,8.54 2.5,16.0s6.03,13.5 13.49,13.5S29.5,23.46 29.5,16.0S23.45,2.5 15.99,2.5zM16.0,26.8c-5.97,0.0 -10.8,-4.83 -10.8,-10.8S10.03,5.2 16.0,5.2S26.8,10.03 26.8,16.0S21.97,26.8 16.0,26.8z"/>
+    <path
+        android:fillColor="@color/warning"
+        android:pathData="M14.65,20.05l2.7,0.0l0.0,2.7l-2.7,0.0z"/>
+    <path
+        android:fillColor="@color/warning"
+        android:pathData="M14.65,9.25l2.7,0.0l0.0,8.1l-2.7,0.0z"/>
+</vector>
diff --git a/res/layout-land/confirm_lock_password.xml b/res/layout-land/confirm_lock_password.xml
index 4158c88..d8da8fe 100644
--- a/res/layout-land/confirm_lock_password.xml
+++ b/res/layout-land/confirm_lock_password.xml
@@ -1,104 +1,92 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2008, 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.
-*/
+<!-- 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.
 -->
-<!-- This is basically the same layout as choose_lock_password. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/topLayout"
     android:orientation="vertical"
-    android:gravity="center_horizontal">
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent">
 
-    <LinearLayout
-        android:layout_width="match_parent"
+    <TextView
+        style="@android:style/TextAppearance.Material.Title"
+        android:id="@+id/headerText"
+        android:layout_marginStart="24dp"
+        android:layout_marginEnd="24dp"
+        android:layout_marginTop="12dp"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:orientation="horizontal">
-        <!-- "Enter PIN(Password) to unlock" -->
-        <TextView android:id="@+id/headerText"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:orientation="horizontal"
-            android:layout_marginEnd="6dip"
-            android:layout_marginStart="6dip"
-            android:layout_marginTop="10dip"
-            android:layout_marginBottom="10dip"
-            android:gravity="start"
-            android:ellipsize="marquee"
-            android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/colorAccent"/>
+
+    <TextView
+        style="@android:style/TextAppearance.Material.Body1"
+        android:id="@+id/detailsText"
+        android:layout_marginStart="24dp"
+        android:layout_marginEnd="24dp"
+        android:layout_marginTop="8dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         />
 
-        <!-- Password entry field -->
-        <EditText android:id="@+id/password_entry"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="5dip"
-            android:layout_marginBottom="5dip"
-            android:layout_gravity="center"
-            android:layout_weight="1"
-            android:inputType="textPassword"
-            android:imeOptions="actionNext"
-            android:gravity="center"
-            android:textSize="24sp"
-            style="@style/TextAppearance.PasswordEntry"
-        />
-    </LinearLayout>
-
-    <!-- Spacer between password entry and keyboard -->
     <View
         android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1" />
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
 
-    <!-- Alphanumeric keyboard -->
-    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="#00000000"
-        android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
-        android:visibility="gone"
-    />
-
-    <RelativeLayout
+    <FrameLayout
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
-        android:background="@android:drawable/bottom_bar"
-        android:visibility="gone">
+        android:orientation="horizontal">
 
-        <Button android:id="@+id/cancel_button"
-            android:layout_width="150dip"
+        <Button
+            style="@android:style/Widget.Material.Button.Borderless"
+            android:id="@+id/cancelButton"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_margin="5dip"
-            android:layout_alignParentStart="true"
-            android:text="@string/lockpassword_cancel_label"
-        />
+            android:layout_gravity="start|bottom"
+            android:text="@string/cancel"
+            android:layout_marginStart="8dp"
+            android:layout_marginEnd="8dp"
+            android:layout_marginBottom="14dp"/>
 
-        <Button android:id="@+id/next_button"
-            android:layout_width="150dip"
+        <EditText android:id="@+id/password_entry"
+            android:layout_width="208dp"
             android:layout_height="wrap_content"
-            android:layout_margin="5dip"
-            android:layout_alignParentEnd="true"
-            android:drawableEnd="@drawable/ic_btn_next"
-            android:drawablePadding="10dip"
-            android:text="@string/lockpassword_continue_label"
-        />
+            android:layout_gravity="center_horizontal|bottom"
+            android:layout_marginBottom="26dp"
+            android:inputType="textPassword"
+            android:imeOptions="actionNext|flagNoFullscreen"
+            android:gravity="center"
+            android:textSize="16sp"
+            style="@style/TextAppearance.PasswordEntry"/>
 
-    </RelativeLayout>
+        <TextView style="@style/TextAppearance.ConfirmDeviceCredentialsErrorText"
+            android:id="@+id/errorText"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal|bottom"
+            android:layout_marginBottom="10dp"/>
 
-</LinearLayout>
+        <ImageView
+            android:id="@+id/fingerprintIcon"
+            android:layout_gravity="end|bottom"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="28dp"
+            android:layout_marginEnd="20dp"
+            android:visibility="gone"/>
+    </FrameLayout>
 
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-land/confirm_lock_pattern.xml b/res/layout-land/confirm_lock_pattern.xml
index b58b46f..0567fad 100644
--- a/res/layout-land/confirm_lock_pattern.xml
+++ b/res/layout-land/confirm_lock_pattern.xml
@@ -13,56 +13,102 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
-        xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/topLayout"
     android:orientation="horizontal"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false">
 
-    <!-- left side: instructions and messages -->
     <LinearLayout
-            android:orientation="vertical"
-            android:layout_width="0dip"
-            android:layout_height="match_parent"
-            android:layout_weight="1.0"
-            >
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_weight="1"
+        android:orientation="vertical">
 
-        <!-- header message -->
-        <TextView android:id="@+id/headerText"
-                  android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:layout_marginTop="16dp"
-                  android:gravity="center"
-                  android:textSize="18sp"/>
+        <TextView
+            style="@android:style/TextAppearance.Material.Headline"
+            android:id="@+id/headerText"
+            android:layout_marginStart="32dp"
+            android:layout_marginEnd="32dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="?android:attr/colorAccent"/>
 
-        <!-- fill space between header and button below -->
-        <View
-            android:layout_weight="1.0"
-            android:layout_width="match_parent"
-            android:layout_height="0dip"
-            />
+        <TextView
+            style="@android:style/TextAppearance.Material.Body1"
+            android:id="@+id/detailsText"
+            android:layout_marginStart="32dp"
+            android:layout_marginEnd="32dp"
+            android:layout_marginTop="12dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
 
-        <!-- footer message -->
-        <TextView android:id="@+id/footerText"
-                  android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:gravity="center"
-                  android:textSize="14sp"/>
+        <Button
+            style="@android:style/Widget.Material.Button.Borderless"
+            android:id="@+id/cancelButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/cancel"
+            android:layout_marginStart="16dp"
+            android:layout_marginEnd="16dp"
+            android:layout_marginTop="16dp"/>
     </LinearLayout>
 
-    <View
-         android:background="@*android:drawable/code_lock_left"
-         android:layout_width="2dip"
-         android:layout_height="match_parent" />
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:clipChildren="false"
+        android:clipToPadding="false">
 
-    <!-- right side: lock pattern -->
-    <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
-         android:layout_width="match_parent"
-         android:layout_height="match_parent"
-         android:background="@color/lock_pattern_background" />
+        <View
+            android:id="@+id/leftSpacer"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="0.4"/>
 
+        <FrameLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:clipChildren="false"
+            android:clipToPadding="false">
+
+            <com.android.internal.widget.LockPatternView
+                android:id="@+id/lockPattern"
+                android:layout_width="288dp"
+                android:layout_height="288dp"
+                android:layout_marginStart="-42dp"
+                android:layout_marginEnd="-42dp"
+                android:layout_gravity="center_vertical"/>
+
+            <TextView
+                style="@style/TextAppearance.ConfirmDeviceCredentialsErrorText"
+                android:id="@+id/errorText"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="bottom|center_horizontal"
+                android:layout_marginBottom="24dp"/>
+        </FrameLayout>
+
+        <View
+            android:id="@+id/rightSpacer"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"/>
+
+        <ImageView
+            android:id="@+id/fingerprintIcon"
+            android:layout_gravity="center_vertical"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:visibility="gone"/>
+    </LinearLayout>
 
 </com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
 
diff --git a/res/layout-sw600dp-land/confirm_lock_password.xml b/res/layout-sw600dp-land/confirm_lock_password.xml
deleted file mode 100644
index cbaad7a..0000000
--- a/res/layout-sw600dp-land/confirm_lock_password.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2008, 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.
-*/
--->
-
-<!-- This is the same layout as choose_lock_password. TODO: find out why merge tag fails -->
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="20dip"
-        android:orientation="horizontal">
-
-        <!-- "Enter PIN(Password) to unlock" -->
-        <TextView android:id="@+id/headerText"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:orientation="horizontal"
-            android:layout_marginEnd="6dip"
-            android:layout_marginStart="6dip"
-            android:layout_marginTop="10dip"
-            android:layout_marginBottom="10dip"
-            android:gravity="start"
-            android:ellipsize="marquee"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-        />
-
-        <!-- Password entry field -->
-        <EditText android:id="@+id/password_entry"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="5dip"
-            android:layout_marginBottom="5dip"
-            android:layout_gravity="center"
-            android:layout_weight="1"
-            android:inputType="textPassword"
-            android:imeOptions="actionNext"
-            android:gravity="center"
-            android:textSize="24sp"
-            style="@style/TextAppearance.PasswordEntry"
-        />
-    </LinearLayout>
-
-    <!-- confirm / restart buttons -->
-    <LinearLayout
-        style="@style/SecurityPreferenceButtonContainer"
-        android:layout_alignParentEnd="true"
-        android:layout_alignParentBottom="true"
-        android:orientation="horizontal">
-
-        <!-- left / top button: skip, or re-try -->
-        <Button android:id="@+id/cancel_button"
-            style="@style/SecurityPreferenceButton"
-            android:text="@string/lockpassword_cancel_label"/>
-
-        <!-- right / bottom button: confirm or ok -->
-        <Button android:id="@+id/next_button"
-            style="@style/SecurityPreferenceButton"
-            android:text="@string/lockpassword_continue_label"/>
-
-    </LinearLayout>
-    <!-- Alphanumeric keyboard -->
-    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="#00000000"
-        android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
-        android:visibility="gone"
-    />
-
-</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout-sw600dp-land/confirm_lock_pattern.xml b/res/layout-sw600dp-land/confirm_lock_pattern.xml
deleted file mode 100644
index a090a6f..0000000
--- a/res/layout-sw600dp-land/confirm_lock_pattern.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/topLayout"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center_horizontal">
-
-    <!-- header message -->
-    <ScrollView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:layout_marginTop="40dip">
-
-        <TextView android:id="@+id/headerText"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="center"
-            android:textAppearance="?android:attr/textAppearanceMedium"/>
-
-    </ScrollView>
-
-    <!-- footer message -->
-    <TextView android:id="@+id/footerText"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="16dip"
-        android:textAppearance="?android:attr/textAppearanceMedium"/>
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="0.6"/>
-
-    <!-- lock pattern widget -->
-    <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
-        android:layout_width="354dip"
-        android:layout_height="354dip"
-        android:background="@color/lock_pattern_background"
-        aspect="square"/>
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1"/>
-
-</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
-
diff --git a/res/layout-sw600dp/confirm_lock_password.xml b/res/layout-sw600dp/confirm_lock_password.xml
index 12b6ab2..1649aaa 100644
--- a/res/layout-sw600dp/confirm_lock_password.xml
+++ b/res/layout-sw600dp/confirm_lock_password.xml
@@ -1,103 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-**
-** Copyright 2008, 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.
-*/
--->
+  ~ 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
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingTop="48dp">
 
-<!-- This is the same layout as choose_lock_password -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:orientation="vertical"
-    android:gravity="center_horizontal">
-
-    <!-- header text ('Enter Pin') -->
-    <TextView android:id="@+id/headerText"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:textAppearance="?android:attr/textAppearanceMedium"/>
-
-    <!-- spacer above text entry field -->
-    <View
-        android:id="@+id/spacerBottom"
-        android:layout_width="fill_parent"
-        android:layout_height="1dip"
-        android:layout_marginTop="6dip"
-        android:background="@android:drawable/divider_horizontal_dark"
-    />
-
-    <!-- Password entry field -->
-    <EditText android:id="@+id/password_entry"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="15dip"
-        android:layout_marginStart="30dip"
-        android:layout_marginEnd="30dip"
-        android:layout_gravity="center"
-        android:inputType="textPassword"
-        android:imeOptions="actionNext"
-        android:gravity="center"
-        android:textSize="32sp"
-        style="@style/TextAppearance.PasswordEntry"
-    />
-
-    <!-- Spacer between password entry and keyboard -->
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1" />
-
-    <!-- Alphanumeric keyboard -->
-    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="#00000000"
-        android:layout_marginBottom="30dip"
-        android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
-        android:visibility="gone"
-    />
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-    />
-
-    <RelativeLayout
-        android:layout_height="wrap_content"
-        android:layout_width="match_parent">
-
-        <LinearLayout
-            style="@style/SecurityPreferenceButtonContainer"
-            android:layout_alignParentEnd="true"
-            android:orientation="horizontal">
-
-            <Button android:id="@+id/cancel_button"
-                style="@style/SecurityPreferenceButton"
-                android:text="@string/lockpassword_cancel_label"/>
-
-            <Button android:id="@+id/next_button"
-                style="@style/SecurityPreferenceButton"
-                android:text="@string/lockpassword_continue_label"/>
-
-        </LinearLayout>
-
-    </RelativeLayout>
-
-</LinearLayout>
-
+    <include layout="@layout/confirm_lock_password_base"
+        android:layout_width="328dp"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_horizontal"/>
+</FrameLayout>
diff --git a/res/layout-sw600dp/confirm_lock_pattern.xml b/res/layout-sw600dp/confirm_lock_pattern.xml
index 12e2b86..5c93ccd 100644
--- a/res/layout-sw600dp/confirm_lock_pattern.xml
+++ b/res/layout-sw600dp/confirm_lock_pattern.xml
@@ -13,48 +13,19 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
-        xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/topLayout"
-    android:orientation="vertical"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="center_horizontal">
+    android:paddingTop="48dp"
+    android:background="#80000000">
 
-    <!-- top: instructions and buttons -->
-    <!-- header message -->
-    <ScrollView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:layout_marginTop="96dip">
+    <View android:layout_width="328dp"
+        android:layout_height="match_parent"
+        android:background="@color/confirm_device_credential_dark_background"
+        android:layout_gravity="center_horizontal"/>
 
-        <TextView android:id="@+id/headerText"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:gravity="center"
-            android:textAppearance="?android:attr/textAppearanceMedium"/>
-
-    </ScrollView>
-
-    <!-- footer message -->
-    <TextView android:id="@+id/footerText"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="16dip"
-        android:textAppearance="?android:attr/textAppearanceMedium"/>
-
-    <!-- bottom: lock pattern -->
-    <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
-         android:layout_width="354dip"
-         android:layout_height="354dip"
-         android:layout_marginTop="80dip"
-         android:background="@color/lock_pattern_background"
-         aspect="square"/>
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1"/>
-
-</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
+    <include layout="@layout/confirm_lock_pattern_base"
+        android:layout_width="328dp"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_horizontal"/>
+</FrameLayout>
diff --git a/res/layout/confirm_lock_password.xml b/res/layout/confirm_lock_password.xml
deleted file mode 100644
index c9a0bbe..0000000
--- a/res/layout/confirm_lock_password.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2008, 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.
-*/
--->
-
-<!-- This is the same layout as choose_lock_password -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="fill_parent"
-    android:layout_height="fill_parent"
-    android:orientation="vertical"
-    android:gravity="center_horizontal">
-
-    <!-- header text ('Enter Pin') -->
-    <TextView android:id="@+id/headerText"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:textAppearance="?android:attr/textAppearanceMedium"/>
-
-    <!-- Password entry field -->
-    <EditText android:id="@+id/password_entry"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="30dip"
-        android:layout_marginEnd="30dip"
-        android:layout_gravity="center"
-        android:inputType="textPassword"
-        android:imeOptions="actionNext"
-        android:gravity="center"
-        android:textSize="24sp"
-        style="@style/TextAppearance.PasswordEntry"
-    />
-
-    <!-- Spacer between password entry and keyboard -->
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1" />
-
-    <!-- Alphanumeric keyboard -->
-    <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="#00000000"
-        android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
-        android:visibility="gone"
-    />
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-    />
-
-    <LinearLayout
-        style="@style/SecurityPreferenceButtonContainer"
-        android:layout_alignParentEnd="true"
-        android:layout_alignParentBottom="true"
-        android:orientation="horizontal">
-
-        <!-- left / top button: skip, or re-try -->
-        <Button android:id="@+id/cancel_button"
-            style="@style/SecurityPreferenceButton"
-            android:text="@string/lockpattern_tutorial_cancel_label"/>
-
-        <!-- right / bottom button: confirm or ok -->
-        <Button android:id="@+id/next_button"
-            style="@style/SecurityPreferenceButton"
-            android:text="@string/lockpattern_tutorial_continue_label"/>
-
-    </LinearLayout>
-
-</LinearLayout>
-
diff --git a/res/layout/confirm_lock_password_base.xml b/res/layout/confirm_lock_password_base.xml
new file mode 100644
index 0000000..af4ffe8
--- /dev/null
+++ b/res/layout/confirm_lock_password_base.xml
@@ -0,0 +1,89 @@
+<?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.
+-->
+<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/topLayout"
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent">
+
+    <TextView
+        style="@android:style/TextAppearance.Material.Headline"
+        android:id="@+id/headerText"
+        android:layout_marginStart="?attr/confirmDeviceCredentialsSideMargin"
+        android:layout_marginEnd="?attr/confirmDeviceCredentialsSideMargin"
+        android:layout_marginTop="?attr/confirmDeviceCredentialsTopMargin"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="?android:attr/colorAccent"/>
+
+    <TextView
+        style="@android:style/TextAppearance.Material.Body1"
+        android:id="@+id/detailsText"
+        android:layout_marginStart="?attr/confirmDeviceCredentialsSideMargin"
+        android:layout_marginEnd="?attr/confirmDeviceCredentialsSideMargin"
+        android:layout_marginTop="12dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+
+    <Button
+        style="@android:style/Widget.Material.Button.Borderless"
+        android:id="@+id/cancelButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/cancel"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
+        android:layout_marginTop="16dp"/>
+
+    <View android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+    <EditText
+        android:id="@+id/password_entry"
+        android:layout_width="208dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="-40dp"
+        android:inputType="textPassword"
+        android:imeOptions="actionNext"
+        android:gravity="center"
+        android:textSize="16sp"
+        style="@style/TextAppearance.PasswordEntry"/>
+
+    <TextView
+        style="@style/TextAppearance.ConfirmDeviceCredentialsErrorText"
+        android:id="@+id/errorText"
+        android:layout_width="wrap_content"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:gravity="center_vertical"/>
+
+    <ImageView
+        android:id="@+id/fingerprintIcon"
+        android:layout_gravity="center_horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_marginBottom="24dp"
+        android:visibility="gone"/>
+
+</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
\ No newline at end of file
diff --git a/res/layout/confirm_lock_pattern.xml b/res/layout/confirm_lock_pattern.xml
deleted file mode 100644
index 4426242..0000000
--- a/res/layout/confirm_lock_pattern.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 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.
--->
-<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
-        xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/topLayout"
-    android:orientation="vertical"
-    android:layout_width="wrap_content"
-    android:layout_height="match_parent">
-
-    <TextView android:id="@+id/headerText"
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1.0"
-        android:gravity="center"
-        android:textSize="18sp"/>
-
-    <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
-         android:layout_width="match_parent"
-         android:layout_height="match_parent"
-         android:background="@color/lock_pattern_background" />
-
-    <TextView android:id="@+id/footerText"
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1.0"
-        android:gravity="center"
-        android:textSize="14sp"/>
-
-</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
diff --git a/res/layout/confirm_lock_pattern_base.xml b/res/layout/confirm_lock_pattern_base.xml
new file mode 100644
index 0000000..029b856
--- /dev/null
+++ b/res/layout/confirm_lock_pattern_base.xml
@@ -0,0 +1,93 @@
+<?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.
+-->
+<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/topLayout"
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent">
+
+    <TextView
+        style="@android:style/TextAppearance.Material.Headline"
+        android:id="@+id/headerText"
+        android:layout_marginStart="?attr/confirmDeviceCredentialsSideMargin"
+        android:layout_marginEnd="?attr/confirmDeviceCredentialsSideMargin"
+        android:layout_marginTop="?attr/confirmDeviceCredentialsTopMargin"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="?android:attr/colorAccent"/>
+
+    <TextView
+        style="@android:style/TextAppearance.Material.Body1"
+        android:id="@+id/detailsText"
+        android:layout_marginStart="?attr/confirmDeviceCredentialsSideMargin"
+        android:layout_marginEnd="?attr/confirmDeviceCredentialsSideMargin"
+        android:layout_marginTop="12dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        />
+
+    <Button
+        style="@android:style/Widget.Material.Button.Borderless"
+        android:id="@+id/cancelButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/cancel"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
+        android:layout_marginTop="16dp"/>
+
+    <View android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="0.5"/>
+
+    <com.android.internal.widget.LockPatternView
+        android:id="@+id/lockPattern"
+        android:layout_width="312dp"
+        android:layout_height="312dp"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="-46dp"
+        android:layout_marginBottom="-46dp"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <TextView
+            style="@style/TextAppearance.ConfirmDeviceCredentialsErrorText"
+            android:id="@+id/errorText"
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="12dp"
+            android:layout_marginStart="12dp"
+            android:layout_marginEnd="12dp"
+            android:gravity="center_vertical"/>
+
+        <ImageView
+            android:id="@+id/fingerprintIcon"
+            android:layout_gravity="center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="24dp"
+            android:visibility="gone"/>
+    </LinearLayout>
+
+</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
\ No newline at end of file
diff --git a/res/layout/zen_rule_name.xml b/res/layout/zen_rule_name.xml
index 62f51ea..a192c83 100755
--- a/res/layout/zen_rule_name.xml
+++ b/res/layout/zen_rule_name.xml
@@ -13,7 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="wrap_content" >
 
@@ -29,4 +30,34 @@
 
     </EditText>
 
-</FrameLayout>
+    <RadioGroup
+        android:id="@+id/rule_types"
+        android:visibility="gone"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="22dp"
+        android:layout_marginRight="22dp"
+        android:layout_marginTop="16dp"
+        android:orientation="vertical"
+        android:checkedButton="@+id/rule_type_schedule" >
+
+        <RadioButton android:id="@+id/rule_type_schedule"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/zen_schedule_rule_type_name" />
+
+        <RadioButton android:id="@+id/rule_type_2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+        <RadioButton android:id="@+id/rule_type_3"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+        <RadioButton android:id="@+id/rule_type_4"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+    </RadioGroup>
+
+</LinearLayout>
diff --git a/res/values-land/themes.xml b/res/values-land/themes.xml
new file mode 100644
index 0000000..8f407c6
--- /dev/null
+++ b/res/values-land/themes.xml
@@ -0,0 +1,22 @@
+<?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
+  -->
+<resources>
+    <style name="Theme.ConfirmDeviceCredentials" parent="Theme.SubSettings">
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index e1bb33e..a26c99b 100755
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -87,4 +87,6 @@
     <dimen name="wifi_assistant_padding_start_end">24dp</dimen>
     <dimen name="wifi_assistant_padding">25dp</dimen>
     <dimen name="wifi_assistant_text_padding">24dp</dimen>
+
+    <dimen name="confirm_credentials_security_method_margin">72dp</dimen>
 </resources>
diff --git a/res/values-sw600dp/styles.xml b/res/values-sw600dp/styles.xml
index d09b23e..8cf0e86 100644
--- a/res/values-sw600dp/styles.xml
+++ b/res/values-sw600dp/styles.xml
@@ -68,5 +68,4 @@
     <style name="KeyguardAppWidgetItem">
         <item name="android:textSize">16sp</item>
     </style>
-
 </resources>
diff --git a/res/values-sw600dp/themes.xml b/res/values-sw600dp/themes.xml
new file mode 100644
index 0000000..833e36e
--- /dev/null
+++ b/res/values-sw600dp/themes.xml
@@ -0,0 +1,21 @@
+<?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
+  -->
+<resources>
+    <style name="Theme.ConfirmDeviceCredentials" parent="Theme.SubSettings">
+    </style>
+</resources>
\ No newline at end of file
diff --git a/res/values/aliases.xml b/res/values/aliases.xml
index 97a50c5..248ac71 100644
--- a/res/values/aliases.xml
+++ b/res/values/aliases.xml
@@ -15,7 +15,8 @@
 -->
 
 <resources>
-    <item name="notification_app_section" type="layout">@*android:layout/preference_category_material
-    </item>
+     <item name="notification_app_section" type="layout">@*android:layout/preference_category_material</item>
+     <item name="confirm_lock_pattern" type="layout">@layout/confirm_lock_pattern_base</item>
+     <item name="confirm_lock_password" type="layout">@layout/confirm_lock_password_base</item>
 </resources>
 
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 1fe36d6..248cf92 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -109,4 +109,8 @@
     <attr name="switchBarMarginEnd" format="dimension" />
 
     <attr name="preferenceBackgroundColor" format="color" />
+
+    <!-- Confirm device credentials screen -->
+    <attr name="confirmDeviceCredentialsSideMargin" format="dimension" />
+    <attr name="confirmDeviceCredentialsTopMargin" format="dimension" />
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 6e97a2a..0d0637c 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -54,7 +54,9 @@
 
     <color name="lock_pattern_background">#00000000</color>
     <color name="lock_pattern_view_regular_color">#ff37474f</color>
-    <color name="lock_pattern_view_error_color">#fff4511e</color>
+    <color name="lock_pattern_view_error_color">@color/warning</color>
+
+    <color name="lock_pattern_view_regular_color_dark">#ffffff</color>
 
     <color name="unlock_pattern_view_regular_color">@android:color/white</color>
     <color name="unlock_pattern_view_error_color">#fff4511e</color>
@@ -77,4 +79,6 @@
     <color name="wifi_divider">#ffe0e0e0</color>
     <color name="sim_noitification">@*android:color/material_deep_teal_500</color>
 
+    <color name="warning">#ff5621</color>
+    <color name="confirm_device_credential_dark_background">#263238</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1fd22e0..07e6957 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -238,4 +238,5 @@
     <dimen name="fingerprint_ring_radius">96dip</dimen>
     <dimen name="fingerprint_ring_thickness">4dip</dimen>
 
+    <dimen name="confirm_credentials_security_method_margin">48dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 133f6f5..92dfd51 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -835,12 +835,6 @@
     <string name="crypt_keeper_dialog_need_password_message">You need to set a lock screen PIN or
     password before you can start encryption.</string>
 
-    <!-- String at bottom of screen when prompting for pattern.
-         Note: This is only for the pattern case. -->
-    <string name="crypt_keeper_confirm_encrypt">
-    You need to draw your unlock pattern to confirm encrypting your device.
-    </string>
-
     <!-- Title of the final confirmation screen before starting device encryption -->
     <string name="crypt_keeper_confirm_title">Encrypt?</string>
     <!-- Warning text in the final confirmation screen before starting device encryption -->
@@ -2430,8 +2424,6 @@
     <string name="reset_network_final_desc">Reset all network settings? You can\'t undo this action!</string>
     <!-- SD card & phone storage settings screen, button on screen after user selects Reset settings button -->
     <string name="reset_network_final_button_text">Reset settings</string>
-    <!-- Explanation of drawing unlock pattern to reset settings -->
-    <string name="reset_network_gesture_explanation">You need to draw your unlock pattern to confirm a network settings reset.</string>
     <!-- Reset settings confirmation screen title [CHAR LIMIT=30] -->
     <string name="reset_network_confirm_title">Reset?</string>
     <!-- Reset settings complete toast text [CHAR LIMIT=75] -->
@@ -2472,8 +2464,6 @@
     <string name="master_clear_final_desc">Erase all your personal information and downloaded apps? You can\'t undo this action!</string>
     <!-- SD card & phone storage settings screen, button on screen after user selects Reset phone button -->
     <string name="master_clear_final_button_text">Erase everything</string>
-    <!-- Explanation of drawing unlock pattern to reset phone -->
-    <string name="master_clear_gesture_explanation">You need to draw your unlock pattern to confirm a factory data reset.</string>
     <!-- Master clear failed message -->
     <string name="master_clear_failed">No reset was performed because the System Clear service isn\'t available.</string>
     <!-- Master clear confirmation screen title [CHAR LIMIT=30] -->
@@ -2508,10 +2498,6 @@
     <string name="media_format_final_desc" product="default">Erase the SD card, deleting all files stored there? You can\'t reverse this action!</string>
     <!-- SD card & phone storage settings screen, button on screen after user selects Format media button -->
     <string name="media_format_final_button_text">Erase everything</string>
-    <!-- Explanation of drawing unlock pattern to format card [CHAR LIMIT=NONE] -->
-    <string name="media_format_gesture_explanation" product="nosdcard">You need to draw your unlock pattern to confirm that you want to erase the USB storage.</string>
-    <!-- Explanation of drawing unlock pattern to format card [CHAR LIMIT=NONE] -->
-    <string name="media_format_gesture_explanation" product="default">You need to draw your unlock pattern to confirm that you want to erase the SD card.</string>
 
     <!-- Main settings screen, Call settings title for item to go into the call settings -->
     <string name="call_settings_title">Call settings</string>
@@ -2764,6 +2750,26 @@
     <!-- Toast shown if setting pattern was successful -->
     <string name="lockpassword_pattern_set_toast">Pattern has been set</string>
 
+    <!-- Message to be used to explain the user that he needs to enter his pattern to continue a
+         particular operation. [CHAR LIMIT=70]-->
+    <string name="lockpassword_confirm_your_pattern_generic">Use your device pattern to continue.</string>
+    <!-- Message to be used to explain the user that he needs to enter his PIN to continue a
+         particular operation. [CHAR LIMIT=70]-->
+    <string name="lockpassword_confirm_your_pin_generic">Enter your device PIN to continue.</string>
+    <!-- Message to be used to explain the user that he needs to enter his password to continue a
+         particular operation. [CHAR LIMIT=70]-->
+    <string name="lockpassword_confirm_your_password_generic">Enter your device password to continue.</string>
+
+    <!-- Security & location settings screen, change security method screen instruction if user
+         enters incorrect PIN [CHAR LIMIT=30] -->
+    <string name="lockpassword_invalid_pin">Wrong PIN</string>
+    <!-- Security & location settings screen, change security method screen instruction if user
+         enters incorrect password [CHAR LIMIT=30] -->
+    <string name="lockpassword_invalid_password">Wrong password</string>
+    <!-- Security & location settings screen, change unlock pattern screen instruction if user
+         draws incorrect pattern [CHAR LIMIT=30] -->
+    <string name="lockpattern_need_to_unlock_wrong">Wrong pattern</string>
+
     <!-- Lock Pattern settings -->
     <!-- Security & location settings screen, header -->
     <string name="lock_settings_title">Device security</string>
@@ -2771,14 +2777,6 @@
     <string name="lockpattern_change_lock_pattern_label">Change unlock pattern</string>
     <!-- Security & location settings screen, change unlock pattern screen instruction when the user chooses "Change unlock pattern".  We first ask the user toe nter the current pattern, and this is the message seen -->
     <string name="lockpattern_change_lock_pin_label">Change unlock PIN</string>
-    <!-- Security & location settings screen, change unlock pattern screen instruction when the user chooses "Change unlock pattern".  We first ask the user toe nter the current pattern, and this is the message seen -->
-    <string name="lockpattern_need_to_unlock">Confirm saved pattern</string>
-    <!-- Do not translate. -->
-    <string name="lockpattern_need_to_unlock_footer" />
-    <!-- Security & location settings screen, change unlock pattern screen instruction if user draws incorrect pattern -->
-    <string name="lockpattern_need_to_unlock_wrong">Try again:</string>
-    <!-- Do not translate. -->
-    <string name="lockpattern_need_to_unlock_wrong_footer" />
     <!-- Security & location settings screen, change unlock pattern screen instruction on top of screen.  This si when they are supposed to draw a new unlock pattern (for example, if they are changing their unlock patterns)..-->
     <string name="lockpattern_recording_intro_header">Draw an unlock pattern</string>
     <!-- Security & location settings screen, change unlock pattern screen hint on bottom of screen.  We are telling them to press the menu button to see more options or help. -->
@@ -2820,10 +2818,9 @@
     <string name="lockpattern_settings_change_lock_pattern">Change unlock pattern</string>
     <!-- Security & location settings screen, the help instructions (an animation) caption -->
     <string name="lockpattern_settings_help_how_to_record">How to draw an unlock pattern</string>
-    <!-- Security & location settings screen, change unlock pattern screen instruction on top of screen after too many incorrect attempts -->
-    <string name="lockpattern_too_many_failed_confirmation_attempts_header">Too many incorrect attempts!</string>
-    <!-- Security & location settings screen, change unlock pattern screen countdown hint on bottom of screen after too many incorrect attempts -->
-    <string name="lockpattern_too_many_failed_confirmation_attempts_footer">Try again in <xliff:g id="number">%d</xliff:g> seconds.</string>
+    <!-- Security & location settings screen, confirm unlock pattern screen hint at the bottom after
+         too many incorrect attempts [CHAR LIMIT=120]-->
+    <string name="lockpattern_too_many_failed_confirmation_attempts">Too many incorrect attempts. Try again in <xliff:g id="number">%d</xliff:g> seconds.</string>
     <!-- Displayed when user launches a widget configuration activity that was uninstalled -->
     <string name="activity_not_found">Application is not installed on your phone.</string>
 
@@ -3410,10 +3407,6 @@
     <string name="oem_unlock_enable">OEM unlocking</string>
     <!-- setting Checkbox summary whether to enable OEM unlock [CHAR_LIMIT=50] -->
     <string name="oem_unlock_enable_summary">Allow the bootloader to be unlocked</string>
-    <!-- Message to enter device PIN to enable OEM unlock -->
-    <string name="oem_unlock_enable_pin_prompt">Enter your PIN</string>
-    <!-- Explanation to enter device pin to enable OEM unlock -->
-    <string name="oem_unlock_enable_pin_description">Enter your device PIN to enable OEM unlock</string>
     <!-- Confirmation dialog title to ensure user wishes to enable OEM unlock and disable theft protection features -->
     <string name="confirm_enable_oem_unlock_title">Allow OEM unlocking?</string>
     <!-- Warning dialog message to confirm user wishes to enable OEM unlock and disable theft protection features -->
@@ -4388,9 +4381,6 @@
     <!-- Error message for users that aren't allowed to see or modify credentials [CHAR LIMIT=none] -->
     <string name="credentials_settings_not_available">Credentials are not available for this user</string>
 
-    <!-- Explanation of drawing unlock pattern to install credentials -->
-    <string name="credentials_install_gesture_explanation">You need to draw your unlock pattern to confirm credential installation.</string>
-
     <!-- Title of dialog to enable credential storage [CHAR LIMIT=30] -->
     <string name="credentials_unlock"></string>
     <!-- Description of dialog to enable credential storage [CHAR LIMIT=NONE] -->
@@ -5293,7 +5283,7 @@
     <!-- Title for add user confirmation dialog [CHAR LIMIT=30] -->
     <string name="user_add_user_title">Add new user?</string>
     <!-- Message for add user confirmation dialog - long version. [CHAR LIMIT=none] -->
-    <string name="user_add_user_message_long">You can share this device with other people by creating additional users. Each user has their own space, which they can customize with their own apps, wallpaper, and so on. Users can also adjust device settings like Wi\u2011Fi that affect everyone.\n\nWhen you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users.</string>
+    <string name="user_add_user_message_long">You can share this device with other people by creating additional users. Each user has their own space, which they can customize with apps, wallpaper, and so on. Users can also adjust device settings like Wi\u2011Fi that affect everyone.\n\nWhen you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users.</string>
     <!-- Message for add user confirmation dialog - short version. [CHAR LIMIT=none] -->
     <string name="user_add_user_message_short">When you add a new user, that person needs to set up their space.\n\nAny user can update apps for all other users. </string>
     <!-- Title of dialog to setup a new user [CHAR LIMIT=30] -->
@@ -5908,11 +5898,23 @@
     <!-- [CHAR LIMIT=40] Zen mode settings: Delete rule dialog button caption -->
     <string name="zen_mode_delete_rule_button">Delete</string>
 
+    <!-- [CHAR LIMIT=40] Zen mode settings: External rule type -->
+    <string name="zen_mode_rule_type">Rule type</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: External rule type name if unknown -->
+    <string name="zen_mode_rule_type_unknown">Unknown</string>
+
+    <!-- [CHAR LIMIT=40] Zen mode settings: Configure external rule -->
+    <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>
+
     <!-- [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>
 
     <!-- [CHAR LIMIT=40] Zen mode settings: Rule summary template (when enabled)  -->
-    <string name="zen_mode_rule_summary_template"><xliff:g id="days" example="Sun - Thu">%1$s</xliff:g> / <xliff:g id="timerange" example="10:00 PM to 7:30 AM">%2$s</xliff:g> / <xliff:g id="mode" example="Alarms only">%3$s</xliff:g></string>
+    <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 -->
     <string name="zen_mode_schedule_rule_days">Days</string>
@@ -6311,6 +6313,9 @@
         <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps can open their domain URLs</item>
     </plurals>
 
+    <!-- Fingerprint hint message when finger was not recognized.-->
+    <string name="fingerprint_not_recognized">Not recognized</string>
+
     <!-- Title for Default Apps settings [CHAR LIMIT=30] -->
     <string name="default_apps_title">Default Apps</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 78a3572..22061e6 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -78,7 +78,6 @@
     <style name="TextAppearance.PasswordEntry" parent="android:TextAppearance.Material">
         <item name="android:gravity">center</item>
         <item name="android:singleLine">true</item>
-        <item name="android:textStyle">bold</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
@@ -350,6 +349,11 @@
         <item name="android:textColor">@color/fingerprint_message_color</item>
     </style>
 
+    <style name="TextAppearance.ConfirmDeviceCredentialsErrorText"
+        parent="android:TextAppearance.Material.Caption">
+        <item name="android:textColor">@color/warning</item>
+    </style>
+
     <style name="TextAppearance.Small.SwitchBar">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textStyle">normal</item>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 48c5457..379c36a 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -211,4 +211,24 @@
         <item name="android:colorAccent">@color/switch_accent_color</item>
     </style>
 
+    <style name="Theme.ConfirmDeviceCredentials" parent="Theme.SubSettings">
+        <item name="confirmDeviceCredentialsSideMargin">16dp</item>
+        <item name="confirmDeviceCredentialsTopMargin">16dp</item>
+    </style>
+
+    <style name="Theme.ConfirmDeviceCredentialsDark" parent="@android:style/Theme.Material">
+        <item name="android:colorPrimary">@*android:color/material_blue_grey_900</item>
+        <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_950</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="preferenceBackgroundColor">@color/confirm_device_credential_dark_background</item>
+
+        <item name="confirmDeviceCredentialsSideMargin">32dp</item>
+        <item name="confirmDeviceCredentialsTopMargin">32dp</item>
+
+        <!-- LockPatternView colors -->
+        <item name="@*android:regularColor">@color/lock_pattern_view_regular_color_dark</item>
+        <item name="@*android:successColor">@color/lock_pattern_view_regular_color_dark</item>
+        <item name="@*android:errorColor">@color/lock_pattern_view_error_color</item>
+    </style>
 </resources>
diff --git a/res/xml/zen_mode_external_rule_settings.xml b/res/xml/zen_mode_external_rule_settings.xml
new file mode 100644
index 0000000..e244dc8
--- /dev/null
+++ b/res/xml/zen_mode_external_rule_settings.xml
@@ -0,0 +1,45 @@
+<?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_external_rule_settings" >
+
+    <!-- Rule name -->
+    <Preference
+        android:key="rule_name"
+        android:title="@string/zen_mode_rule_name"
+        android:persistent="false" />
+
+    <!-- Rule type -->
+    <Preference
+        android:key="type"
+        android:title="@string/zen_mode_rule_type"
+        android:persistent="false" />
+
+    <!-- Configure -->
+    <Preference
+        android:key="configure"
+        android:title="@string/zen_mode_configure_rule"
+        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/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java
index 479baf7..f4e3c4e 100644
--- a/src/com/android/settings/ChooseLockGeneric.java
+++ b/src/com/android/settings/ChooseLockGeneric.java
@@ -145,7 +145,8 @@
             } else if (!mWaitingForConfirmation) {
                 ChooseLockSettingsHelper helper =
                         new ChooseLockSettingsHelper(this.getActivity(), this);
-                if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, null, null)) {
+                if (!helper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
+                        getString(R.string.unlock_set_unlock_launch_picker_title))) {
                     mPasswordConfirmed = true; // no password set, so no need to confirm
                     updatePreferencesOrFinish();
                 } else {
diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java
index 6d7b71d..8838286 100644
--- a/src/com/android/settings/ChooseLockPassword.java
+++ b/src/com/android/settings/ChooseLockPassword.java
@@ -243,7 +243,7 @@
                 updateStage(Stage.Introduction);
                 if (confirmCredentials) {
                     mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
-                            null, null);
+                            getString(R.string.unlock_set_unlock_launch_picker_title));
                 }
             } else {
                 mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
diff --git a/src/com/android/settings/ChooseLockPattern.java b/src/com/android/settings/ChooseLockPattern.java
index 1865a6b..8892346 100644
--- a/src/com/android/settings/ChooseLockPattern.java
+++ b/src/com/android/settings/ChooseLockPattern.java
@@ -379,7 +379,8 @@
                     updateStage(Stage.NeedToConfirm);
                     boolean launchedConfirmationActivity =
                         mChooseLockSettingsHelper.launchConfirmationActivity(
-                                CONFIRM_EXISTING_REQUEST, null, null);
+                                CONFIRM_EXISTING_REQUEST,
+                                getString(R.string.unlock_set_unlock_launch_picker_title));
                     if (!launchedConfirmationActivity) {
                         updateStage(Stage.Introduction);
                     }
diff --git a/src/com/android/settings/ChooseLockSettingsHelper.java b/src/com/android/settings/ChooseLockSettingsHelper.java
index bba45ce..65697f6 100644
--- a/src/com/android/settings/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/ChooseLockSettingsHelper.java
@@ -16,13 +16,14 @@
 
 package com.android.settings;
 
-import com.android.internal.widget.LockPatternUtils;
-
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
 
+import com.android.internal.widget.LockPatternUtils;
+
 public final class ChooseLockSettingsHelper {
 
     static final String EXTRA_KEY_TYPE = "type";
@@ -48,64 +49,77 @@
 
     /**
      * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
-     * @param message optional message to display about the action about to be done
-     * @param details optional detail message to display
+     *
+     * @param title title of the confirmation screen; shown in the action bar
      * @return true if one exists and we launched an activity to confirm it
-     * @see #onActivityResult(int, int, android.content.Intent)
+     * @see Activity#onActivityResult(int, int, android.content.Intent)
      */
-    boolean launchConfirmationActivity(int request, CharSequence message, CharSequence details) {
-        return launchConfirmationActivity(request, message, details, false);
+    boolean launchConfirmationActivity(int request, CharSequence title) {
+        return launchConfirmationActivity(request, title, null, null, false, false);
     }
 
     /**
      * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
-     * @param message optional message to display about the action about to be done
-     * @param details optional detail message to display
+     *
+     * @param title title of the confirmation screen; shown in the action bar
      * @param returnCredentials if true, put credentials into intent. Note that if this is true,
-                                this can only be called internally.
+     *                          this can only be called internally.
      * @return true if one exists and we launched an activity to confirm it
-     * @see #onActivityResult(int, int, android.content.Intent)
+     * @see Activity#onActivityResult(int, int, android.content.Intent)
      */
-    boolean launchConfirmationActivity(int request, CharSequence message, CharSequence details,
-                                       boolean returnCredentials) {
+    boolean launchConfirmationActivity(int request, CharSequence title, boolean returnCredentials) {
+        return launchConfirmationActivity(request, title, null, null, returnCredentials, false);
+    }
+
+    /**
+     * If a pattern, password or PIN exists, prompt the user before allowing them to change it.
+     *
+     * @param title title of the confirmation screen; shown in the action bar
+     * @param header header of the confirmation screen; shown as large text
+     * @param description description of the confirmation screen
+     * @param returnCredentials if true, put credentials into intent. Note that if this is true,
+     *                          this can only be called internally.
+     * @param external specifies whether this activity is launched externally, meaning that it will
+     *                 get a dark theme and allow fingerprint authentication
+     * @return true if one exists and we launched an activity to confirm it
+     * @see Activity#onActivityResult(int, int, android.content.Intent)
+     */
+    boolean launchConfirmationActivity(int request, @Nullable CharSequence title,
+            @Nullable CharSequence header, @Nullable CharSequence description,
+            boolean returnCredentials, boolean external) {
         boolean launched = false;
         switch (mLockPatternUtils.getKeyguardStoredPasswordQuality()) {
             case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
-                launched = confirmPattern(request, message, details, returnCredentials);
+                launched = launchConfirmationActivity(request, title, header, description,
+                        returnCredentials
+                                ? ConfirmLockPattern.InternalActivity.class
+                                : ConfirmLockPattern.class, external);
                 break;
             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
             case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
             case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
-                // TODO: update UI layout for ConfirmPassword to show message and details
-                launched = confirmPassword(request, message, returnCredentials);
+                launched = launchConfirmationActivity(request, title, header, description,
+                        returnCredentials
+                                ? ConfirmLockPassword.InternalActivity.class
+                                : ConfirmLockPassword.class, external);
                 break;
         }
         return launched;
     }
 
-    /**
-     * Launch screen to confirm the existing lock pattern.
-     * @param message shown in header of ConfirmLockPattern if not null
-     * @param details shown in footer of ConfirmLockPattern if not null
-     * @param returnCredentials if true, put credentials into intent.
-     * @see #onActivityResult(int, int, android.content.Intent)
-     * @return true if we launched an activity to confirm pattern
-     */
-    private boolean confirmPattern(int request, CharSequence message,
-                                   CharSequence details, boolean returnCredentials) {
-        if (!mLockPatternUtils.isLockPatternEnabled()) {
-            return false;
-        }
+    private boolean launchConfirmationActivity(int request, CharSequence title, CharSequence header,
+            CharSequence message, Class<?> activityClass, boolean external) {
         final Intent intent = new Intent();
-        // supply header and footer text in the intent
-        intent.putExtra(ConfirmLockPattern.HEADER_TEXT, message);
-        intent.putExtra(ConfirmLockPattern.FOOTER_TEXT, details);
-        intent.setClassName("com.android.settings",
-                            returnCredentials
-                            ? ConfirmLockPattern.InternalActivity.class.getName()
-                            : ConfirmLockPattern.class.getName());
+        intent.putExtra(ConfirmDeviceCredentialBaseFragment.TITLE_TEXT, title);
+        intent.putExtra(ConfirmDeviceCredentialBaseFragment.HEADER_TEXT, header);
+        intent.putExtra(ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT, message);
+        intent.putExtra(ConfirmDeviceCredentialBaseFragment.ALLOW_FP_AUTHENTICATION, external);
+        intent.putExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, external);
+        intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_CANCEL_BUTTON, external);
+        intent.putExtra(ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, external);
+        intent.setClassName(ConfirmDeviceCredentialBaseFragment.PACKAGE, activityClass.getName());
         if (mFragment != null) {
             mFragment.startActivityForResult(intent, request);
         } else {
@@ -113,31 +127,4 @@
         }
         return true;
     }
-
-    /**
-     * Launch screen to confirm the existing lock password.
-     * @param message shown in header of ConfirmLockPassword if not null
-     * @param returnCredentials if true, put credentials into intent.
-     * @see #onActivityResult(int, int, android.content.Intent)
-     * @return true if we launched an activity to confirm password
-     */
-    private boolean confirmPassword(int request, CharSequence message,
-            boolean returnCredentials) {
-        if (!mLockPatternUtils.isLockPasswordEnabled()) return false;
-        final Intent intent = new Intent();
-        // supply header text in the intent
-        intent.putExtra(ConfirmLockPattern.HEADER_TEXT, message);
-        intent.setClassName("com.android.settings",
-                            returnCredentials
-                            ? ConfirmLockPassword.InternalActivity.class.getName()
-                            : ConfirmLockPassword.class.getName());
-        if (mFragment != null) {
-            mFragment.startActivityForResult(intent, request);
-        } else {
-            mActivity.startActivityForResult(intent, request);
-        }
-        return true;
-    }
-
-
 }
diff --git a/src/com/android/settings/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/ConfirmDeviceCredentialActivity.java
index beb2d97..9a7f843 100644
--- a/src/com/android/settings/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/ConfirmDeviceCredentialActivity.java
@@ -48,7 +48,8 @@
         String details = intent.getStringExtra(KeyguardManager.EXTRA_DESCRIPTION);
 
         ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this);
-        if (!helper.launchConfirmationActivity(0 /* request code */, title, details)) {
+        if (!helper.launchConfirmationActivity(0 /* request code */, null /* title */, title,
+                details, false /* returnCredentials */, true /* isExternal */)) {
             Log.d(TAG, "No pattern, password or PIN set.");
             setResult(Activity.RESULT_OK);
             finish();
diff --git a/src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java b/src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java
new file mode 100644
index 0000000..fe3de97
--- /dev/null
+++ b/src/com/android/settings/ConfirmDeviceCredentialBaseActivity.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+import android.app.KeyguardManager;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.WindowManager;
+
+public class ConfirmDeviceCredentialBaseActivity extends SettingsActivity {
+
+    @Override
+    protected void onCreate(Bundle savedState) {
+        if (getIntent().getBooleanExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, false)) {
+            setTheme(R.style.Theme_ConfirmDeviceCredentialsDark);
+        }
+        super.onCreate(savedState);
+        boolean deviceLocked = getSystemService(KeyguardManager.class).isKeyguardLocked();
+        if (deviceLocked && getIntent().getBooleanExtra(
+                ConfirmDeviceCredentialBaseFragment.SHOW_WHEN_LOCKED, false)) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+        }
+        CharSequence msg = getIntent().getStringExtra(
+                ConfirmDeviceCredentialBaseFragment.TITLE_TEXT);
+        setTitle(msg);
+        if (getActionBar() != null) {
+            getActionBar().setDisplayHomeAsUpEnabled(true);
+            getActionBar().setHomeButtonEnabled(true);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java
new file mode 100644
index 0000000..f9908b0
--- /dev/null
+++ b/src/com/android/settings/ConfirmDeviceCredentialBaseFragment.java
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
+ */
+public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFragment
+        implements FingerprintUiHelper.Callback {
+
+    public static final String PACKAGE = "com.android.settings";
+    public static final String TITLE_TEXT = PACKAGE + ".ConfirmCredentials.title";
+    public static final String HEADER_TEXT = PACKAGE + ".ConfirmCredentials.header";
+    public static final String DETAILS_TEXT = PACKAGE + ".ConfirmCredentials.details";
+    public static final String ALLOW_FP_AUTHENTICATION =
+            PACKAGE + ".ConfirmCredentials.allowFpAuthentication";
+    public static final String DARK_THEME = PACKAGE + ".ConfirmCredentials.darkTheme";
+    public static final String SHOW_CANCEL_BUTTON =
+            PACKAGE + ".ConfirmCredentials.showCancelButton";
+    public static final String SHOW_WHEN_LOCKED =
+            PACKAGE + ".ConfirmCredentials.showWhenLocked";
+
+    private FingerprintUiHelper mFingerprintHelper;
+    private boolean mAllowFpAuthentication;
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mAllowFpAuthentication = getActivity().getIntent().getBooleanExtra(
+                ALLOW_FP_AUTHENTICATION, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        mFingerprintHelper = new FingerprintUiHelper(
+                (ImageView) view.findViewById(R.id.fingerprintIcon),
+                (TextView) view.findViewById(R.id.errorText), this);
+        boolean showCancelButton = getActivity().getIntent().getBooleanExtra(
+                SHOW_CANCEL_BUTTON, false);
+        Button cancelButton = (Button) view.findViewById(R.id.cancelButton);
+        cancelButton.setVisibility(showCancelButton ? View.VISIBLE : View.GONE);
+        cancelButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                getActivity().finish();
+            }
+        });
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mAllowFpAuthentication) {
+            mFingerprintHelper.startListening();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mAllowFpAuthentication) {
+            mFingerprintHelper.stopListening();
+        }
+    }
+
+    @Override
+    public void onAuthenticated() {
+        // Check whether we are still active.
+        if (getActivity() != null && getActivity().isResumed()) {
+            authenticationSucceeded(null /* password */);
+        }
+    }
+
+    protected abstract void authenticationSucceeded(@Nullable String password);
+
+    @Override
+    public void onFingerprintIconVisibilityChanged(boolean visible) {
+    }
+}
diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java
index b49dc6e..44c599b 100644
--- a/src/com/android/settings/ConfirmLockPassword.java
+++ b/src/com/android/settings/ConfirmLockPassword.java
@@ -16,14 +16,11 @@
 
 package com.android.settings;
 
+import android.annotation.Nullable;
 import android.text.TextUtils;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.PasswordEntryKeyboardHelper;
-import com.android.internal.widget.PasswordEntryKeyboardView;
 
-import android.app.Activity;
-import android.app.Fragment;
 import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
 import android.os.Bundle;
@@ -31,23 +28,18 @@
 import android.os.Handler;
 import android.os.SystemClock;
 import android.os.storage.StorageManager;
-import android.text.Editable;
 import android.text.InputType;
-import android.text.TextWatcher;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.inputmethod.EditorInfo;
-import android.widget.Button;
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
-public class ConfirmLockPassword extends SettingsActivity {
-
-    public static final String PACKAGE = "com.android.settings";
-    public static final String HEADER_TEXT = PACKAGE + ".ConfirmLockPattern.header";
+public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
 
     public static class InternalActivity extends ConfirmLockPassword {
     }
@@ -65,28 +57,17 @@
         return false;
     }
 
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        // Disable IME on our window since we provide our own keyboard
-        //getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
-                //WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
-        super.onCreate(savedInstanceState);
-        CharSequence msg = getText(R.string.lockpassword_confirm_your_password_header);
-        setTitle(msg);
-    }
-
-    public static class ConfirmLockPasswordFragment extends InstrumentedFragment
-            implements OnClickListener, OnEditorActionListener, TextWatcher {
+    public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
+            implements OnClickListener, OnEditorActionListener {
         private static final String KEY_NUM_WRONG_CONFIRM_ATTEMPTS
                 = "confirm_lock_password_fragment.key_num_wrong_confirm_attempts";
         private static final long ERROR_MESSAGE_TIMEOUT = 3000;
         private TextView mPasswordEntry;
         private LockPatternUtils mLockPatternUtils;
-        private TextView mHeaderText;
+        private TextView mHeaderTextView;
+        private TextView mDetailsTextView;
+        private TextView mErrorTextView;
         private Handler mHandler = new Handler();
-        private PasswordEntryKeyboardHelper mKeyboardHelper;
-        private PasswordEntryKeyboardView mKeyboardView;
-        private Button mContinueButton;
         private int mNumWrongConfirmAttempts;
         private CountDownTimer mCountdownTimer;
         private boolean mIsAlpha;
@@ -111,51 +92,35 @@
                 Bundle savedInstanceState) {
             final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality();
             View view = inflater.inflate(R.layout.confirm_lock_password, null);
-            // Disable IME on our window since we provide our own keyboard
-
-            view.findViewById(R.id.cancel_button).setOnClickListener(this);
-            mContinueButton = (Button) view.findViewById(R.id.next_button);
-            mContinueButton.setOnClickListener(this);
-            mContinueButton.setEnabled(false); // disable until the user enters at least one char
 
             mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
             mPasswordEntry.setOnEditorActionListener(this);
-            mPasswordEntry.addTextChangedListener(this);
 
-            mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
-            mHeaderText = (TextView) view.findViewById(R.id.headerText);
+            mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
+            mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
+            mErrorTextView = (TextView) view.findViewById(R.id.errorText);
             mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
                     || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
                     || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality;
 
             Intent intent = getActivity().getIntent();
             if (intent != null) {
-                CharSequence headerMessage = intent.getCharSequenceExtra(HEADER_TEXT);
+                CharSequence headerMessage = intent.getCharSequenceExtra(
+                        ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
+                CharSequence detailsMessage = intent.getCharSequenceExtra(
+                        ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
                 if (TextUtils.isEmpty(headerMessage)) {
                     headerMessage = getString(getDefaultHeader());
                 }
-                mHeaderText.setText(headerMessage);
+                if (TextUtils.isEmpty(detailsMessage)) {
+                    detailsMessage = getString(getDefaultDetails());
+                }
+                mHeaderTextView.setText(headerMessage);
+                mDetailsTextView.setText(detailsMessage);
             }
-
-            final Activity activity = getActivity();
-            mKeyboardHelper = new PasswordEntryKeyboardHelper(activity,
-                    mKeyboardView, mPasswordEntry);
-            mKeyboardHelper.setKeyboardMode(mIsAlpha ?
-                    PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
-                    : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
-            mKeyboardView.requestFocus();
-
             int currentType = mPasswordEntry.getInputType();
             mPasswordEntry.setInputType(mIsAlpha ? currentType
                     : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
-
-            if (activity instanceof SettingsActivity) {
-                final SettingsActivity sa = (SettingsActivity) activity;
-                int id = getDefaultHeader();
-                CharSequence title = getText(id);
-                sa.setTitle(title);
-            }
-
             return view;
         }
 
@@ -164,10 +129,19 @@
                     : R.string.lockpassword_confirm_your_pin_header;
         }
 
+        private int getDefaultDetails() {
+            return mIsAlpha ? R.string.lockpassword_confirm_your_password_generic
+                    : R.string.lockpassword_confirm_your_pin_generic;
+        }
+
+        private int getErrorMessage() {
+            return mIsAlpha ? R.string.lockpassword_invalid_password
+                    : R.string.lockpassword_invalid_pin;
+        }
+
         @Override
         public void onPause() {
             super.onPause();
-            mKeyboardView.requestFocus();
             if (mCountdownTimer != null) {
                 mCountdownTimer.cancel();
                 mCountdownTimer = null;
@@ -181,9 +155,7 @@
 
         @Override
         public void onResume() {
-            // TODO Auto-generated method stub
             super.onResume();
-            mKeyboardView.requestFocus();
             long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
             if (deadline != 0) {
                 handleAttemptLockout(deadline);
@@ -196,33 +168,35 @@
             outState.putInt(KEY_NUM_WRONG_CONFIRM_ATTEMPTS, mNumWrongConfirmAttempts);
         }
 
+        @Override
+        protected void authenticationSucceeded(@Nullable String password) {
+            Intent intent = new Intent();
+            if (getActivity() instanceof ConfirmLockPassword.InternalActivity) {
+                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
+                        mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
+                                : StorageManager.CRYPT_TYPE_PIN);
+                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password);
+            }
+            getActivity().setResult(RESULT_OK, intent);
+            getActivity().finish();
+        }
+
         private void handleNext() {
             final String pin = mPasswordEntry.getText().toString();
             if (mLockPatternUtils.checkPassword(pin)) {
-
-                Intent intent = new Intent();
-                if (getActivity() instanceof ConfirmLockPassword.InternalActivity) {
-                    intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
-                                    mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
-                                             : StorageManager.CRYPT_TYPE_PIN);
-                    intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
-                }
-
-                getActivity().setResult(RESULT_OK, intent);
-                getActivity().finish();
+                authenticationSucceeded(pin);
             } else {
                 if (++mNumWrongConfirmAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
                     handleAttemptLockout(deadline);
                 } else {
-                    showError(R.string.lockpattern_need_to_unlock_wrong);
+                    showError(getErrorMessage());
                 }
             }
         }
 
         private void handleAttemptLockout(long elapsedRealtimeDeadline) {
             long elapsedRealtime = SystemClock.elapsedRealtime();
-            showError(R.string.lockpattern_too_many_failed_confirmation_attempts_header, 0);
             mPasswordEntry.setEnabled(false);
             mCountdownTimer = new CountDownTimer(
                     elapsedRealtimeDeadline - elapsedRealtime,
@@ -231,15 +205,15 @@
                 @Override
                 public void onTick(long millisUntilFinished) {
                     final int secondsCountdown = (int) (millisUntilFinished / 1000);
-                    mHeaderText.setText(getString(
-                            R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
-                            secondsCountdown));
+                    showError(getString(
+                            R.string.lockpattern_too_many_failed_confirmation_attempts,
+                            secondsCountdown), 0);
                 }
 
                 @Override
                 public void onFinish() {
                     mPasswordEntry.setEnabled(true);
-                    mHeaderText.setText(getDefaultHeader());
+                    mErrorTextView.setText("");
                     mNumWrongConfirmAttempts = 0;
                 }
             }.start();
@@ -264,13 +238,13 @@
 
         private final Runnable mResetErrorRunnable = new Runnable() {
             public void run() {
-                mHeaderText.setText(getDefaultHeader());
+                mErrorTextView.setText("");
             }
         };
 
-        private void showError(int msg, long timeout) {
-            mHeaderText.setText(msg);
-            mHeaderText.announceForAccessibility(mHeaderText.getText());
+        private void showError(CharSequence msg, long timeout) {
+            mErrorTextView.setText(msg);
+            mErrorTextView.announceForAccessibility(mErrorTextView.getText());
             mPasswordEntry.setText(null);
             mHandler.removeCallbacks(mResetErrorRunnable);
             if (timeout != 0) {
@@ -278,6 +252,10 @@
             }
         }
 
+        private void showError(int msg, long timeout) {
+            showError(getText(msg), timeout);
+        }
+
         // {@link OnEditorActionListener} methods.
         public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
             // Check if this was the result of hitting the enter or "done" key
@@ -289,16 +267,5 @@
             }
             return false;
         }
-
-        // {@link TextWatcher} methods.
-        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-        }
-
-        public void onTextChanged(CharSequence s, int start, int before, int count) {
-        }
-
-        public void afterTextChanged(Editable s) {
-            mContinueButton.setEnabled(mPasswordEntry.getText().length() > 0);
-        }
     }
 }
diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java
index b5d444b..8c4fabc 100644
--- a/src/com/android/settings/ConfirmLockPattern.java
+++ b/src/com/android/settings/ConfirmLockPattern.java
@@ -22,13 +22,14 @@
 import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
 import com.android.internal.widget.LockPatternView.Cell;
 
+import android.annotation.Nullable;
 import android.app.Activity;
-import android.app.Fragment;
 import android.content.Intent;
 import android.os.CountDownTimer;
 import android.os.SystemClock;
 import android.os.Bundle;
 import android.os.storage.StorageManager;
+import android.view.MenuItem;
 import android.widget.TextView;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -42,23 +43,11 @@
  * Sets an activity result of {@link Activity#RESULT_OK} when the user
  * successfully confirmed their pattern.
  */
-public class ConfirmLockPattern extends SettingsActivity {
+public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
 
     public static class InternalActivity extends ConfirmLockPattern {
     }
 
-    /**
-     * Names of {@link CharSequence} fields within the originating {@link Intent}
-     * that are used to configure the keyguard confirmation view's labeling.
-     * The view will use the system-defined resource strings for any labels that
-     * the caller does not supply.
-     */
-    public static final String PACKAGE = "com.android.settings";
-    public static final String HEADER_TEXT = PACKAGE + ".ConfirmLockPattern.header";
-    public static final String FOOTER_TEXT = PACKAGE + ".ConfirmLockPattern.footer";
-    public static final String HEADER_WRONG_TEXT = PACKAGE + ".ConfirmLockPattern.header_wrong";
-    public static final String FOOTER_WRONG_TEXT = PACKAGE + ".ConfirmLockPattern.footer_wrong";
-
     private enum Stage {
         NeedToUnlock,
         NeedToUnlockWrong,
@@ -66,13 +55,6 @@
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        CharSequence msg = getText(R.string.lockpassword_confirm_your_pattern_header);
-        setTitle(msg);
-    }
-
-    @Override
     public Intent getIntent() {
         Intent modIntent = new Intent(super.getIntent());
         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPatternFragment.class.getName());
@@ -85,7 +67,7 @@
         return false;
     }
 
-    public static class ConfirmLockPatternFragment extends InstrumentedFragment {
+    public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment {
 
         // how long we wait to clear a wrong pattern
         private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
@@ -98,13 +80,14 @@
         private CountDownTimer mCountdownTimer;
 
         private TextView mHeaderTextView;
-        private TextView mFooterTextView;
+        private TextView mDetailsTextView;
+        private TextView mErrorTextView;
+        private View mLeftSpacerLandscape;
+        private View mRightSpacerLandscape;
 
         // caller-supplied text for various prompts
         private CharSequence mHeaderText;
-        private CharSequence mFooterText;
-        private CharSequence mHeaderWrongText;
-        private CharSequence mFooterWrongText;
+        private CharSequence mDetailsText;
 
         // required constructor for fragments
         public ConfirmLockPatternFragment() {
@@ -123,7 +106,10 @@
             View view = inflater.inflate(R.layout.confirm_lock_pattern, null);
             mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
             mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
-            mFooterTextView = (TextView) view.findViewById(R.id.footerText);
+            mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
+            mErrorTextView = (TextView) view.findViewById(R.id.errorText);
+            mLeftSpacerLandscape = view.findViewById(R.id.leftSpacer);
+            mRightSpacerLandscape = view.findViewById(R.id.rightSpacer);
 
             // make it so unhandled touch events within the unlock screen go to the
             // lock pattern view.
@@ -133,10 +119,10 @@
 
             Intent intent = getActivity().getIntent();
             if (intent != null) {
-                mHeaderText = intent.getCharSequenceExtra(HEADER_TEXT);
-                mFooterText = intent.getCharSequenceExtra(FOOTER_TEXT);
-                mHeaderWrongText = intent.getCharSequenceExtra(HEADER_WRONG_TEXT);
-                mFooterWrongText = intent.getCharSequenceExtra(FOOTER_WRONG_TEXT);
+                mHeaderText = intent.getCharSequenceExtra(
+                        ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
+                mDetailsText = intent.getCharSequenceExtra(
+                        ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
             }
 
             mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
@@ -199,28 +185,21 @@
                     if (mHeaderText != null) {
                         mHeaderTextView.setText(mHeaderText);
                     } else {
-                        mHeaderTextView.setText(R.string.lockpattern_need_to_unlock);
+                        mHeaderTextView.setText(R.string.lockpassword_confirm_your_pattern_header);
                     }
-                    if (mFooterText != null) {
-                        mFooterTextView.setText(mFooterText);
+                    if (mDetailsText != null) {
+                        mDetailsTextView.setText(mDetailsText);
                     } else {
-                        mFooterTextView.setText(R.string.lockpattern_need_to_unlock_footer);
+                        mDetailsTextView.setText(
+                                R.string.lockpassword_confirm_your_pattern_generic);
                     }
+                    mErrorTextView.setText("");
 
                     mLockPatternView.setEnabled(true);
                     mLockPatternView.enableInput();
                     break;
                 case NeedToUnlockWrong:
-                    if (mHeaderWrongText != null) {
-                        mHeaderTextView.setText(mHeaderWrongText);
-                    } else {
-                        mHeaderTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
-                    }
-                    if (mFooterWrongText != null) {
-                        mFooterTextView.setText(mFooterWrongText);
-                    } else {
-                        mFooterTextView.setText(R.string.lockpattern_need_to_unlock_wrong_footer);
-                    }
+                    mErrorTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
 
                     mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
                     mLockPatternView.setEnabled(true);
@@ -252,6 +231,28 @@
             mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
         }
 
+        @Override
+        protected void authenticationSucceeded(@Nullable String password) {
+            Intent intent = new Intent();
+            if (getActivity() instanceof ConfirmLockPattern.InternalActivity) {
+                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
+                        StorageManager.CRYPT_TYPE_PATTERN);
+                intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password);
+            }
+            getActivity().setResult(Activity.RESULT_OK, intent);
+            getActivity().finish();
+        }
+
+        @Override
+        public void onFingerprintIconVisibilityChanged(boolean visible) {
+            if (mLeftSpacerLandscape != null && mRightSpacerLandscape != null) {
+
+                // In landscape, adjust spacing depending on fingerprint icon visibility.
+                mLeftSpacerLandscape.setVisibility(visible ? View.GONE : View.VISIBLE);
+                mRightSpacerLandscape.setVisibility(visible ? View.GONE : View.VISIBLE);
+            }
+        }
+
         /**
          * The pattern listener that responds according to a user confirming
          * an existing lock pattern.
@@ -273,17 +274,7 @@
 
             public void onPatternDetected(List<LockPatternView.Cell> pattern) {
                 if (mLockPatternUtils.checkPattern(pattern)) {
-
-                    Intent intent = new Intent();
-                    if (getActivity() instanceof ConfirmLockPattern.InternalActivity) {
-                        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
-                                        StorageManager.CRYPT_TYPE_PATTERN);
-                        intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD,
-                                        LockPatternUtils.patternToString(pattern));
-                    }
-
-                    getActivity().setResult(Activity.RESULT_OK, intent);
-                    getActivity().finish();
+                    authenticationSucceeded(LockPatternUtils.patternToString(pattern));
                 } else {
                     if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
                             ++mNumWrongConfirmAttempts
@@ -308,10 +299,9 @@
 
                 @Override
                 public void onTick(long millisUntilFinished) {
-                    mHeaderTextView.setText(R.string.lockpattern_too_many_failed_confirmation_attempts_header);
                     final int secondsCountdown = (int) (millisUntilFinished / 1000);
-                    mFooterTextView.setText(getString(
-                            R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
+                    mErrorTextView.setText(getString(
+                            R.string.lockpattern_too_many_failed_confirmation_attempts,
                             secondsCountdown));
                 }
 
diff --git a/src/com/android/settings/CredentialStorage.java b/src/com/android/settings/CredentialStorage.java
index 45d3d11..8506964 100644
--- a/src/com/android/settings/CredentialStorage.java
+++ b/src/com/android/settings/CredentialStorage.java
@@ -439,9 +439,8 @@
     private boolean confirmKeyGuard() {
         Resources res = getResources();
         boolean launched = new ChooseLockSettingsHelper(this)
-                .launchConfirmationActivity(CONFIRM_KEY_GUARD_REQUEST, null,
-                                            res.getText(R.string.credentials_install_gesture_explanation),
-                                            true);
+                .launchConfirmationActivity(CONFIRM_KEY_GUARD_REQUEST,
+                        res.getText(R.string.credentials_title), true);
         return launched;
     }
 
diff --git a/src/com/android/settings/CryptKeeperSettings.java b/src/com/android/settings/CryptKeeperSettings.java
index 3fb60e3..2e4aeb8 100644
--- a/src/com/android/settings/CryptKeeperSettings.java
+++ b/src/com/android/settings/CryptKeeperSettings.java
@@ -166,9 +166,8 @@
             return true;
         }
 
-        return helper.launchConfirmationActivity(request, null,
-                res.getText(R.string.crypt_keeper_confirm_encrypt),
-                true);
+        return helper.launchConfirmationActivity(request,
+                res.getText(R.string.crypt_keeper_encrypt_title), true);
     }
 
     @Override
diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java
index 5fafc92..5c7e0e9 100644
--- a/src/com/android/settings/DevelopmentSettings.java
+++ b/src/com/android/settings/DevelopmentSettings.java
@@ -1610,10 +1610,8 @@
     }
 
     private boolean showKeyguardConfirmation(Resources resources, int requestCode) {
-        return new ChooseLockSettingsHelper(getActivity(), this)
-                .launchConfirmationActivity(requestCode,
-                        resources.getString(R.string.oem_unlock_enable_pin_prompt),
-                        resources.getString(R.string.oem_unlock_enable_pin_description));
+        return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
+                requestCode, resources.getString(R.string.oem_unlock_enable));
     }
 
     @Override
diff --git a/src/com/android/settings/FingerprintUiHelper.java b/src/com/android/settings/FingerprintUiHelper.java
new file mode 100644
index 0000000..20ad7fc
--- /dev/null
+++ b/src/com/android/settings/FingerprintUiHelper.java
@@ -0,0 +1,119 @@
+/*
+ * 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;
+
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.CancellationSignal;
+import android.os.Vibrator;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * Small helper class to manage text/icon around fingerprint authentication UI.
+ */
+public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback {
+
+    private static final long ERROR_TIMEOUT = 1300;
+    private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30};
+    private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30};
+
+    private ImageView mIcon;
+    private TextView mErrorTextView;
+    private CancellationSignal mCancellationSignal;
+
+    private Callback mCallback;
+    private FingerprintManager mFingerprintManager;
+
+    public FingerprintUiHelper(ImageView icon, TextView errorTextView, Callback callback) {
+        mFingerprintManager = icon.getContext().getSystemService(FingerprintManager.class);
+        mIcon = icon;
+        mErrorTextView = errorTextView;
+        mCallback = callback;
+    }
+
+    public void startListening() {
+        if (mFingerprintManager.getEnrolledFingerprints().size() > 0) {
+            mCancellationSignal = new CancellationSignal();
+            mFingerprintManager.authenticate(null, mCancellationSignal, this, 0 /* flags */);
+            setFingerprintIconVisibility(true);
+            mIcon.setImageResource(R.drawable.ic_fingerprint);
+        }
+    }
+
+    public void stopListening() {
+        mCancellationSignal.cancel();
+        mCancellationSignal = null;
+    }
+
+    private void setFingerprintIconVisibility(boolean visible) {
+        mIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mCallback.onFingerprintIconVisibilityChanged(visible);
+    }
+
+    @Override
+    public void onAuthenticationError(int errMsgId, CharSequence errString) {
+        showError(errString);
+        setFingerprintIconVisibility(false);
+    }
+
+    @Override
+    public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+        showError(helpString);
+    }
+
+    @Override
+    public void onAuthenticationFailed() {
+        showError(mIcon.getResources().getString(
+                R.string.fingerprint_not_recognized));
+    }
+
+    @Override
+    public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+        vibrateFingerprintSuccess();
+        mCallback.onAuthenticated();
+    }
+
+    private void showError(CharSequence error) {
+        vibrateFingerprintError();
+        mIcon.setImageResource(R.drawable.ic_fingerprint_error);
+        mErrorTextView.setText(error);
+        mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
+        mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT);
+    }
+
+    private void vibrateFingerprintError() {
+        mIcon.getContext().getSystemService(Vibrator.class).vibrate(FP_ERROR_VIBRATE_PATTERN, -1);
+    }
+
+    private void vibrateFingerprintSuccess() {
+        mIcon.getContext().getSystemService(Vibrator.class).vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1);
+    }
+
+    private Runnable mResetErrorTextRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mErrorTextView.setText("");
+            mIcon.setImageResource(R.drawable.ic_fingerprint);
+        }
+    };
+
+    public interface Callback {
+        void onAuthenticated();
+        void onFingerprintIconVisibilityChanged(boolean visible);
+    }
+}
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 1b6468f..23f6812 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -75,8 +75,8 @@
      */
     private boolean runKeyguardConfirmation(int request) {
         Resources res = getActivity().getResources();
-        return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(request,
-                null, res.getText(R.string.master_clear_gesture_explanation));
+        return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
+                request, res.getText(R.string.master_clear_title));
     }
 
     @Override
diff --git a/src/com/android/settings/MediaFormat.java b/src/com/android/settings/MediaFormat.java
index 20dac8e..517ec2d 100644
--- a/src/com/android/settings/MediaFormat.java
+++ b/src/com/android/settings/MediaFormat.java
@@ -72,9 +72,8 @@
      * component as a subactivity
      */
     private boolean runKeyguardConfirmation(int request) {
-        return new ChooseLockSettingsHelper(this)
-                .launchConfirmationActivity(request, null,
-                        getText(R.string.media_format_gesture_explanation));
+        return new ChooseLockSettingsHelper(this).launchConfirmationActivity(request,
+                getText(R.string.media_format_title));
     }
 
     @Override
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index a3376d3..587b8e8 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -78,8 +78,8 @@
      */
     private boolean runKeyguardConfirmation(int request) {
         Resources res = getActivity().getResources();
-        return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(request,
-                null, res.getText(R.string.reset_network_gesture_explanation));
+        return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
+                request, res.getText(R.string.reset_network_title));
     }
 
     @Override
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index c74c90a..fd99536 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -603,8 +603,9 @@
             ChooseLockSettingsHelper helper =
                     new ChooseLockSettingsHelper(this.getActivity(), this);
             mTrustAgentClickIntent = preference.getIntent();
-            if (!helper.launchConfirmationActivity(CHANGE_TRUST_AGENT_SETTINGS, null, null) &&
-                    mTrustAgentClickIntent != null) {
+            boolean confirmationLaunched = helper.launchConfirmationActivity(
+                    CHANGE_TRUST_AGENT_SETTINGS, preference.getTitle());
+            if (!confirmationLaunched&&  mTrustAgentClickIntent != null) {
                 // If this returns false, it means no password confirmation is required.
                 startActivity(mTrustAgentClickIntent);
                 mTrustAgentClickIntent = null;
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index fe0df59..ba08036 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -98,6 +98,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 ZenModeExternalRuleSettingsActivity extends SettingsActivity { /* empty */ }
     public static class NotificationSettingsActivity extends SettingsActivity { /* empty */ }
     public static class NotificationAppListActivity extends SettingsActivity { /* empty */ }
     public static class AppNotificationSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 7bfd249..0fdf04b 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -95,11 +95,11 @@
 import com.android.settings.nfc.AndroidBeam;
 import com.android.settings.nfc.PaymentSettings;
 import com.android.settings.notification.AppNotificationSettings;
-import com.android.settings.notification.ConditionProviderSettings;
 import com.android.settings.notification.NotificationAccessSettings;
 import com.android.settings.notification.NotificationSettings;
 import com.android.settings.notification.NotificationStation;
 import com.android.settings.notification.OtherSoundSettings;
+import com.android.settings.notification.ZenModeExternalRuleSettings;
 import com.android.settings.notification.ZenModeSettings;
 import com.android.settings.notification.ZenModeScheduleRuleSettings;
 import com.android.settings.print.PrintJobSettingsFragment;
@@ -320,7 +320,6 @@
             DreamSettings.class.getName(),
             UserSettings.class.getName(),
             NotificationAccessSettings.class.getName(),
-            ConditionProviderSettings.class.getName(),
             PrintSettingsFragment.class.getName(),
             PrintJobSettingsFragment.class.getName(),
             TrustedCredentialsSettings.class.getName(),
@@ -337,6 +336,7 @@
             ApnSettings.class.getName(),
             WifiCallingSettings.class.getName(),
             ZenModeScheduleRuleSettings.class.getName(),
+            ZenModeExternalRuleSettings.class.getName(),
     };
 
 
diff --git a/src/com/android/settings/notification/ConditionProviderSettings.java b/src/com/android/settings/notification/ConditionProviderSettings.java
deleted file mode 100644
index 76576ab..0000000
--- a/src/com/android/settings/notification/ConditionProviderSettings.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2014 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.content.pm.PackageManager;
-import android.provider.Settings;
-import android.service.notification.ConditionProviderService;
-
-public class ConditionProviderSettings extends ManagedServiceSettings {
-    private static final String TAG = ConditionProviderSettings.class.getSimpleName();
-    private static final Config CONFIG = getConditionProviderConfig();
-
-    private static Config getConditionProviderConfig() {
-        final Config c = new Config();
-        c.tag = TAG;
-        c.setting = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
-        c.intentAction = ConditionProviderService.SERVICE_INTERFACE;
-        c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
-        c.noun = "condition provider";
-        return c;
-    }
-
-    @Override
-    protected Config getConfig() {
-        return CONFIG;
-    }
-
-    public static int getProviderCount(PackageManager pm) {
-        return getServicesCount(CONFIG, pm);
-    }
-
-    public static int getEnabledProviderCount(Context context) {
-        return getEnabledServicesCount(CONFIG, context);
-    }
-}
diff --git a/src/com/android/settings/notification/ManagedServiceSettings.java b/src/com/android/settings/notification/ManagedServiceSettings.java
index 7be644e..cc9e734 100644
--- a/src/com/android/settings/notification/ManagedServiceSettings.java
+++ b/src/com/android/settings/notification/ManagedServiceSettings.java
@@ -16,28 +16,17 @@
 
 package com.android.settings.notification;
 
-import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
 import android.app.ListFragment;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.provider.Settings;
-import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -49,17 +38,15 @@
 
 import com.android.settings.R;
 
-import java.util.HashSet;
 import java.util.List;
 
 public abstract class ManagedServiceSettings extends ListFragment {
     private static final boolean SHOW_PACKAGE_NAME = false;
 
     private final Config mConfig;
-    private PackageManager mPM;
-    private ContentResolver mCR;
 
-    private final HashSet<ComponentName> mEnabledServices = new HashSet<ComponentName>();
+    private PackageManager mPM;
+    private ServiceListing mServiceListing;
     private ServiceListAdapter mListAdapter;
 
     abstract protected Config getConfig();
@@ -68,19 +55,65 @@
         mConfig = getConfig();
     }
 
-    private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            updateList();
-        }
-    };
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
 
-    private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            updateList();
+        mPM = getActivity().getPackageManager();
+        mServiceListing = new ServiceListing(getActivity(), mConfig);
+        mServiceListing.addCallback(new ServiceListing.Callback() {
+            @Override
+            public void onServicesReloaded(List<ServiceInfo> services) {
+                updateList(services);
+            }
+        });
+        mListAdapter = new ServiceListAdapter(getActivity());
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View v =  inflater.inflate(R.layout.managed_service_settings, container, false);
+        TextView empty = (TextView) v.findViewById(android.R.id.empty);
+        empty.setText(mConfig.emptyText);
+        return v;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mServiceListing.reload();
+        mServiceListing.setListening(true);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mServiceListing.setListening(false);
+    }
+
+    private void updateList(List<ServiceInfo> services) {
+        mListAdapter.clear();
+        mListAdapter.addAll(services);
+        mListAdapter.sort(new PackageItemInfo.DisplayNameComparator(mPM));
+
+        getListView().setAdapter(mListAdapter);
+    }
+
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        ServiceInfo info = mListAdapter.getItem(position);
+        final ComponentName cn = new ComponentName(info.packageName, info.name);
+        if (mServiceListing.isEnabled(cn)) {
+            // the simple version: disabling
+            mServiceListing.setEnabled(cn, false);
+        } else {
+            // show a scary dialog
+            new ScaryWarningDialogFragment()
+                .setServiceInfo(cn, info.loadLabel(mPM).toString())
+                .show(getFragmentManager(), "dialog");
         }
-    };
+    }
 
     public class ScaryWarningDialogFragment extends DialogFragment {
         static final String KEY_COMPONENT = "c";
@@ -99,7 +132,8 @@
             super.onCreate(savedInstanceState);
             final Bundle args = getArguments();
             final String label = args.getString(KEY_LABEL);
-            final ComponentName cn = ComponentName.unflattenFromString(args.getString(KEY_COMPONENT));
+            final ComponentName cn = ComponentName.unflattenFromString(args
+                    .getString(KEY_COMPONENT));
 
             final String title = getResources().getString(mConfig.warningDialogTitle, label);
             final String summary = getResources().getString(mConfig.warningDialogSummary, label);
@@ -110,8 +144,7 @@
                     .setPositiveButton(android.R.string.ok,
                             new DialogInterface.OnClickListener() {
                                 public void onClick(DialogInterface dialog, int id) {
-                                    mEnabledServices.add(cn);
-                                    saveEnabledServices();
+                                    mServiceListing.setEnabled(cn, true);
                                 }
                             })
                     .setNegativeButton(android.R.string.cancel,
@@ -124,151 +157,6 @@
         }
     }
 
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        mPM = getActivity().getPackageManager();
-        mCR = getActivity().getContentResolver();
-        mListAdapter = new ServiceListAdapter(getActivity());
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        View v =  inflater.inflate(R.layout.managed_service_settings, container, false);
-        TextView empty = (TextView) v.findViewById(android.R.id.empty);
-        empty.setText(mConfig.emptyText);
-        return v;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        updateList();
-
-        // listen for package changes
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-        filter.addDataScheme("package");
-        getActivity().registerReceiver(mPackageReceiver, filter);
-
-        mCR.registerContentObserver(Settings.Secure.getUriFor(mConfig.setting),
-                false, mSettingsObserver);
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-
-        getActivity().unregisterReceiver(mPackageReceiver);
-        mCR.unregisterContentObserver(mSettingsObserver);
-    }
-
-    private void loadEnabledServices() {
-        mEnabledServices.clear();
-        final String flat = Settings.Secure.getString(mCR, mConfig.setting);
-        if (flat != null && !"".equals(flat)) {
-            final String[] names = flat.split(":");
-            for (int i = 0; i < names.length; i++) {
-                final ComponentName cn = ComponentName.unflattenFromString(names[i]);
-                if (cn != null) {
-                    mEnabledServices.add(cn);
-                }
-            }
-        }
-    }
-
-    private void saveEnabledServices() {
-        StringBuilder sb = null;
-        for (ComponentName cn : mEnabledServices) {
-            if (sb == null) {
-                sb = new StringBuilder();
-            } else {
-                sb.append(':');
-            }
-            sb.append(cn.flattenToString());
-        }
-        Settings.Secure.putString(mCR,
-                mConfig.setting,
-                sb != null ? sb.toString() : "");
-    }
-
-    private void updateList() {
-        loadEnabledServices();
-
-        getServices(mConfig, mListAdapter, mPM);
-        mListAdapter.sort(new PackageItemInfo.DisplayNameComparator(mPM));
-
-        getListView().setAdapter(mListAdapter);
-    }
-
-    protected static int getEnabledServicesCount(Config config, Context context) {
-        final String flat = Settings.Secure.getString(context.getContentResolver(), config.setting);
-        if (flat == null || "".equals(flat)) return 0;
-        final String[] components = flat.split(":");
-        return components.length;
-    }
-
-    protected static int getServicesCount(Config c, PackageManager pm) {
-        return getServices(c, null, pm);
-    }
-
-    private static int getServices(Config c, ArrayAdapter<ServiceInfo> adapter, PackageManager pm) {
-        int services = 0;
-        if (adapter != null) {
-            adapter.clear();
-        }
-        final int user = ActivityManager.getCurrentUser();
-
-        List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
-                new Intent(c.intentAction),
-                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
-                user);
-
-        for (int i = 0, count = installedServices.size(); i < count; i++) {
-            ResolveInfo resolveInfo = installedServices.get(i);
-            ServiceInfo info = resolveInfo.serviceInfo;
-
-            if (!c.permission.equals(info.permission)) {
-                Slog.w(c.tag, "Skipping " + c.noun + " service "
-                        + info.packageName + "/" + info.name
-                        + ": it does not require the permission "
-                        + c.permission);
-                continue;
-            }
-            if (adapter != null) {
-                adapter.add(info);
-            }
-            services++;
-        }
-        return services;
-    }
-
-    private boolean isServiceEnabled(ServiceInfo info) {
-        final ComponentName cn = new ComponentName(info.packageName, info.name);
-        return mEnabledServices.contains(cn);
-    }
-
-    @Override
-    public void onListItemClick(ListView l, View v, int position, long id) {
-        ServiceInfo info = mListAdapter.getItem(position);
-        final ComponentName cn = new ComponentName(info.packageName, info.name);
-        if (mEnabledServices.contains(cn)) {
-            // the simple version: disabling
-            mEnabledServices.remove(cn);
-            saveEnabledServices();
-        } else {
-            // show a scary dialog
-            new ScaryWarningDialogFragment()
-                .setServiceInfo(cn, info.loadLabel(mPM).toString())
-                .show(getFragmentManager(), "dialog");
-        }
-    }
-
     private static class ViewHolder {
         ImageView icon;
         TextView name;
@@ -327,7 +215,8 @@
             } else {
                 vh.description.setVisibility(View.GONE);
             }
-            vh.checkbox.setChecked(isServiceEnabled(info));
+            final ComponentName cn = new ComponentName(info.packageName, info.name);
+            vh.checkbox.setChecked(mServiceListing.isEnabled(cn));
         }
     }
 
diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
index ced71a4..5104d4a 100644
--- a/src/com/android/settings/notification/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -46,10 +46,10 @@
     }
 
     public static int getListenersCount(PackageManager pm) {
-        return getServicesCount(CONFIG, pm);
+        return ServiceListing.getServicesCount(CONFIG, pm);
     }
 
     public static int getEnabledListenersCount(Context context) {
-        return getEnabledServicesCount(CONFIG, context);
+        return ServiceListing.getEnabledServicesCount(CONFIG, context);
     }
 }
diff --git a/src/com/android/settings/notification/ServiceListing.java b/src/com/android/settings/notification/ServiceListing.java
new file mode 100644
index 0000000..d296139
--- /dev/null
+++ b/src/com/android/settings/notification/ServiceListing.java
@@ -0,0 +1,206 @@
+/*
+ * 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.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.settings.notification.ManagedServiceSettings.Config;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+public class ServiceListing {
+    private final ContentResolver mContentResolver;
+    private final Context mContext;
+    private final Config mConfig;
+    private final HashSet<ComponentName> mEnabledServices = new HashSet<ComponentName>();
+    private final List<ServiceInfo> mServices = new ArrayList<ServiceInfo>();
+    private final List<Callback> mCallbacks = new ArrayList<Callback>();
+
+    private boolean mListening;
+
+    public ServiceListing(Context context, Config config) {
+        mContext = context;
+        mConfig = config;
+        mContentResolver = context.getContentResolver();
+    }
+
+    public void addCallback(Callback callback) {
+        mCallbacks.add(callback);
+    }
+
+    public void removeCallback(Callback callback) {
+        mCallbacks.remove(callback);
+    }
+
+    public void setListening(boolean listening) {
+        if (mListening == listening) return;
+        mListening = listening;
+        if (mListening) {
+            // listen for package changes
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+            filter.addDataScheme("package");
+            mContext.registerReceiver(mPackageReceiver, filter);
+            mContentResolver.registerContentObserver(Settings.Secure.getUriFor(mConfig.setting),
+                    false, mSettingsObserver);
+        } else {
+            mContext.unregisterReceiver(mPackageReceiver);
+            mContentResolver.unregisterContentObserver(mSettingsObserver);
+        }
+    }
+
+    public static int getEnabledServicesCount(Config config, Context context) {
+        final String flat = Settings.Secure.getString(context.getContentResolver(), config.setting);
+        if (flat == null || "".equals(flat)) return 0;
+        final String[] components = flat.split(":");
+        return components.length;
+    }
+
+    public static int getServicesCount(Config c, PackageManager pm) {
+        return getServices(c, null, pm);
+    }
+
+    public static ServiceInfo findService(Context context, Config config, final ComponentName cn) {
+        final ServiceListing listing = new ServiceListing(context, config);
+        final List<ServiceInfo> services = listing.reload();
+        for (ServiceInfo service : services) {
+            final ComponentName serviceCN = new ComponentName(service.packageName, service.name);
+            if (serviceCN.equals(cn)) {
+                return service;
+            }
+        }
+        return null;
+    }
+
+    private static int getServices(Config c, List<ServiceInfo> list, PackageManager pm) {
+        int services = 0;
+        if (list != null) {
+            list.clear();
+        }
+        final int user = ActivityManager.getCurrentUser();
+
+        List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
+                new Intent(c.intentAction),
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+                user);
+
+        for (int i = 0, count = installedServices.size(); i < count; i++) {
+            ResolveInfo resolveInfo = installedServices.get(i);
+            ServiceInfo info = resolveInfo.serviceInfo;
+
+            if (!c.permission.equals(info.permission)) {
+                Slog.w(c.tag, "Skipping " + c.noun + " service "
+                        + info.packageName + "/" + info.name
+                        + ": it does not require the permission "
+                        + c.permission);
+                continue;
+            }
+            if (list != null) {
+                list.add(info);
+            }
+            services++;
+        }
+        return services;
+    }
+
+    private void saveEnabledServices() {
+        StringBuilder sb = null;
+        for (ComponentName cn : mEnabledServices) {
+            if (sb == null) {
+                sb = new StringBuilder();
+            } else {
+                sb.append(':');
+            }
+            sb.append(cn.flattenToString());
+        }
+        Settings.Secure.putString(mContentResolver, mConfig.setting,
+                sb != null ? sb.toString() : "");
+    }
+
+    private void loadEnabledServices() {
+        mEnabledServices.clear();
+        final String flat = Settings.Secure.getString(mContentResolver, mConfig.setting);
+        if (flat != null && !"".equals(flat)) {
+            final String[] names = flat.split(":");
+            for (int i = 0; i < names.length; i++) {
+                final ComponentName cn = ComponentName.unflattenFromString(names[i]);
+                if (cn != null) {
+                    mEnabledServices.add(cn);
+                }
+            }
+        }
+    }
+
+    public List<ServiceInfo> reload() {
+        loadEnabledServices();
+        getServices(mConfig, mServices, mContext.getPackageManager());
+        for (Callback callback : mCallbacks) {
+            callback.onServicesReloaded(mServices);
+        }
+        return mServices;
+    }
+
+    public boolean isEnabled(ComponentName cn) {
+        return mEnabledServices.contains(cn);
+    }
+
+    public void setEnabled(ComponentName cn, boolean enabled) {
+        if (enabled) {
+            mEnabledServices.add(cn);
+        } else {
+            mEnabledServices.remove(cn);
+        }
+        saveEnabledServices();
+    }
+
+    private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            reload();
+        }
+    };
+
+    private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            reload();
+        }
+    };
+
+    public interface Callback {
+        void onServicesReloaded(List<ServiceInfo> services);
+    }
+}
diff --git a/src/com/android/settings/notification/ZenModeAutomationSettings.java b/src/com/android/settings/notification/ZenModeAutomationSettings.java
index c43d99e..f2ee71d 100644
--- a/src/com/android/settings/notification/ZenModeAutomationSettings.java
+++ b/src/com/android/settings/notification/ZenModeAutomationSettings.java
@@ -18,12 +18,16 @@
 
 import static android.service.notification.ZenModeConfig.ALL_DAYS;
 
+import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ServiceInfo;
 import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceScreen;
+import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.service.notification.ConditionProviderService;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.service.notification.ZenModeConfig.ZenRule;
@@ -35,30 +39,39 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.settings.R;
+import com.android.settings.notification.ManagedServiceSettings.Config;
+import com.android.settings.notification.ZenRuleNameDialog.RuleInfo;
 
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
+import java.util.List;
 import java.util.TreeSet;
 
 public class ZenModeAutomationSettings extends ZenModeSettingsBase {
     private static final SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("EEE");
 
+    static final Config CONFIG = getConditionProviderConfig();
+
     private final Calendar mCalendar = Calendar.getInstance();
 
+    private ServiceListing mServiceListing;
+
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-
         setHasOptionsMenu(true);
-
         addPreferencesFromResource(R.xml.zen_mode_automation_settings);
+        mServiceListing = new ServiceListing(mContext, CONFIG);
+        mServiceListing.addCallback(mServiceListingCallback);
+        mServiceListing.reload();
+        mServiceListing.setListening(true);
     }
 
-    private void showRule(String ruleId, String ruleName) {
-        if (DEBUG) Log.d(TAG, "showRule " + ruleId + " name=" + ruleName);
-        mContext.startActivity(new Intent(ZenModeScheduleRuleSettings.ACTION)
-                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
-                .putExtra(ZenModeScheduleRuleSettings.EXTRA_RULE_ID, ruleId));
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mServiceListing.setListening(false);
+        mServiceListing.removeCallback(mServiceListingCallback);
     }
 
     @Override
@@ -75,29 +88,6 @@
         return super.onOptionsItemSelected(item);
     }
 
-    private void showAddRuleDialog() {
-        new ZenRuleNameDialog(mContext, "", mConfig.getAutomaticRuleNames()) {
-            @Override
-            public void onOk(String ruleName) {
-                final ScheduleInfo schedule = new ScheduleInfo();
-                schedule.days = ZenModeConfig.ALL_DAYS;
-                schedule.startHour = 22;
-                schedule.endHour = 7;
-                final ZenRule rule = new ZenRule();
-                rule.name = ruleName;
-                rule.enabled = true;
-                rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-                rule.conditionId = ZenModeConfig.toScheduleConditionId(schedule);
-                final ZenModeConfig newConfig = mConfig.copy();
-                final String ruleId = newConfig.newRuleId();
-                newConfig.automaticRules.put(ruleId, rule);
-                if (setZenModeConfig(newConfig)) {
-                    showRule(ruleId, rule.name);
-                }
-            }
-        }.show();
-    }
-
     @Override
     protected void onZenModeChanged() {
         // don't care
@@ -114,15 +104,42 @@
         updateControls();
     }
 
+    private void showAddRuleDialog() {
+        new ZenRuleNameDialog(mContext, mServiceListing, null, mConfig.getAutomaticRuleNames()) {
+            @Override
+            public void onOk(String ruleName, RuleInfo ri) {
+                final ZenRule rule = new ZenRule();
+                rule.name = ruleName;
+                rule.enabled = true;
+                rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+                rule.conditionId = ri.defaultConditionId;
+                rule.component = ri.serviceComponent;
+                final ZenModeConfig newConfig = mConfig.copy();
+                final String ruleId = newConfig.newRuleId();
+                newConfig.automaticRules.put(ruleId, rule);
+                if (setZenModeConfig(newConfig)) {
+                    showRule(ri.settingsAction, ri.configurationActivity, ruleId, rule.name);
+                }
+            }
+        }.show();
+    }
+
+    private void showRule(String settingsAction, ComponentName configurationActivity,
+            String ruleId, String ruleName) {
+        if (DEBUG) Log.d(TAG, "showRule " + ruleId + " name=" + ruleName);
+        mContext.startActivity(new Intent(settingsAction)
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+                .putExtra(ZenModeRuleSettingsBase.EXTRA_RULE_ID, ruleId));
+    }
+
     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);
-            if (!ZenModeConfig.isValidScheduleConditionId(rule.conditionId)) continue;
+            final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(rule.conditionId);
             final Preference p = new Preference(mContext);
             p.setTitle(rule.name);
             p.setSummary(computeRuleSummary(rule));
@@ -130,7 +147,9 @@
             p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                 @Override
                 public boolean onPreferenceClick(Preference preference) {
-                    showRule(id, rule.name);
+                    final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION
+                            : ZenModeExternalRuleSettings.ACTION;
+                    showRule(action, null, id, rule.name);
                     return true;
                 }
             });
@@ -146,13 +165,16 @@
     private String computeRuleSummary(ZenRule rule) {
         if (rule == null || !rule.enabled) return getString(R.string.switch_off_text);
         final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(rule.conditionId);
-        if (schedule == null) return getString(R.string.switch_on_text);
-        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);
         final String mode = ZenModeSettings.computeZenModeCaption(getResources(), rule.zenMode);
-        return getString(R.string.zen_mode_rule_summary_template, days, time, mode);
+        String summary = getString(R.string.switch_on_text);
+        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);
+        }
+        return getString(R.string.zen_mode_rule_summary_combination, summary, mode);
     }
 
     private String getTime(int hour, int minute) {
@@ -199,4 +221,30 @@
         return DAY_FORMAT.format(mCalendar.getTime());
     }
 
+    private static Config getConditionProviderConfig() {
+        final Config c = new Config();
+        c.tag = TAG;
+        c.setting = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
+        c.intentAction = ConditionProviderService.SERVICE_INTERFACE;
+        c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
+        c.noun = "condition provider";
+        return c;
+    }
+
+    private final ServiceListing.Callback mServiceListingCallback = new ServiceListing.Callback() {
+        @Override
+        public void onServicesReloaded(List<ServiceInfo> services) {
+            for (ServiceInfo service : services) {
+                final RuleInfo ri = ZenModeExternalRuleSettings.getRuleInfo(service);
+                if (ri != null && ri.serviceComponent != null
+                        && ri.settingsAction == ZenModeExternalRuleSettings.ACTION) {
+                    if (!mServiceListing.isEnabled(ri.serviceComponent)) {
+                        Log.i(TAG, "Enabling external condition provider: " + ri.serviceComponent);
+                        mServiceListing.setEnabled(ri.serviceComponent, true);
+                    }
+                }
+            }
+        }
+    };
+
 }
diff --git a/src/com/android/settings/notification/ZenModeExternalRuleSettings.java b/src/com/android/settings/notification/ZenModeExternalRuleSettings.java
new file mode 100644
index 0000000..9f9dc8a
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeExternalRuleSettings.java
@@ -0,0 +1,133 @@
+/*
+ * 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.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.service.notification.ZenModeConfig.ZenRule;
+import android.util.Log;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.R;
+import com.android.settings.notification.ZenRuleNameDialog.RuleInfo;
+
+public class ZenModeExternalRuleSettings extends ZenModeRuleSettingsBase {
+    private static final String KEY_TYPE = "type";
+    private static final String KEY_CONFIGURE = "configure";
+
+    public static final String ACTION = Settings.ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS;
+    private static final int REQUEST_CODE_CONFIGURE = 1;
+
+    private static final String MD_RULE_TYPE = "automatic.ruleType";
+    private static final String MD_DEFAULT_CONDITION_ID = "automatic.defaultConditionId";
+    private static final String MD_CONFIGURATION_ACTIVITY = "automatic.configurationActivity";
+    private static final String EXTRA_CONDITION_ID = "automatic.conditionId";
+
+    private Preference mType;
+    private Preference mConfigure;
+
+    @Override
+    protected boolean setRule(ZenRule rule) {
+        return rule != null;
+    }
+
+    @Override
+    protected String getZenModeDependency() {
+        return null;
+    }
+
+    @Override
+    protected void onCreateInternal() {
+        addPreferencesFromResource(R.xml.zen_mode_external_rule_settings);
+        final PreferenceScreen root = getPreferenceScreen();
+        final ServiceInfo si = ServiceListing.findService(mContext,
+                ZenModeAutomationSettings.CONFIG, mRule.component);
+        if (DEBUG) Log.d(TAG, "ServiceInfo: " + si);
+        final RuleInfo ri = getRuleInfo(si);
+        if (DEBUG) Log.d(TAG, "RuleInfo: " + ri);
+        mType = root.findPreference(KEY_TYPE);
+        if (ri == null) {
+            mType.setSummary(R.string.zen_mode_rule_type_unknown);
+        } else {
+            mType.setSummary(ri.caption);
+        }
+
+        mConfigure = root.findPreference(KEY_CONFIGURE);
+        if (ri == null || ri.configurationActivity == null) {
+            mConfigure.setEnabled(false);
+        } else {
+            mConfigure.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+                @Override
+                public boolean onPreferenceClick(Preference preference) {
+                    startActivityForResult(new Intent().setComponent(ri.configurationActivity),
+                            REQUEST_CODE_CONFIGURE);
+                    return true;
+                }
+            });
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (requestCode == REQUEST_CODE_CONFIGURE) {
+            if (resultCode == Activity.RESULT_OK && data != null) {
+                final Uri conditionId = data.getParcelableExtra(EXTRA_CONDITION_ID);
+                if (conditionId != null && !conditionId.equals(mRule.conditionId)) {
+                    updateRule(conditionId);
+                }
+            }
+        }
+    }
+
+    public static RuleInfo getRuleInfo(ServiceInfo si) {
+        if (si == null || si.metaData == null) return null;
+        final String ruleType = si.metaData.getString(MD_RULE_TYPE);
+        final String defaultConditionId = si.metaData.getString(MD_DEFAULT_CONDITION_ID);
+        final String configurationActivity = si.metaData.getString(MD_CONFIGURATION_ACTIVITY);
+        if (ruleType != null && !ruleType.trim().isEmpty() && defaultConditionId != null) {
+            final RuleInfo ri = new RuleInfo();
+            ri.serviceComponent = new ComponentName(si.packageName, si.name);
+            ri.settingsAction = ZenModeExternalRuleSettings.ACTION;
+            ri.caption = ruleType;
+            ri.defaultConditionId = Uri.parse(defaultConditionId);
+            if (configurationActivity != null) {
+                ri.configurationActivity = ComponentName.unflattenFromString(configurationActivity);
+            }
+            return ri;
+        }
+        return null;
+    }
+
+    @Override
+    protected void updateControlsInternal() {
+        // everything done up front
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.NOTIFICATION_ZEN_MODE_EXTERNAL_RULE;
+    }
+
+}
diff --git a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
new file mode 100644
index 0000000..f6bc75f
--- /dev/null
+++ b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java
@@ -0,0 +1,256 @@
+/*
+ * 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.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceScreen;
+import android.provider.Settings.Global;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.ZenRule;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.Switch;
+import android.widget.Toast;
+
+import com.android.settings.DropDownPreference;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.widget.SwitchBar;
+
+public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase
+        implements SwitchBar.OnSwitchChangeListener {
+    protected static final String TAG = ZenModeSettingsBase.TAG;
+    protected static final boolean DEBUG = ZenModeSettingsBase.DEBUG;
+
+    public static final String EXTRA_RULE_ID = "rule_id";
+    private static final String KEY_RULE_NAME = "rule_name";
+    private static final String KEY_ZEN_MODE = "zen_mode";
+
+    protected Context mContext;
+    protected boolean mDisableListeners;
+    protected ZenRule mRule;
+
+    private String mRuleId;
+    private boolean mDeleting;
+    private Preference mRuleName;
+    private SwitchBar mSwitchBar;
+    private DropDownPreference mZenMode;
+
+    abstract protected void onCreateInternal();
+    abstract protected boolean setRule(ZenRule rule);
+    abstract protected String getZenModeDependency();
+    abstract protected void updateControlsInternal();
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mContext = getActivity();
+
+        final Intent intent = getActivity().getIntent();
+        if (DEBUG) Log.d(TAG, "onCreate getIntent()=" + intent);
+        if (intent == null) {
+            Log.w(TAG, "No intent");
+            toastAndFinish();
+            return;
+        }
+
+        mRuleId = intent.getStringExtra(EXTRA_RULE_ID);
+        if (DEBUG) Log.d(TAG, "mRuleId=" + mRuleId);
+        if (refreshRuleOrFinish()) {
+            return;
+        }
+
+        setHasOptionsMenu(true);
+
+        onCreateInternal();
+
+        final PreferenceScreen root = getPreferenceScreen();
+        mRuleName = root.findPreference(KEY_RULE_NAME);
+        mRuleName.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+            @Override
+            public boolean onPreferenceClick(Preference preference) {
+                showRuleNameDialog();
+                return true;
+            }
+        });
+
+        mZenMode = (DropDownPreference) root.findPreference(KEY_ZEN_MODE);
+        mZenMode.addItem(R.string.zen_mode_option_important_interruptions,
+                Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenMode.addItem(R.string.zen_mode_option_alarms, Global.ZEN_MODE_ALARMS);
+        mZenMode.addItem(R.string.zen_mode_option_no_interruptions,
+                Global.ZEN_MODE_NO_INTERRUPTIONS);
+        mZenMode.setCallback(new DropDownPreference.Callback() {
+            @Override
+            public boolean onItemSelected(int pos, Object value) {
+                if (mDisableListeners) return true;
+                final int zenMode = (Integer) value;
+                if (zenMode == mRule.zenMode) return true;
+                if (DEBUG) Log.d(TAG, "onPrefChange zenMode=" + zenMode);
+                mRule.zenMode = zenMode;
+                setZenModeConfig(mConfig);
+                return true;
+            }
+        });
+        mZenMode.setOrder(10);  // sort at the bottom of the category
+        mZenMode.setDependency(getZenModeDependency());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        updateControls();
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        final SettingsActivity activity = (SettingsActivity) getActivity();
+        mSwitchBar = activity.getSwitchBar();
+        mSwitchBar.addOnSwitchChangeListener(this);
+        mSwitchBar.show();
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mSwitchBar.removeOnSwitchChangeListener(this);
+        mSwitchBar.hide();
+    }
+
+    @Override
+    public void onSwitchChanged(Switch switchView, boolean isChecked) {
+        if (DEBUG) Log.d(TAG, "onSwitchChanged " + isChecked);
+        if (mDisableListeners) return;
+        final boolean enabled = isChecked;
+        if (enabled == mRule.enabled) return;
+        if (DEBUG) Log.d(TAG, "onSwitchChanged enabled=" + enabled);
+        mRule.enabled = enabled;
+        mRule.snoozing = false;
+        setZenModeConfig(mConfig);
+    }
+
+    protected void updateRule(Uri newConditionId) {
+        mRule.conditionId = newConditionId;
+        mRule.condition = null;
+        mRule.snoozing = false;
+        setZenModeConfig(mConfig);
+    }
+
+    @Override
+    protected void onZenModeChanged() {
+        // noop
+    }
+
+    @Override
+    protected void onZenModeConfigChanged() {
+        if (!refreshRuleOrFinish()) {
+            updateControls();
+        }
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        if (DEBUG) Log.d(TAG, "onCreateOptionsMenu");
+        inflater.inflate(R.menu.zen_mode_rule, menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (DEBUG) Log.d(TAG, "onOptionsItemSelected " + item.getItemId());
+        if (item.getItemId() == R.id.delete) {
+            showDeleteRuleDialog();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void showRuleNameDialog() {
+        new ZenRuleNameDialog(mContext, null, mRule.name, mConfig.getAutomaticRuleNames()) {
+            @Override
+            public void onOk(String ruleName, RuleInfo type) {
+                final ZenModeConfig newConfig = mConfig.copy();
+                final ZenRule rule = newConfig.automaticRules.get(mRuleId);
+                if (rule == null) return;
+                rule.name = ruleName;
+                setZenModeConfig(newConfig);
+            }
+        }.show();
+    }
+
+    private boolean refreshRuleOrFinish() {
+        mRule = mConfig.automaticRules.get(mRuleId);
+        if (DEBUG) Log.d(TAG, "mRule=" + mRule);
+        if (!setRule(mRule)) {
+            toastAndFinish();
+            return true;
+        }
+        return false;
+    }
+
+    private void showDeleteRuleDialog() {
+        new AlertDialog.Builder(mContext)
+                .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, mRule.name))
+                .setNegativeButton(R.string.cancel, null)
+                .setPositiveButton(R.string.zen_mode_delete_rule_button, new OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        mDeleting = true;
+                        mConfig.automaticRules.remove(mRuleId);
+                        setZenModeConfig(mConfig);
+                    }
+                })
+                .show();
+    }
+
+    private void toastAndFinish() {
+        if (!mDeleting) {
+            Toast.makeText(mContext, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT)
+                    .show();
+        }
+        getActivity().finish();
+    }
+
+    private void updateRuleName() {
+        getActivity().setTitle(mRule.name);
+        mRuleName.setSummary(mRule.name);
+    }
+
+    private void updateControls() {
+        mDisableListeners = true;
+        updateRuleName();
+        updateControlsInternal();
+        mZenMode.setSelectedValue(mRule.zenMode);
+        mDisableListeners = false;
+        if (mSwitchBar != null) {
+            mSwitchBar.setChecked(mRule.enabled);
+        }
+    }
+
+}
diff --git a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
index f0f0294..f7015d3 100644
--- a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
+++ b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java
@@ -25,141 +25,58 @@
 import android.app.TimePickerDialog;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
 import android.content.DialogInterface.OnDismissListener;
-import android.content.Intent;
 import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.service.notification.ZenModeConfig.ZenRule;
 import android.text.format.DateFormat;
 import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.widget.Switch;
 import android.widget.TimePicker;
-import android.widget.Toast;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.settings.DropDownPreference;
 import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.widget.SwitchBar;
 
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Calendar;
 
-public class ZenModeScheduleRuleSettings extends ZenModeSettingsBase
-        implements SwitchBar.OnSwitchChangeListener {
-    private static final String TAG = ZenModeSettingsBase.TAG;
-    private static final boolean DEBUG = ZenModeSettingsBase.DEBUG;
-
-    private static final String KEY_RULE_NAME = "rule_name";
+public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase {
     private static final String KEY_DAYS = "days";
     private static final String KEY_START_TIME = "start_time";
     private static final String KEY_END_TIME = "end_time";
-    private static final String KEY_ZEN_MODE = "zen_mode";
 
     private static final SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("EEE");
 
     public static final String ACTION = Settings.ACTION_ZEN_MODE_SCHEDULE_RULE_SETTINGS;
-    public static final String EXTRA_RULE_ID = "rule_id";
 
-    private Context mContext;
-    private boolean mDisableListeners;
-    private SwitchBar mSwitchBar;
-    private Preference mRuleName;
     private Preference mDays;
     private TimePickerPreference mStart;
     private TimePickerPreference mEnd;
-    private DropDownPreference mZenMode;
 
-    private String mRuleId;
-    private ZenRule mRule;
     private ScheduleInfo mSchedule;
-    private boolean mDeleting;
 
     @Override
-    protected void onZenModeChanged() {
-        // noop
-    }
-
-    @Override
-    protected void onZenModeConfigChanged() {
-        if (!refreshRuleOrFinish()) {
-            updateControls();
-        }
-    }
-
-    private boolean refreshRuleOrFinish() {
-        mRule = mConfig.automaticRules.get(mRuleId);
-        if (DEBUG) Log.d(TAG, "mRule=" + mRule);
-        mSchedule = mRule != null ? ZenModeConfig.tryParseScheduleConditionId(mRule.conditionId)
+    protected boolean setRule(ZenRule rule) {
+        mSchedule = rule != null ? ZenModeConfig.tryParseScheduleConditionId(rule.conditionId)
                 : null;
-        if (mSchedule == null) {
-            toastAndFinish();
-            return true;
-        }
-        return false;
+        return mSchedule != null;
     }
 
     @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        if (DEBUG) Log.d(TAG, "onCreateOptionsMenu");
-        inflater.inflate(R.menu.zen_mode_rule, menu);
+    protected String getZenModeDependency() {
+        return mDays.getKey();
     }
 
     @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (DEBUG) Log.d(TAG, "onOptionsItemSelected " + item.getItemId());
-        if (item.getItemId() == R.id.delete) {
-            showDeleteRuleDialog();
-            return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        mContext = getActivity();
-
-        final Intent intent = getActivity().getIntent();
-        if (DEBUG) Log.d(TAG, "onCreate getIntent()=" + intent);
-        if (intent == null) {
-            Log.w(TAG, "No intent");
-            toastAndFinish();
-            return;
-        }
-
-        mRuleId = intent.getStringExtra(EXTRA_RULE_ID);
-        if (DEBUG) Log.d(TAG, "mRuleId=" + mRuleId);
-        if (refreshRuleOrFinish()) {
-            return;
-        }
-
+    protected void onCreateInternal() {
         addPreferencesFromResource(R.xml.zen_mode_schedule_rule_settings);
         final PreferenceScreen root = getPreferenceScreen();
 
-        setHasOptionsMenu(true);
-
-        mRuleName = root.findPreference(KEY_RULE_NAME);
-        mRuleName.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-            @Override
-            public boolean onPreferenceClick(Preference preference) {
-                showRuleNameDialog();
-                return true;
-            }
-        });
-
         mDays = root.findPreference(KEY_DAYS);
         mDays.setOnPreferenceClickListener(new OnPreferenceClickListener() {
             @Override
@@ -186,10 +103,7 @@
                 if (DEBUG) Log.d(TAG, "onPrefChange start h=" + hour + " m=" + minute);
                 mSchedule.startHour = hour;
                 mSchedule.startMinute = minute;
-                mRule.conditionId = ZenModeConfig.toScheduleConditionId(mSchedule);
-                mRule.condition = null;
-                mRule.snoozing = false;
-                setZenModeConfig(mConfig);
+                updateRule(ZenModeConfig.toScheduleConditionId(mSchedule));
                 return true;
             }
         });
@@ -211,63 +125,12 @@
                 if (DEBUG) Log.d(TAG, "onPrefChange end h=" + hour + " m=" + minute);
                 mSchedule.endHour = hour;
                 mSchedule.endMinute = minute;
-                mRule.conditionId = ZenModeConfig.toScheduleConditionId(mSchedule);
-                mRule.condition = null;
-                mRule.snoozing = false;
-                setZenModeConfig(mConfig);
+                updateRule(ZenModeConfig.toScheduleConditionId(mSchedule));
                 return true;
             }
         });
         root.addPreference(mEnd);
         mEnd.setDependency(mDays.getKey());
-
-        mZenMode = (DropDownPreference) root.findPreference(KEY_ZEN_MODE);
-        mZenMode.addItem(R.string.zen_mode_option_important_interruptions, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-        mZenMode.addItem(R.string.zen_mode_option_alarms, Global.ZEN_MODE_ALARMS);
-        mZenMode.addItem(R.string.zen_mode_option_no_interruptions, Global.ZEN_MODE_NO_INTERRUPTIONS);
-        mZenMode.setCallback(new DropDownPreference.Callback() {
-            @Override
-            public boolean onItemSelected(int pos, Object value) {
-                if (mDisableListeners) return true;
-                final int zenMode = (Integer) value;
-                if (zenMode == mRule.zenMode) return true;
-                if (DEBUG) Log.d(TAG, "onPrefChange zenMode=" + zenMode);
-                mRule.zenMode = zenMode;
-                setZenModeConfig(mConfig);
-                return true;
-            }
-        });
-        mZenMode.setOrder(10);  // sort at the bottom of the category
-        mZenMode.setDependency(mDays.getKey());
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-
-        final SettingsActivity activity = (SettingsActivity) getActivity();
-        mSwitchBar = activity.getSwitchBar();
-        mSwitchBar.addOnSwitchChangeListener(this);
-        mSwitchBar.show();
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        mSwitchBar.removeOnSwitchChangeListener(this);
-        mSwitchBar.hide();
-    }
-
-    @Override
-    public void onSwitchChanged(Switch switchView, boolean isChecked) {
-        if (DEBUG) Log.d(TAG, "onSwitchChanged " + isChecked);
-        if (mDisableListeners) return;
-        final boolean enabled = isChecked;
-        if (enabled == mRule.enabled) return;
-        if (DEBUG) Log.d(TAG, "onSwitchChanged enabled=" + enabled);
-        mRule.enabled = enabled;
-        mRule.snoozing = false;
-        setZenModeConfig(mConfig);
     }
 
     private void updateDays() {
@@ -308,28 +171,11 @@
     }
 
     @Override
-    public void onResume() {
-        super.onResume();
-        updateControls();
-    }
-
-    private void updateRuleName() {
-        getActivity().setTitle(mRule.name);
-        mRuleName.setSummary(mRule.name);
-    }
-
-    private void updateControls() {
-        mDisableListeners = true;
-        updateRuleName();
+    protected void updateControlsInternal() {
         updateDays();
         mStart.setTime(mSchedule.startHour, mSchedule.startMinute);
         mEnd.setTime(mSchedule.endHour, mSchedule.endMinute);
-        mZenMode.setSelectedValue(mRule.zenMode);
-        mDisableListeners = false;
         updateEndSummary();
-        if (mSwitchBar != null) {
-            mSwitchBar.setChecked(mRule.enabled);
-        }
     }
 
     @Override
@@ -337,34 +183,6 @@
         return MetricsLogger.NOTIFICATION_ZEN_MODE_SCHEDULE_RULE;
     }
 
-    private void showDeleteRuleDialog() {
-        new AlertDialog.Builder(mContext)
-                .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, mRule.name))
-                .setNegativeButton(R.string.cancel, null)
-                .setPositiveButton(R.string.zen_mode_delete_rule_button, new OnClickListener() {
-                    @Override
-                    public void onClick(DialogInterface dialog, int which) {
-                        mDeleting = true;
-                        mConfig.automaticRules.remove(mRuleId);
-                        setZenModeConfig(mConfig);
-                    }
-                })
-                .show();
-    }
-
-    private void showRuleNameDialog() {
-        new ZenRuleNameDialog(mContext, mRule.name, mConfig.getAutomaticRuleNames()) {
-            @Override
-            public void onOk(String ruleName) {
-                final ZenModeConfig newConfig = mConfig.copy();
-                final ZenRule rule = newConfig.automaticRules.get(mRuleId);
-                if (rule == null) return;
-                rule.name = ruleName;
-                setZenModeConfig(newConfig);
-            }
-        }.show();
-    }
-
     private void showDaysDialog() {
         new AlertDialog.Builder(mContext)
                 .setTitle(R.string.zen_mode_schedule_rule_days)
@@ -375,10 +193,7 @@
                           if (Arrays.equals(days, mSchedule.days)) return;
                           if (DEBUG) Log.d(TAG, "days.onChanged days=" + Arrays.asList(days));
                           mSchedule.days = days;
-                          mRule.conditionId = ZenModeConfig.toScheduleConditionId(mSchedule);
-                          mRule.condition = null;
-                          mRule.snoozing = false;
-                          setZenModeConfig(mConfig);
+                          updateRule(ZenModeConfig.toScheduleConditionId(mSchedule));
                       }
                 })
                 .setOnDismissListener(new OnDismissListener() {
@@ -391,14 +206,6 @@
                 .show();
     }
 
-    private void toastAndFinish() {
-        if (!mDeleting) {
-            Toast.makeText(mContext, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT)
-                    .show();
-        }
-        getActivity().finish();
-    }
-
     private static class TimePickerPreference extends Preference {
         private final Context mContext;
 
@@ -474,4 +281,5 @@
             boolean onSetTime(int hour, int minute);
         }
     }
+
 }
diff --git a/src/com/android/settings/notification/ZenRuleNameDialog.java b/src/com/android/settings/notification/ZenRuleNameDialog.java
index b0eaaec..8b44e46 100644
--- a/src/com/android/settings/notification/ZenRuleNameDialog.java
+++ b/src/com/android/settings/notification/ZenRuleNameDialog.java
@@ -17,35 +17,71 @@
 package com.android.settings.notification;
 
 import android.app.AlertDialog;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.ArraySet;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.EditText;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
 
 import com.android.settings.R;
 
+import java.util.List;
+
 public abstract class ZenRuleNameDialog {
+    private static final String TAG = ZenModeSettings.TAG;
+    private static final boolean DEBUG = ZenModeSettings.DEBUG;
+
     private final AlertDialog mDialog;
     private final EditText mEditText;
+    private final RadioGroup mTypes;
     private final ArraySet<String> mExistingNames;
+    private final ServiceListing mServiceListing;
+    private final RuleInfo[] mExternalRules = new RuleInfo[3];
 
-    public ZenRuleNameDialog(Context context, String ruleName, ArraySet<String> existingNames) {
+    public ZenRuleNameDialog(Context context, ServiceListing serviceListing, String ruleName,
+            ArraySet<String> existingNames) {
+        mServiceListing = serviceListing;
         final View v = LayoutInflater.from(context).inflate(R.layout.zen_rule_name, null, false);
         mEditText = (EditText) v.findViewById(R.id.rule_name);
-        mEditText.setText(ruleName);
+        if (ruleName != null) {
+            mEditText.setText(ruleName);
+        }
         mEditText.setSelectAllOnFocus(true);
+        mTypes = (RadioGroup) v.findViewById(R.id.rule_types);
+        if (mServiceListing != null) {
+            bindType(R.id.rule_type_schedule, defaultNewSchedule());
+            bindExternalRules();
+            mServiceListing.addCallback(mServiceListingCallback);
+            mServiceListing.reload();
+        }
         mDialog = new AlertDialog.Builder(context)
                 .setTitle(R.string.zen_mode_rule_name)
                 .setView(v)
                 .setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
-                        onOk(trimmedText());
+                        onOk(trimmedText(), selectedRuleInfo());
+                    }
+                })
+                .setOnDismissListener(new OnDismissListener() {
+                    @Override
+                    public void onDismiss(DialogInterface dialog) {
+                        if (mServiceListing != null) {
+                            mServiceListing.removeCallback(mServiceListingCallback);
+                        }
                     }
                 })
                 .setNegativeButton(R.string.cancel, null)
@@ -72,17 +108,37 @@
         }
     }
 
-    abstract public void onOk(String ruleName);
-
-    private String trimmedText() {
-        return mEditText.getText() == null ? null : mEditText.getText().toString().trim();
-    }
+    abstract public void onOk(String ruleName, RuleInfo ruleInfo);
 
     public void show() {
         mDialog.show();
         updatePositiveButton();
     }
 
+    private void bindType(int id, RuleInfo ri) {
+        final RadioButton rb = (RadioButton) mTypes.findViewById(id);
+        if (ri == null) {
+            rb.setVisibility(View.GONE);
+            return;
+        }
+        rb.setVisibility(View.VISIBLE);
+        if (ri.caption != null) {
+            rb.setText(ri.caption);
+        }
+        rb.setTag(ri);
+    }
+
+    private RuleInfo selectedRuleInfo() {
+        final int id = mTypes.getCheckedRadioButtonId();
+        if (id == -1) return null;
+        final RadioButton rb = (RadioButton) mTypes.findViewById(id);
+        return (RuleInfo) rb.getTag();
+    }
+
+    private String trimmedText() {
+        return mEditText.getText() == null ? null : mEditText.getText().toString().trim();
+    }
+
     private void updatePositiveButton() {
         final String name = trimmedText();
         final boolean validName = !TextUtils.isEmpty(name)
@@ -90,4 +146,51 @@
         mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validName);
     }
 
+    private static RuleInfo defaultNewSchedule() {
+        final ScheduleInfo schedule = new ScheduleInfo();
+        schedule.days = ZenModeConfig.ALL_DAYS;
+        schedule.startHour = 22;
+        schedule.endHour = 7;
+        final RuleInfo rt = new RuleInfo();
+        rt.settingsAction = ZenModeScheduleRuleSettings.ACTION;
+        rt.defaultConditionId = ZenModeConfig.toScheduleConditionId(schedule);
+        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);
+    }
+
+    private final ServiceListing.Callback mServiceListingCallback = new ServiceListing.Callback() {
+        @Override
+        public void onServicesReloaded(List<ServiceInfo> services) {
+            if (DEBUG) Log.d(TAG, "Services reloaded: count=" + services.size());
+            mExternalRules[0] = mExternalRules[1] = mExternalRules[2] = null;
+            int i = 0;
+            for (ServiceInfo si : services) {
+                final RuleInfo ri = ZenModeExternalRuleSettings.getRuleInfo(si);
+                if (ri != null) {
+                    mExternalRules[i] = ri;
+                    i++;
+                    if (i == mExternalRules.length) {
+                        break;
+                    }
+                }
+            }
+            bindExternalRules();
+        }
+    };
+
+    public static class RuleInfo {
+        public String caption;
+        public String settingsAction;
+        public Uri defaultConditionId;
+        public ComponentName serviceComponent;
+        public ComponentName configurationActivity;
+    }
+
 }
\ No newline at end of file