Merge "Correct logic for showing overide dnd field." into nyc-mr1-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e633741..3a3f567 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2921,7 +2921,7 @@
         <activity android:name="Settings$GestureSettingsActivity"
                   android:label="@string/gesture_preference_title"
                   android:icon="@drawable/ic_settings_gestures"
-                  android:taskAffinity="">
+                  android:enabled="@bool/config_gesture_settings_enabled">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.android.settings.SHORTCUT" />
diff --git a/res/drawable/ic_check_green_24dp.xml b/res/drawable/ic_check_green_24dp.xml
new file mode 100644
index 0000000..c836a02
--- /dev/null
+++ b/res/drawable/ic_check_green_24dp.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2016 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="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"
+        android:fillColor="#0F9D58"/>
+</vector>
diff --git a/res/drawable/ic_cross_grey_24dp.xml b/res/drawable/ic_cross_grey_24dp.xml
new file mode 100644
index 0000000..312c034
--- /dev/null
+++ b/res/drawable/ic_cross_grey_24dp.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2016 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="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"
+        android:fillColor="#757575"/>
+</vector>
diff --git a/res/layout/choose_lock_password.xml b/res/layout/choose_lock_password.xml
index 99657b4..4b9f3e5 100644
--- a/res/layout/choose_lock_password.xml
+++ b/res/layout/choose_lock_password.xml
@@ -14,7 +14,6 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-
 <com.android.setupwizardlib.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:settings="http://schemas.android.com/apk/res-auto"
@@ -31,9 +30,11 @@
         android:orientation="vertical">
 
         <!-- header text ('Enter Pin') -->
-        <TextView android:id="@+id/headerText"
+        <TextView
+            android:id="@+id/headerText"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:accessibilityLiveRegion="polite"
             android:gravity="center"
             android:lines="2"
             android:textAppearance="?android:attr/textAppearanceMedium"/>
@@ -50,48 +51,44 @@
             style="@style/TextAppearance.PasswordEntry"/>
 
         <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:clipChildren="false"
-            android:clipToPadding="false"
-            android:gravity="end"
-            android:orientation="horizontal">
+                android:id="@+id/bottom_container"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:orientation="vertical">
 
-            <!-- left : cancel -->
-            <Button android:id="@+id/cancel_button"
-                style="@style/SetupWizardButton.Negative"
-                android:layout_width="wrap_content"
+            <android.support.v7.widget.RecyclerView
+                    android:id="@+id/password_requirements_view"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"/>
+
+            <LinearLayout
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/lockpassword_cancel_label" />
+                android:clipChildren="false"
+                android:clipToPadding="false"
+                android:gravity="end"
+                android:orientation="horizontal">
 
-            <Space
-                android:layout_width="0dp"
-                android:layout_height="0dp"
-                android:layout_weight="1" />
+                <!-- left : cancel -->
+                <Button android:id="@+id/cancel_button"
+                    style="@style/SetupWizardButton.Negative"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/lockpassword_cancel_label" />
 
-            <!-- right : continue -->
-            <Button android:id="@+id/next_button"
-                style="@style/SetupWizardButton.Positive"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/lockpassword_continue_label" />
+                <Space
+                    android:layout_width="0dp"
+                    android:layout_height="0dp"
+                    android:layout_weight="1" />
 
+                <!-- right : continue -->
+                <Button android:id="@+id/next_button"
+                    style="@style/SetupWizardButton.Positive"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/lockpassword_continue_label" />
+            </LinearLayout>
         </LinearLayout>
-
-        <!-- 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_width="match_parent"
-            android:layout_height="wrap_content"
-            android:background="#00000000"
-            android:keyBackground="@*android:drawable/btn_keyboard_key_fulltrans"
-            android:visibility="gone"/>
-
     </LinearLayout>
 
 </com.android.setupwizardlib.GlifLayout>
diff --git a/res/layout/fingerprint_enroll_introduction.xml b/res/layout/fingerprint_enroll_introduction.xml
index 6d1c699..2c0e7de 100644
--- a/res/layout/fingerprint_enroll_introduction.xml
+++ b/res/layout/fingerprint_enroll_introduction.xml
@@ -15,10 +15,51 @@
     limitations under the License
 -->
 
-<com.android.setupwizardlib.GlifRecyclerLayout
+<com.android.setupwizardlib.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     style="?attr/fingerprint_layout_theme"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:entries="@xml/fingerprint_enroll_introduction_items" />
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        style="@style/SuwContentFrame"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <com.android.setupwizardlib.view.RichTextView
+            style="@style/SuwDescription.Glif"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/security_settings_fingerprint_enroll_introduction_message" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/fingerprint_cancel_button"
+                style="@style/SetupWizardButton.Negative"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/security_settings_fingerprint_enroll_introduction_cancel" />
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <Button
+                android:id="@+id/fingerprint_next_button"
+                style="@style/SetupWizardButton.Positive"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/suw_next_button_label" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</com.android.setupwizardlib.GlifLayout>
diff --git a/res/layout/password_requirement_item.xml b/res/layout/password_requirement_item.xml
new file mode 100644
index 0000000..df7f45c
--- /dev/null
+++ b/res/layout/password_requirement_item.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/description_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingStart="4dp"
+        android:textSize="14sp"/>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index 3491922..c8ea171 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -52,4 +52,7 @@
     <string name="gesture_double_twist_sensor_name" translatable="false"></string>
     <string name="gesture_double_twist_sensor_vendor" translatable="false"></string>
 
+    <!-- When true enable gesture setting. -->
+    <bool name="config_gesture_settings_enabled">false</bool>
+
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b2d0570..d65bdfb 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -305,4 +305,9 @@
     <dimen name="support_tile_min_height">48dp</dimen>
     <!-- support spacer layout height -->
     <dimen name="support_spacer_height">8dp</dimen>
+
+    <dimen name="password_requirement_textsize">14sp</dimen>
+    <!-- Visible vertical space we want to show below password edittext field when ime is shown.
+         The unit is sp as it is related to the text size of password requirement item. -->
+    <dimen name="visible_vertical_space_below_password">20sp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c6b278c..b8b4273 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1166,7 +1166,7 @@
     <string name="lock_profile_wipe_dismiss">Dismiss</string>
 
     <!-- Hint shown in dialog screen when password is too short -->
-    <string name="lockpassword_password_too_short">Password must be at least %d characters</string>
+    <string name="lockpassword_password_too_short">Must be at least %d characters</string>
     <!-- Hint shown in dialog screen when PIN is too short -->
     <string name="lockpassword_pin_too_short">PIN must be at least %d digits</string>
 
@@ -1174,62 +1174,62 @@
     <string name="lockpassword_continue_label">Continue</string>
 
     <!-- Error shown in popup when password is too long -->
-    <string name="lockpassword_password_too_long">Password must be fewer than <xliff:g id="number" example="17">%d</xliff:g> characters.</string>
+    <string name="lockpassword_password_too_long">Must be fewer than <xliff:g id="number" example="17">%d</xliff:g> characters.</string>
     <!-- Error shown in popup when PIN is too long -->
-    <string name="lockpassword_pin_too_long">PIN must be fewer than <xliff:g id="number" example="17">%d</xliff:g> digits.</string>
+    <string name="lockpassword_pin_too_long">Must be fewer than <xliff:g id="number" example="17">%d</xliff:g> digits.</string>
 
     <!-- Error shown when in PIN mode and user enters a non-digit -->
-    <string name="lockpassword_pin_contains_non_digits">PIN must contain only digits 0-9.</string>
+    <string name="lockpassword_pin_contains_non_digits">Must contain only digits 0-9.</string>
 
     <!-- Error shown when in PIN mode and PIN has been used recently. Please keep this string short! -->
     <string name="lockpassword_pin_recently_used">Device administrator doesn\u2019t allow using a recent PIN.</string>
 
     <!-- Error shown when in PASSWORD mode and user enters an invalid character -->
-    <string name="lockpassword_illegal_character">Password contains an illegal character.</string>
+    <string name="lockpassword_illegal_character">This can\'t include an invalid character</string>
 
     <!-- Error shown when in PASSWORD mode and password is all digits -->
-    <string name="lockpassword_password_requires_alpha">Password must contain at least one letter.</string>
+    <string name="lockpassword_password_requires_alpha">Must contain at least one letter</string>
 
     <!-- Error shown when in PASSWORD mode and password doesn't contain any digits -->
-    <string name="lockpassword_password_requires_digit">Password must contain at least one digit.</string>
+    <string name="lockpassword_password_requires_digit">Must contain at least one digit</string>
 
     <!-- Error shown when in PASSWORD mode and password doesn't contain any symbols -->
-    <string name="lockpassword_password_requires_symbol">Password must contain at least one symbol.</string>
+    <string name="lockpassword_password_requires_symbol">Must contain at least one symbol</string>
 
     <!-- Error shown when in PASSWORD mode and password doesn't contain the required number of letters -->
     <plurals name="lockpassword_password_requires_letters">
-        <item quantity="one">Password must contain at least 1 letter.</item>
-        <item quantity="other">Password must contain at least %d letters.</item>
+        <item quantity="one">Must contain at least 1 letter</item>
+        <item quantity="other">Must contain at least %d letters</item>
     </plurals>
 
     <!-- Error shown when in PASSWORD mode and password doesn't contain the required number of lowercase letters -->
     <plurals name="lockpassword_password_requires_lowercase">
-        <item quantity="one">Password must contain at least 1 lowercase letter.</item>
-        <item quantity="other">Password must contain at least %d lowercase letters.</item>
+        <item quantity="one">Must contain at least 1 lowercase letter</item>
+        <item quantity="other">Must contain at least %d lowercase letters</item>
     </plurals>
 
     <!-- Error shown when in PASSWORD mode and password doesn't contain the required number of uppercase letters -->
     <plurals name="lockpassword_password_requires_uppercase">
-        <item quantity="one">Password must contain at least 1 uppercase letter.</item>
-        <item quantity="other">Password must contain at least %d uppercase letters.</item>
+        <item quantity="one">Must contain at least 1 uppercase letter</item>
+        <item quantity="other">Must contain at least %d uppercase letters</item>
     </plurals>
 
     <!-- Error shown when in PASSWORD mode and password doesn't contain the required number of numerical digits -->
     <plurals name="lockpassword_password_requires_numeric">
-        <item quantity="one">Password must contain at least 1 numerical digit.</item>
-        <item quantity="other">Password must contain at least %d numerical digits.</item>
+        <item quantity="one">Must contain at least 1 numerical digit</item>
+        <item quantity="other">Must contain at least %d numerical digits</item>
     </plurals>
 
     <!-- Error shown when in PASSWORD mode and password doesn't contain the required number of special symbols -->
     <plurals name="lockpassword_password_requires_symbols">
-        <item quantity="one">Password must contain at least 1 special symbol.</item>
-        <item quantity="other">Password must contain at least %d special symbols.</item>
+        <item quantity="one">Must contain at least 1 special symbol</item>
+        <item quantity="other">Must contain at least %d special symbols</item>
     </plurals>
 
     <!-- Error shown when in PASSWORD mode and password doesn't contain the required number of non-letter characters -->
     <plurals name="lockpassword_password_requires_nonletter">
-        <item quantity="one">Password must contain at least 1 non-letter character.</item>
-        <item quantity="other">Password must contain at least %d non-letter characters.</item>
+        <item quantity="one">Must contain at least 1 non-letter character</item>
+        <item quantity="other">Must contain at least %d non-letter characters</item>
     </plurals>
 
     <!-- Error shown when in PASSWORD mode and password has been used recently. Please keep this string short! -->
@@ -3231,19 +3231,19 @@
     <!-- Security & location settings screen, title when changing or confirming the work profile lock -->
     <string name="lock_settings_profile_screen_lock_title">Work profile screen lock</string>
     <!-- Security & location settings screen, setting option name to unify work and personal locks -->
-    <string name="lock_settings_profile_unification_title">Use the same lock</string>
+    <string name="lock_settings_profile_unification_title">Use one lock</string>
     <!-- Security & location settings screen, setting option explanation to unify work and personal locks -->
-    <string name="lock_settings_profile_unification_summary">Change device screen lock to match work profile</string>
+    <string name="lock_settings_profile_unification_summary">Use one lock for work profile and device screen</string>
     <!-- Security & location settings screen, title of the dialog asking if the user wants to unify work and personal locks -->
-    <string name="lock_settings_profile_unification_dialog_title">Use the same lock?</string>
+    <string name="lock_settings_profile_unification_dialog_title">Use one lock?</string>
     <!-- Security & location settings screen, explanation in the dialog asking if the user wants to unify work and personal locks -->
-    <string name="lock_settings_profile_unification_dialog_body">You can use your work profile lock for your device screen lock as well. If you do, any work lock policies will also apply to your device screen lock.</string>
+    <string name="lock_settings_profile_unification_dialog_body">Your work profile and device screen will use the same lock. Any work lock policies will also apply to your device screen lock.</string>
     <!-- Security & location settings screen, explanation in the dialog asking if the user wants to create a new lock for personal and work as the current work lock is not enough for the device. -->
-    <string name="lock_settings_profile_unification_dialog_uncompliant_body">Your work profile lock doesn\'t meet your organization\'s security requirements.\n\nYou can set a new screen lock for both your device and your work profile, but any work lock policies will apply to your device screen lock as well.</string>
+    <string name="lock_settings_profile_unification_dialog_uncompliant_body">Your work profile lock doesn\'t meet your organization\'s security requirements. You can use the same lock for your device screen and your work profile, but any work lock policies will apply.</string>
     <!-- Security & location settings screen, confirmation button of the dialog for user with compliant lock, asking to create a new lock for both personal and work profiles. -->
-    <string name="lock_settings_profile_unification_dialog_confirm">Change device lock</string>
+    <string name="lock_settings_profile_unification_dialog_confirm">Use one lock</string>
     <!-- Security & location settings screen, confirmation button of the dialog for user with uncompliant lock, asking to create a new lock for both personal and work profiles. -->
-    <string name="lock_settings_profile_unification_dialog_uncompliant_confirm">Change lock</string>
+    <string name="lock_settings_profile_unification_dialog_uncompliant_confirm">Use one lock</string>
     <!-- Security & location settings screen, summary of the item that changes your work profile lock when it is unified with the personal lock -->
     <string name="lock_settings_profile_unified_summary">Same as device screen lock</string>
 
@@ -7499,13 +7499,10 @@
     <string name="button_confirm_convert_fbe">Wipe and convert</string>
 
     <!-- Reset rate-limiting in the system service ShortcutManager. [CHAR_LIMIT=none] -->
-    <string name="reset_shortcut_manager_throttling">Reset ShortcutManager rate-limiting counters</string>
+    <string name="reset_shortcut_manager_throttling">Reset ShortcutManager rate-limiting</string>
 
-    <!-- Title of the dialog box to confirm resetting rate-limiting in the system service ShortcutManager. [CHAR_LIMIT=none] -->
-    <string name="confirm_reset_shortcut_manager_throttling_title">Reset ShortcutManager rate-limiting?</string>
-
-    <!-- Message of the dialog box to confirm resetting rate-limiting in the system service ShortcutManager. [CHAR_LIMIT=none] -->
-    <string name="confirm_reset_shortcut_manager_throttling_message">Reset ShortcutManager rate-limiting counters?</string>
+    <!-- Toast message shown when "Reset ShortcutManager rate-limiting" has been performed. [CHAR_LIMIT=none] -->
+    <string name="reset_shortcut_manager_throttling_complete">ShortcutManager rate-limiting has been reset</string>
 
     <!-- Title of notification suggestion during optional steps of setup. [CHAR_LIMIT=60] -->
     <string name="notification_suggestion_title">Control lock screen notifications</string>
diff --git a/res/xml/development_prefs.xml b/res/xml/development_prefs.xml
index 29363ad..2288520 100644
--- a/res/xml/development_prefs.xml
+++ b/res/xml/development_prefs.xml
@@ -372,6 +372,10 @@
             android:key="force_resizable_activities"
             android:title="@string/force_resizable_activities"
             android:summary="@string/force_resizable_activities_summary"/>
+
+        <Preference
+            android:key="reset_shortcut_manager_throttling"
+            android:title="@string/reset_shortcut_manager_throttling" />
      </PreferenceCategory>
 
 </PreferenceScreen>
diff --git a/res/xml/fingerprint_enroll_introduction_items.xml b/res/xml/fingerprint_enroll_introduction_items.xml
deleted file mode 100644
index 28a3f6b..0000000
--- a/res/xml/fingerprint_enroll_introduction_items.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2016 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.
--->
-
-<ItemGroup xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <Item
-        android:enabled="false"
-        android:id="@+id/fingerprint_introduction_message"
-        android:layout="@layout/suw_items_description" />
-
-    <Item
-        android:id="@+id/next_button"
-        android:icon="@drawable/ic_fingerprint_list_icon"
-        android:title="@string/security_settings_fingerprint_enroll_introduction_continue" />
-
-    <Item
-        android:id="@+id/cancel_button"
-        android:icon="@drawable/ic_skip"
-        android:title="@string/security_settings_fingerprint_enroll_introduction_cancel" />
-
-</ItemGroup>
\ No newline at end of file
diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java
index 5a8960c..5519e87 100644
--- a/src/com/android/settings/ChooseLockPassword.java
+++ b/src/com/android/settings/ChooseLockPassword.java
@@ -21,10 +21,11 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.drawable.InsetDrawable;
 import android.inputmethodservice.KeyboardView;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.Selection;
@@ -32,13 +33,15 @@
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.Log;
+import android.view.inputmethod.EditorInfo;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
+import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.Button;
+import android.widget.EditText;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
@@ -50,8 +53,18 @@
 import com.android.internal.widget.PasswordEntryKeyboardView;
 import com.android.internal.widget.TextViewInputDisabler;
 import com.android.settings.notification.RedactionInterstitial;
+import com.android.settings.password.PasswordRequirementAdapter;
 import com.android.setupwizardlib.GlifLayout;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
+
 public class ChooseLockPassword extends SettingsActivity {
     public static final String PASSWORD_MIN_KEY = "lockscreen.password_min";
     public static final String PASSWORD_MAX_KEY = "lockscreen.password_max";
@@ -137,10 +150,6 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        // TODO: Fix on phones
-        // 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_choose_your_password_header);
         setTitle(msg);
@@ -149,7 +158,7 @@
     }
 
     public static class ChooseLockPasswordFragment extends InstrumentedFragment
-            implements OnClickListener, OnEditorActionListener,  TextWatcher,
+            implements OnClickListener, OnEditorActionListener, TextWatcher,
             SaveAndFinishWorker.Listener {
         private static final String KEY_FIRST_PIN = "first_pin";
         private static final String KEY_UI_STAGE = "ui_stage";
@@ -160,7 +169,7 @@
         private String mChosenPassword;
         private boolean mHasChallenge;
         private long mChallenge;
-        private TextView mPasswordEntry;
+        private EditText mPasswordEntry;
         private TextViewInputDisabler mPasswordEntryInputDisabler;
         private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
         private int mPasswordMaxLength = 16;
@@ -170,35 +179,51 @@
         private int mPasswordMinSymbols = 0;
         private int mPasswordMinNumeric = 0;
         private int mPasswordMinNonLetter = 0;
+        private int mUserId;
+        private boolean mHideDrawer = false;
+        /**
+         * Password requirements that we need to verify.
+         */
+        private int[] mPasswordRequirements;
+
         private LockPatternUtils mLockPatternUtils;
         private SaveAndFinishWorker mSaveAndFinishWorker;
         private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
         private ChooseLockSettingsHelper mChooseLockSettingsHelper;
         private Stage mUiStage = Stage.Introduction;
+        private PasswordRequirementAdapter mPasswordRequirementAdapter;
 
         private TextView mHeaderText;
         private String mFirstPin;
-        private KeyboardView mKeyboardView;
-        private PasswordEntryKeyboardHelper mKeyboardHelper;
+        private RecyclerView mPasswordRestrictionView;
         private boolean mIsAlphaMode;
         private Button mCancelButton;
         private Button mNextButton;
+
         private static final int CONFIRM_EXISTING_REQUEST = 58;
         static final int RESULT_FINISHED = RESULT_FIRST_USER;
-        private static final long ERROR_MESSAGE_TIMEOUT = 3000;
-        private static final int MSG_SHOW_ERROR = 1;
 
-        private int mUserId;
-        private boolean mHideDrawer = false;
+        private static final int MIN_LETTER_IN_PASSWORD = 0;
+        private static final int MIN_UPPER_LETTERS_IN_PASSWORD = 1;
+        private static final int MIN_LOWER_LETTERS_IN_PASSWORD = 2;
+        private static final int MIN_SYMBOLS_IN_PASSWORD = 3;
+        private static final int MIN_NUMBER_IN_PASSWORD = 4;
+        private static final int MIN_NON_LETTER_IN_PASSWORD = 5;
 
-        private Handler mHandler = new Handler() {
-            @Override
-            public void handleMessage(Message msg) {
-                if (msg.what == MSG_SHOW_ERROR) {
-                    updateStage((Stage) msg.obj);
-                }
-            }
-        };
+        // Error code returned from {@link #validatePassword(String)}.
+        private static final int NO_ERROR = 0;
+        private static final int CONTAIN_INVALID_CHARACTERS = 1 << 0;
+        private static final int TOO_SHORT = 1 << 1;
+        private static final int TOO_LONG = 1 << 2;
+        private static final int CONTAIN_NON_DIGITS = 1 << 3;
+        private static final int CONTAIN_SEQUENTIAL_DIGITS = 1 << 4;
+        private static final int RECENTLY_USED = 1 << 5;
+        private static final int NOT_ENOUGH_LETTER = 1 << 6;
+        private static final int NOT_ENOUGH_UPPER_CASE = 1 << 7;
+        private static final int NOT_ENOUGH_LOWER_CASE = 1 << 8;
+        private static final int NOT_ENOUGH_DIGITS = 1 << 9;
+        private static final int NOT_ENOUGH_SYMBOLS = 1 << 10;
+        private static final int NOT_ENOUGH_NON_LETTER = 1 << 11;
 
         /**
          * Keep track internally of where the user is in choosing a pattern.
@@ -243,33 +268,7 @@
             }
             // Only take this argument into account if it belongs to the current profile.
             mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras());
-            mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
-                    mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality(
-                    mUserId));
-            mPasswordMinLength = Math.max(Math.max(
-                    LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
-                    intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength)),
-                    mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId));
-            mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
-            mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY,
-                    mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters(
-                    mUserId));
-            mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
-                    mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase(
-                    mUserId));
-            mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
-                    mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase(
-                    mUserId));
-            mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
-                    mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric(
-                    mUserId));
-            mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
-                    mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols(
-                    mUserId));
-            mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
-                    mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter(
-                    mUserId));
-
+            processPasswordRequirements(intent);
             mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
             mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false);
 
@@ -290,10 +289,7 @@
         @Override
         public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
-            GlifLayout layout = (GlifLayout) inflater.inflate(
-                    R.layout.choose_lock_password, container, false);
-            layout.setHeaderText(getActivity().getTitle());
-            return layout;
+            return inflater.inflate(R.layout.choose_lock_password, container, false);
         }
 
         @Override
@@ -308,21 +304,18 @@
             mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
                     || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality
                     || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
-            mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
-            mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
+
+            setupPasswordRequirementsView(view);
+
+            mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
+            mPasswordEntry = (EditText) view.findViewById(R.id.password_entry);
             mPasswordEntry.setOnEditorActionListener(this);
             mPasswordEntry.addTextChangedListener(this);
+            mPasswordEntry.requestFocus();
             mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
 
             final Activity activity = getActivity();
-            mKeyboardHelper = new PasswordEntryKeyboardHelper(activity,
-                    mKeyboardView, mPasswordEntry);
-            mKeyboardHelper.setKeyboardMode(mIsAlphaMode ?
-                    PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
-                    : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
-
             mHeaderText = (TextView) view.findViewById(R.id.headerText);
-            mKeyboardView.requestFocus();
 
             int currentType = mPasswordEntry.getInputType();
             mPasswordEntry.setInputType(mIsAlphaMode ? currentType
@@ -359,15 +352,83 @@
                 mSaveAndFinishWorker = (SaveAndFinishWorker) getFragmentManager().findFragmentByTag(
                         FRAGMENT_TAG_SAVE_AND_FINISH);
             }
+
+            // Workaround to show one password requirement below EditText when IME is shown.
+            // By adding an inset to the edit text background, we make the EditText occupy more
+            // vertical space, and the keyboard will then avoid hiding it. We have also set
+            // negative margin in the layout below in order to have them show in the correct
+            // position.
+            final int visibleVerticalSpaceBelowPassword =
+                    getResources().getDimensionPixelOffset(
+                        R.dimen.visible_vertical_space_below_password);
+            InsetDrawable drawable =
+                    new InsetDrawable(
+                    mPasswordEntry.getBackground(), 0, 0, 0, visibleVerticalSpaceBelowPassword);
+            mPasswordEntry.setBackgroundDrawable(drawable);
+            LinearLayout bottomContainer = (LinearLayout) view.findViewById(R.id.bottom_container);
+            LinearLayout.LayoutParams bottomContainerLp =
+                    (LinearLayout.LayoutParams) bottomContainer.getLayoutParams();
+            bottomContainerLp.setMargins(0, -visibleVerticalSpaceBelowPassword, 0, 0);
+
             if (activity instanceof SettingsActivity) {
                 final SettingsActivity sa = (SettingsActivity) activity;
                 int id = mIsAlphaMode ? R.string.lockpassword_choose_your_password_header
                         : R.string.lockpassword_choose_your_pin_header;
                 CharSequence title = getText(id);
                 sa.setTitle(title);
+                ((GlifLayout) view).setHeaderText(title);
             }
         }
 
+        private void setupPasswordRequirementsView(View view) {
+            // Construct passwordRequirements and requirementDescriptions.
+            List<Integer> passwordRequirements = new ArrayList<>();
+            List<String> requirementDescriptions = new ArrayList<>();
+            if (mPasswordMinUpperCase > 0) {
+                passwordRequirements.add(MIN_UPPER_LETTERS_IN_PASSWORD);
+                requirementDescriptions.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase,
+                        mPasswordMinUpperCase));
+            }
+            if (mPasswordMinLowerCase > 0) {
+                passwordRequirements.add(MIN_LOWER_LETTERS_IN_PASSWORD);
+                requirementDescriptions.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase,
+                        mPasswordMinLowerCase));
+            }
+            if (mPasswordMinLetters > 0) {
+                passwordRequirements.add(MIN_LETTER_IN_PASSWORD);
+                requirementDescriptions.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters,
+                        mPasswordMinLetters));
+            }
+            if (mPasswordMinNumeric > 0) {
+                passwordRequirements.add(MIN_NUMBER_IN_PASSWORD);
+                requirementDescriptions.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric,
+                        mPasswordMinNumeric));
+            }
+            if (mPasswordMinSymbols > 0) {
+                passwordRequirements.add(MIN_SYMBOLS_IN_PASSWORD);
+                requirementDescriptions.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols,
+                        mPasswordMinSymbols));
+            }
+            if (mPasswordMinNonLetter > 0) {
+                passwordRequirements.add(MIN_NON_LETTER_IN_PASSWORD);
+                requirementDescriptions.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter,
+                        mPasswordMinNonLetter));
+            }
+            // Convert list to array.
+            mPasswordRequirements = passwordRequirements.stream().mapToInt(i -> i).toArray();
+            mPasswordRestrictionView =
+                    (RecyclerView) view.findViewById(R.id.password_requirements_view);
+            mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity()));
+            mPasswordRequirementAdapter = new PasswordRequirementAdapter();
+            mPasswordRestrictionView.setAdapter(mPasswordRequirementAdapter);
+        }
+
         @Override
         protected int getMetricsCategory() {
             return MetricsEvent.CHOOSE_LOCK_PASSWORD;
@@ -380,17 +441,15 @@
             if (mSaveAndFinishWorker != null) {
                 mSaveAndFinishWorker.setListener(this);
             } else {
-                mKeyboardView.requestFocus();
+                mPasswordEntry.requestFocus();
             }
         }
 
         @Override
         public void onPause() {
-            mHandler.removeMessages(MSG_SHOW_ERROR);
             if (mSaveAndFinishWorker != null) {
                 mSaveAndFinishWorker.setListener(null);
             }
-
             super.onPause();
         }
 
@@ -436,21 +495,95 @@
         }
 
         /**
-         * Validates PIN and returns a message to display if PIN fails test.
-         * @param password the raw password the user typed in
-         * @return error message to show to user or null if password is OK
+         * Read the requirements from {@link DevicePolicyManager} and intent and aggregate them.
+         *
+         * @param intent the incoming intent
          */
-        private String validatePassword(String password) {
+        private void processPasswordRequirements(Intent intent) {
+            final int dpmPasswordQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId);
+            mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
+                    mRequestedQuality), dpmPasswordQuality);
+            mPasswordMinLength = Math.max(Math.max(
+                    LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
+                    intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength)),
+                    mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId));
+            mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
+            mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY,
+                    mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters(
+                    mUserId));
+            mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
+                    mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase(
+                    mUserId));
+            mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
+                    mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase(
+                    mUserId));
+            mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
+                    mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric(
+                    mUserId));
+            mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
+                    mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols(
+                    mUserId));
+            mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
+                    mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter(
+                    mUserId));
+
+            // Modify the value based on dpm policy.
+            switch (dpmPasswordQuality) {
+                case PASSWORD_QUALITY_ALPHABETIC:
+                    if (mPasswordMinLetters == 0) {
+                        mPasswordMinLetters = 1;
+                    }
+                    break;
+                case PASSWORD_QUALITY_ALPHANUMERIC:
+                    if (mPasswordMinLetters == 0) {
+                        mPasswordMinLetters = 1;
+                    }
+                    if (mPasswordMinNumeric == 0) {
+                        mPasswordMinNumeric = 1;
+                    }
+                    break;
+                case PASSWORD_QUALITY_COMPLEX:
+                    // Reserve all the requirements.
+                    break;
+                default:
+                    mPasswordMinNumeric = 0;
+                    mPasswordMinLetters = 0;
+                    mPasswordMinUpperCase = 0;
+                    mPasswordMinLowerCase = 0;
+                    mPasswordMinSymbols = 0;
+                    mPasswordMinNonLetter = 0;
+            }
+        }
+
+        /**
+         * Validates PIN and returns the validation result.
+         *
+         * @param password the raw password the user typed in
+         * @return the validation result.
+         */
+        private int validatePassword(String password) {
+            int errorCode = NO_ERROR;
+
             if (password.length() < mPasswordMinLength) {
-                return getString(mIsAlphaMode ?
-                        R.string.lockpassword_password_too_short
-                        : R.string.lockpassword_pin_too_short, mPasswordMinLength);
+                errorCode |= TOO_SHORT;
+            } else if (password.length() > mPasswordMaxLength) {
+                errorCode |= TOO_LONG;
+            } else {
+                // The length requirements are fulfilled.
+                if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
+                    // Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
+                    final int sequence = LockPatternUtils.maxLengthSequence(password);
+                    if (sequence > LockPatternUtils.MAX_ALLOWED_SEQUENCE) {
+                        errorCode |= CONTAIN_SEQUENTIAL_DIGITS;
+                    }
+                }
+                // Is the password recently used?
+                if (mLockPatternUtils.checkPasswordHistory(password, mUserId)) {
+                    errorCode |= RECENTLY_USED;
+                }
             }
-            if (password.length() > mPasswordMaxLength) {
-                return getString(mIsAlphaMode ?
-                        R.string.lockpassword_password_too_long
-                        : R.string.lockpassword_pin_too_long, mPasswordMaxLength + 1);
-            }
+
+            // Count different types of character.
             int letters = 0;
             int numbers = 0;
             int lowercase = 0;
@@ -461,7 +594,8 @@
                 char c = password.charAt(i);
                 // allow non control Latin-1 characters only
                 if (c < 32 || c > 127) {
-                    return getString(R.string.lockpassword_illegal_character);
+                    errorCode |= CONTAIN_INVALID_CHARACTERS;
+                    continue;
                 }
                 if (c >= '0' && c <= '9') {
                     numbers++;
@@ -477,63 +611,53 @@
                     nonletter++;
                 }
             }
-            if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality
-                    || DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX == mRequestedQuality) {
+
+            // Ensure no non-digits if we are requesting numbers. This shouldn't be possible unless
+            // user finds some way to bring up soft keyboard.
+            if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC
+                    || mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) {
                 if (letters > 0 || symbols > 0) {
-                    // This shouldn't be possible unless user finds some way to bring up
-                    // soft keyboard
-                    return getString(R.string.lockpassword_pin_contains_non_digits);
+                    errorCode |= CONTAIN_NON_DIGITS;
                 }
-                // Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
-                final int sequence = LockPatternUtils.maxLengthSequence(password);
-                if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX == mRequestedQuality
-                        && sequence > LockPatternUtils.MAX_ALLOWED_SEQUENCE) {
-                    return getString(R.string.lockpassword_pin_no_sequential_digits);
-                }
-            } else if (DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality) {
-                if (letters < mPasswordMinLetters) {
-                    return String.format(getResources().getQuantityString(
-                            R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters),
-                            mPasswordMinLetters);
-                } else if (numbers < mPasswordMinNumeric) {
-                    return String.format(getResources().getQuantityString(
-                            R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric),
-                            mPasswordMinNumeric);
-                } else if (lowercase < mPasswordMinLowerCase) {
-                    return String.format(getResources().getQuantityString(
-                            R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase),
-                            mPasswordMinLowerCase);
-                } else if (uppercase < mPasswordMinUpperCase) {
-                    return String.format(getResources().getQuantityString(
-                            R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase),
-                            mPasswordMinUpperCase);
-                } else if (symbols < mPasswordMinSymbols) {
-                    return String.format(getResources().getQuantityString(
-                            R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols),
-                            mPasswordMinSymbols);
-                } else if (nonletter < mPasswordMinNonLetter) {
-                    return String.format(getResources().getQuantityString(
-                            R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter),
-                            mPasswordMinNonLetter);
-                }
-            } else {
-                final boolean alphabetic = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
-                        == mRequestedQuality;
-                final boolean alphanumeric = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
-                        == mRequestedQuality;
-                if ((alphabetic || alphanumeric) && letters == 0) {
-                    return getString(R.string.lockpassword_password_requires_alpha);
-                }
-                if (alphanumeric && numbers == 0) {
-                    return getString(R.string.lockpassword_password_requires_digit);
-                }
-            }
-            if(mLockPatternUtils.checkPasswordHistory(password, mUserId)) {
-                return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used
-                        : R.string.lockpassword_pin_recently_used);
             }
 
-            return null;
+            // Check the requirements one by one.
+            for (int i = 0; i < mPasswordRequirements.length; i++) {
+                int passwordRestriction = mPasswordRequirements[i];
+                switch (passwordRestriction) {
+                    case MIN_LETTER_IN_PASSWORD:
+                        if (letters < mPasswordMinLetters) {
+                            errorCode |= NOT_ENOUGH_LETTER;
+                        }
+                        break;
+                    case MIN_UPPER_LETTERS_IN_PASSWORD:
+                        if (uppercase < mPasswordMinUpperCase) {
+                            errorCode |= NOT_ENOUGH_UPPER_CASE;
+                        }
+                        break;
+                    case MIN_LOWER_LETTERS_IN_PASSWORD:
+                        if (lowercase < mPasswordMinLowerCase) {
+                            errorCode |= NOT_ENOUGH_LOWER_CASE;
+                        }
+                        break;
+                    case MIN_SYMBOLS_IN_PASSWORD:
+                        if (symbols < mPasswordMinSymbols) {
+                            errorCode |= NOT_ENOUGH_SYMBOLS;
+                        }
+                        break;
+                    case MIN_NUMBER_IN_PASSWORD:
+                        if (numbers < mPasswordMinNumeric) {
+                            errorCode |= NOT_ENOUGH_DIGITS;
+                        }
+                        break;
+                    case MIN_NON_LETTER_IN_PASSWORD:
+                        if (nonletter < mPasswordMinNonLetter) {
+                            errorCode |= NOT_ENOUGH_NON_LETTER;
+                        }
+                        break;
+                }
+            }
+            return errorCode;
         }
 
         public void handleNext() {
@@ -542,10 +666,8 @@
             if (TextUtils.isEmpty(mChosenPassword)) {
                 return;
             }
-            String errorMsg = null;
             if (mUiStage == Stage.Introduction) {
-                errorMsg = validatePassword(mChosenPassword);
-                if (errorMsg == null) {
+                if (validatePassword(mChosenPassword) == NO_ERROR) {
                     mFirstPin = mChosenPassword;
                     mPasswordEntry.setText("");
                     updateStage(Stage.NeedToConfirm);
@@ -561,9 +683,6 @@
                     updateStage(Stage.ConfirmWrong);
                 }
             }
-            if (errorMsg != null) {
-                showError(errorMsg, mUiStage);
-            }
         }
 
         protected void setNextEnabled(boolean enabled) {
@@ -586,14 +705,6 @@
             }
         }
 
-        private void showError(String msg, final Stage next) {
-            mHeaderText.setText(msg);
-            mHeaderText.announceForAccessibility(mHeaderText.getText());
-            Message mesg = mHandler.obtainMessage(MSG_SHOW_ERROR, next);
-            mHandler.removeMessages(MSG_SHOW_ERROR);
-            mHandler.sendMessageDelayed(mesg, ERROR_MESSAGE_TIMEOUT);
-        }
-
         public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
             // Check if this was the result of hitting the enter or "done" key
             if (actionId == EditorInfo.IME_NULL
@@ -606,6 +717,68 @@
         }
 
         /**
+         * @param errorCode error code returned from {@link #validatePassword(String)}.
+         * @return an array of messages describing the error, important messages come first.
+         */
+        private String[] convertErrorCodeToMessages(int errorCode) {
+            List<String> messages = new ArrayList<>();
+            if ((errorCode & CONTAIN_INVALID_CHARACTERS) > 0) {
+                messages.add(getString(R.string.lockpassword_illegal_character));
+            }
+            if ((errorCode & CONTAIN_NON_DIGITS) > 0) {
+                messages.add(getString(R.string.lockpassword_pin_contains_non_digits));
+            }
+            if ((errorCode & NOT_ENOUGH_LETTER) > 0) {
+                messages.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters,
+                        mPasswordMinLetters));
+            }
+            if ((errorCode & NOT_ENOUGH_UPPER_CASE) > 0) {
+                messages.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase,
+                        mPasswordMinUpperCase));
+            }
+            if ((errorCode & NOT_ENOUGH_LOWER_CASE) > 0) {
+                messages.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase,
+                        mPasswordMinLowerCase));
+            }
+            if ((errorCode & NOT_ENOUGH_DIGITS) > 0) {
+                messages.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric,
+                        mPasswordMinNumeric));
+            }
+            if ((errorCode & NOT_ENOUGH_SYMBOLS) > 0) {
+                messages.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols,
+                        mPasswordMinSymbols));
+            }
+            if ((errorCode & NOT_ENOUGH_NON_LETTER) > 0) {
+                messages.add(getResources().getQuantityString(
+                        R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter,
+                        mPasswordMinNonLetter));
+            }
+            if ((errorCode & TOO_SHORT) > 0) {
+                messages.add(getString(mIsAlphaMode ?
+                        R.string.lockpassword_password_too_short
+                        : R.string.lockpassword_pin_too_short, mPasswordMinLength));
+            }
+            if ((errorCode & TOO_LONG) > 0) {
+                messages.add(getString(mIsAlphaMode ?
+                        R.string.lockpassword_password_too_long
+                        : R.string.lockpassword_pin_too_long, mPasswordMaxLength + 1));
+            }
+            if ((errorCode & CONTAIN_SEQUENTIAL_DIGITS) > 0) {
+                messages.add(getString(R.string.lockpassword_pin_no_sequential_digits));
+            }
+            if ((errorCode & RECENTLY_USED) > 0) {
+                messages.add(getString((mIsAlphaMode) ? R.string.lockpassword_password_recently_used
+                        : R.string.lockpassword_pin_recently_used));
+            }
+            return messages.toArray(new String[0]);
+        }
+
+        /**
          * Update the hint based on current Stage and length of password entry
          */
         private void updateUi() {
@@ -613,29 +786,33 @@
             String password = mPasswordEntry.getText().toString();
             final int length = password.length();
             if (mUiStage == Stage.Introduction) {
-                if (length < mPasswordMinLength) {
-                    String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short
-                            : R.string.lockpassword_pin_too_short, mPasswordMinLength);
-                    mHeaderText.setText(msg);
-                    setNextEnabled(false);
-                } else {
-                    String error = validatePassword(password);
-                    if (error != null) {
-                        mHeaderText.setText(error);
-                        setNextEnabled(false);
-                    } else {
-                        mHeaderText.setText(null);
-                        setNextEnabled(true);
-                    }
-                }
+                mPasswordRestrictionView.setVisibility(View.VISIBLE);
+                final int errorCode = validatePassword(password);
+                String[] messages = convertErrorCodeToMessages(errorCode);
+                // Update the fulfillment of requirements.
+                mPasswordRequirementAdapter.setRequirements(messages);
+                // Enable/Disable the next button accordingly.
+                setNextEnabled(errorCode == NO_ERROR);
             } else {
-                mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint);
+                // Hide password requirement view when we are just asking user to confirm the pw.
+                mPasswordRestrictionView.setVisibility(View.GONE);
+                setHeaderText(getString(
+                        mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint));
                 setNextEnabled(canInput && length > 0);
             }
             setNextText(mUiStage.buttonText);
             mPasswordEntryInputDisabler.setInputEnabled(canInput);
         }
 
+        private void setHeaderText(String text) {
+            // Only set the text if it is different than the existing one to avoid announcing again.
+            if (!TextUtils.isEmpty(mHeaderText.getText())
+                    && mHeaderText.getText().toString().equals(text)) {
+                return;
+            }
+            mHeaderText.setText(text);
+        }
+
         public void afterTextChanged(Editable s) {
             // Changing the text while error displayed resets to NeedToConfirm state
             if (mUiStage == Stage.ConfirmWrong) {
diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java
index 934e752..db6a373b 100644
--- a/src/com/android/settings/DevelopmentSettings.java
+++ b/src/com/android/settings/DevelopmentSettings.java
@@ -1911,7 +1911,7 @@
         } else if (preference == mWebViewMultiprocess) {
             writeWebViewMultiprocessOptions();
         } else if (SHORTCUT_MANAGER_RESET_KEY.equals(preference.getKey())) {
-            confirmResetShortcutManagerThrottling();
+            resetShortcutManagerThrottling();
         } else {
             return super.onPreferenceTreeClick(preference);
         }
@@ -2153,30 +2153,18 @@
                 }
             };
 
-    private void confirmResetShortcutManagerThrottling() {
+    private void resetShortcutManagerThrottling() {
         final IShortcutService service = IShortcutService.Stub.asInterface(
                 ServiceManager.getService(Context.SHORTCUT_SERVICE));
-
-        DialogInterface.OnClickListener onClickListener = new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                if (which == DialogInterface.BUTTON_POSITIVE) {
-                    try {
-                        service.resetThrottling();
-                    } catch (RemoteException e) {
-                    }
-                }
+        if (service != null) {
+            try {
+                service.resetThrottling();
+                Toast.makeText(getActivity(), R.string.reset_shortcut_manager_throttling_complete,
+                        Toast.LENGTH_SHORT).show();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to reset rate limiting", e);
             }
-        };
-
-        new AlertDialog.Builder(getActivity())
-                .setTitle(R.string.confirm_reset_shortcut_manager_throttling_title)
-                .setMessage(R.string.confirm_reset_shortcut_manager_throttling_message)
-                .setPositiveButton(R.string.okay, onClickListener)
-                .setNegativeButton(android.R.string.cancel, null)
-                .create()
-                .show();
-
+        }
     }
 
     private void updateOemUnlockSettingDescription() {
diff --git a/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
index c1a0925..23755fd 100644
--- a/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
@@ -24,24 +24,21 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
+import android.view.View;
+import android.widget.Button;
 
 import com.android.internal.logging.MetricsProto.MetricsEvent;
-import com.android.internal.util.CharSequences;
 import com.android.settings.ChooseLockGeneric;
 import com.android.settings.ChooseLockSettingsHelper;
 import com.android.settings.R;
 import com.android.settingslib.HelpUtils;
-import com.android.setupwizardlib.GlifRecyclerLayout;
-import com.android.setupwizardlib.items.IItem;
-import com.android.setupwizardlib.items.Item;
-import com.android.setupwizardlib.items.RecyclerItemAdapter;
 import com.android.setupwizardlib.span.LinkSpan;
 
 /**
  * Onboarding activity for fingerprint enrollment.
  */
 public class FingerprintEnrollIntroduction extends FingerprintEnrollBase
-        implements RecyclerItemAdapter.OnItemSelectedListener, LinkSpan.OnClickListener {
+        implements View.OnClickListener, LinkSpan.OnClickListener {
 
     private static final String TAG = "FingerprintIntro";
 
@@ -57,12 +54,11 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.fingerprint_enroll_introduction);
         setHeaderText(R.string.security_settings_fingerprint_enroll_introduction_title);
-        final GlifRecyclerLayout layout = (GlifRecyclerLayout) getLayout();
+
+        final Button cancelButton = (Button) findViewById(R.id.fingerprint_cancel_button);
+        cancelButton.setOnClickListener(this);
+
         mUserManager = UserManager.get(this);
-        final RecyclerItemAdapter adapter = (RecyclerItemAdapter) layout.getAdapter();
-        adapter.setOnItemSelectedListener(this);
-        Item item = (Item) adapter.findItemById(R.id.fingerprint_introduction_message);
-        item.setTitle(getText(R.string.security_settings_fingerprint_enroll_introduction_message));
         updatePasswordQuality();
     }
 
@@ -73,6 +69,11 @@
     }
 
     @Override
+    protected Button getNextButton() {
+        return (Button) findViewById(R.id.fingerprint_next_button);
+    }
+
+    @Override
     protected void onNextButtonClick() {
         if (!mHasPassword) {
             // No fingerprints registered, launch into enrollment wizard.
@@ -142,14 +143,11 @@
     }
 
     @Override
-    public void onItemSelected(IItem item) {
-        switch (((Item) item).getId()) {
-            case R.id.next_button:
-                onNextButtonClick();
-                break;
-            case R.id.cancel_button:
-                onCancelButtonClick();
-                break;
+    public void onClick(View v) {
+        if (v.getId() == R.id.fingerprint_cancel_button) {
+            onCancelButtonClick();
+        } else {
+            super.onClick(v);
         }
     }
 
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
index aac2057..cc8e823 100644
--- a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
@@ -19,15 +19,13 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.UserHandle;
+import android.widget.Button;
 
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settings.R;
 import com.android.settings.SetupChooseLockGeneric;
 import com.android.settings.SetupWizardUtils;
-import com.android.setupwizardlib.GlifRecyclerLayout;
-import com.android.setupwizardlib.items.Item;
-import com.android.setupwizardlib.items.RecyclerItemAdapter;
 
 public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntroduction {
 
@@ -53,15 +51,14 @@
 
     @Override
     protected void initViews() {
-        GlifRecyclerLayout layout = (GlifRecyclerLayout) getLayout();
-        final RecyclerItemAdapter adapter = (RecyclerItemAdapter) layout.getAdapter();
-        final Item nextItem = (Item) adapter.findItemById(R.id.next_button);
-        nextItem.setTitle(
-                getText(R.string.security_settings_fingerprint_enroll_introduction_continue_setup));
+        super.initViews();
+        Button nextButton = getNextButton();
+        nextButton.setText(
+                R.string.security_settings_fingerprint_enroll_introduction_continue_setup);
 
-        final Item cancelItem = (Item) adapter.findItemById(R.id.cancel_button);
-        cancelItem.setTitle(
-                getText(R.string.security_settings_fingerprint_enroll_introduction_cancel_setup));
+        final Button cancelButton = (Button) findViewById(R.id.fingerprint_cancel_button);
+        cancelButton.setText(
+                R.string.security_settings_fingerprint_enroll_introduction_cancel_setup);
     }
 
     @Override
diff --git a/src/com/android/settings/password/PasswordRequirementAdapter.java b/src/com/android/settings/password/PasswordRequirementAdapter.java
new file mode 100644
index 0000000..dd4d10a
--- /dev/null
+++ b/src/com/android/settings/password/PasswordRequirementAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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.password;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+import static com.android.settings.password.PasswordRequirementAdapter
+        .PasswordRequirementViewHolder;
+
+/**
+ * Used in {@link com.android.settings.ConfirmLockPassword} to show password requirements.
+ */
+public class PasswordRequirementAdapter extends
+        RecyclerView.Adapter<PasswordRequirementViewHolder> {
+    private String[] mRequirements;
+
+    public PasswordRequirementAdapter() {
+        setHasStableIds(true);
+    }
+
+    @Override
+    public PasswordRequirementViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View v = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.password_requirement_item, parent, false);
+        return new PasswordRequirementViewHolder(v);
+    }
+
+    @Override
+    public int getItemCount() {
+        return  mRequirements.length;
+    }
+
+    public void setRequirements(String[] requirements) {
+        mRequirements = requirements;
+        notifyDataSetChanged();
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return mRequirements[position].hashCode();
+    }
+
+    @Override
+    public void onBindViewHolder(PasswordRequirementViewHolder holder, int position) {
+        holder.mDescriptionText.setText(mRequirements[position]);
+    }
+
+    public static class PasswordRequirementViewHolder extends RecyclerView.ViewHolder {
+        private TextView mDescriptionText;
+
+        public PasswordRequirementViewHolder(View itemView) {
+            super(itemView);
+            mDescriptionText = (TextView) itemView;
+        }
+    }
+
+}
diff --git a/src/com/android/settings/vpn2/AppManagementFragment.java b/src/com/android/settings/vpn2/AppManagementFragment.java
index 99d2f55..b306577 100644
--- a/src/com/android/settings/vpn2/AppManagementFragment.java
+++ b/src/com/android/settings/vpn2/AppManagementFragment.java
@@ -80,7 +80,7 @@
         public void onForget() {
             // Unset always-on-vpn when forgetting the VPN
             if (isVpnAlwaysOn()) {
-                setAlwaysOnVpnByUI(false);
+                setAlwaysOnVpn(false);
             }
             // Also dismiss and go back to VPN list
             finish();
@@ -187,14 +187,18 @@
         if (mUserId == UserHandle.USER_SYSTEM) {
             VpnUtils.clearLockdownVpn(getContext());
         }
-        final boolean success = mConnectivityManager.setAlwaysOnVpnPackageForUser(mUserId,
-                isEnabled ? mPackageName : null, /* lockdownEnabled */ false);
+        final boolean success = setAlwaysOnVpn(isEnabled);
         if (isEnabled && (!success || !isVpnAlwaysOn())) {
             CannotConnectFragment.show(this, mVpnLabel);
         }
         return success;
     }
 
+    private boolean setAlwaysOnVpn(boolean isEnabled) {
+         return mConnectivityManager.setAlwaysOnVpnPackageForUser(mUserId,
+                isEnabled ? mPackageName : null, /* lockdownEnabled */ false);
+    }
+
     private boolean checkTargetVersion() {
         if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
             return true;