am a3be8640: am 0f9daab7: Manage apps "third party" and "on sd card" filters were swapped.

Merge commit 'a3be864081ece005cf378ba8878063218cb1a131'

* commit 'a3be864081ece005cf378ba8878063218cb1a131':
  Manage apps "third party" and "on sd card" filters were swapped.
diff --git a/res/layout/locale_picker_in_setupwizard.xml b/res/layout/locale_picker_in_setupwizard.xml
index d816f47..e412b07 100644
--- a/res/layout/locale_picker_in_setupwizard.xml
+++ b/res/layout/locale_picker_in_setupwizard.xml
@@ -4,9 +4,9 @@
      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.
@@ -18,22 +18,6 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
-    
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/select_your_language"
-        android:layout_gravity="center_horizontal"
-        android:paddingBottom="10dip"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-    />
-    
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="1dip"
-        android:layout_marginTop="8dip"
-        android:background="@android:drawable/divider_horizontal_dark"
-    />
 
     <ListView android:id="@android:id/list"
         android:layout_width="match_parent"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 720b998..a725166 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -419,9 +419,7 @@
 
     <!-- LocalePicker -->
     <!-- Title for the locale picker activity -->
-    <string name="language_picker_title">Locale</string>
-    <!-- Locale picker screen. Label for choosing the locale -->
-    <string name="select_your_language">Select your language</string>
+    <string name="language_picker_title">Language</string>
 
     <!-- The title of the dialog to pick an activity.  This is shown when there are multiple activities that can do a particular action.  For example, suppose you click on the "Share" menu item in the Browser.  Since you can share the webpage URL via many communication methods, this dialog would come up with choices like "Email", "IM", etc.  This is a generic message, and the previous example is a single possible scenario (so please don't assume it's for the browser or anything :) ). -->
     <string name="activity_picker_label">Select activity</string>
@@ -592,7 +590,7 @@
     <string name="lockpassword_pin_too_short">PIN must be at least %d characters</string>
 
     <!-- Hint shown after minimum password criteria is met -->
-    <string name="lockpassword_press_continue">Press Continue when done</string>
+    <string name="lockpassword_press_continue">Touch Continue when done</string>
 
     <!-- Hint shown after minimum password criteria is met -->
     <string name="lockpassword_continue_label">Continue</string>
@@ -605,6 +603,9 @@
     <!-- 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>
 
+    <!-- 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 disallows 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>
 
@@ -617,6 +618,45 @@
     <!-- 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>
 
+    <!-- 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>
+    </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>
+    </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>
+    </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>
+    </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>
+    </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>
+    </plurals>
+
+    <!-- Error shown when in PASSWORD mode and password has been used recently. Please keep this string short! -->
+    <string name="lockpassword_password_recently_used">Device administrator disallows using a recent password</string>
+
     <!-- Label for ChoosePassword/PIN OK button -->
     <string name="lockpassword_ok_label">OK</string>
 
@@ -714,6 +754,8 @@
     <string name="bluetooth_profile_headset">Phone</string>
     <!-- Bluetooth settings.  The user-visible string that is used whenever referring to the OPP profile. -->
     <string name="bluetooth_profile_opp">Transfer</string>
+    <!-- Bluetooth settings. The user-visible string that is used whenever referring to the HID profile. -->
+    <string name="bluetooth_profile_hid">Input Device</string>
 
     <!-- Bluetooth settings.  The summary string when a device is connected to the A2DP profile. -->
     <string name="bluetooth_summary_connected_to_a2dp">Connected to media audio</string>
@@ -721,6 +763,8 @@
     <string name="bluetooth_summary_connected_to_headset">Connected to phone audio</string>
     <!-- Bluetooth settings.  The summary string when a device is connected to the A2DP and headset profiles. -->
     <string name="bluetooth_summary_connected_to_a2dp_headset">Connected to phone and media audio</string>
+    <!-- Bluetooth settings. The summary string when a device is connected to the HID profile. -->
+    <string name="bluetooth_summary_connected_to_hid">Connected to input device</string>
 
     <!-- Bluetooth settings.  Connection options screen.  The title of the screen. -->
     <string name="bluetooth_device_advanced_title"><xliff:g id="device_name">%1$s</xliff:g> options</string>
@@ -738,6 +782,8 @@
     <string name="bluetooth_opp_profile_summary_connected">Connected to file transfer server</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the OPP checkbox preference when OPP is not connected. -->
     <string name="bluetooth_opp_profile_summary_not_connected">Not connected to file transfer server</string>
+    <!-- Bluetooth settings. Connection options screen. The summary for the HID checkbox preference when HID is connected. -->
+    <string name="bluetooth_hid_profile_summary_connected">Connected to input device</string>
 
     <!-- Bluetooth settings.  Connection options screen.  The summary for the A2DP checkbox preference that describes how checking it will set the A2DP profile as preferred. -->
     <string name="bluetooth_a2dp_profile_summary_use_for">Use for media audio</string>
@@ -745,6 +791,10 @@
     <string name="bluetooth_headset_profile_summary_use_for">Use for phone audio</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the OPP checkbox preference that describes how checking it will set the OPP profile as preferred. -->
     <string name="bluetooth_opp_profile_summary_use_for">Use for file transfer</string>
+    <!-- Bluetooth settings. Connection options screen. The summary
+         for the HID checkbox preference that describes how checking it
+         will set the HID profile as preferred. -->
+    <string name="bluetooth_hid_profile_summary_use_for">Use for input</string>
 
     <!-- Bluetooth settings.  Dock Setting Title -->
     <string name="bluetooth_dock_settings">Dock Settings</string>
@@ -1914,6 +1964,8 @@
 
     <!-- Input methods Settings -->
     <string name="input_methods_settings_title">Text input</string>
+    <!-- Setting name for Input Method chooser -->
+    <string name="input_method">Input method</string>
     <string name="input_methods_settings_summary">Manage text input options</string>
     <!-- Input Methods Settings localized format string for generating the appropriate "Foo settings" menu label for the Input Method named "Foo" -->
     <string name="input_methods_settings_label_format"><xliff:g id="ime_name">%1$s</xliff:g> settings</string>
diff --git a/res/xml/language_settings.xml b/res/xml/language_settings.xml
index d3c0e02..3b94680 100644
--- a/res/xml/language_settings.xml
+++ b/res/xml/language_settings.xml
@@ -26,7 +26,13 @@
     </PreferenceScreen>
 
     <PreferenceCategory android:key="text_category"
-            android:title="@string/text_category" />
+            android:title="@string/text_category">
+
+        <PreferenceScreen android:key="input_method"
+                android:title="@string/input_method"
+                android:widgetLayout="@*android:layout/preference_dialog" />
+
+    </PreferenceCategory>
 
     <PreferenceScreen
             android:key="hardkeyboard_category"
diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java
index 0672ad9..5247e79 100644
--- a/src/com/android/settings/ChooseLockGeneric.java
+++ b/src/com/android/settings/ChooseLockGeneric.java
@@ -138,7 +138,7 @@
                 } else if (KEY_UNLOCK_SET_PIN.equals(key)) {
                     enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
                 } else if (KEY_UNLOCK_SET_PASSWORD.equals(key)) {
-                    enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+                    enabled = quality <= DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
                 }
                 if (!enabled) {
                     pref.setSummary(R.string.unlock_set_unlock_disabled_summary);
diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java
index b5e72d7..4d447f4 100644
--- a/src/com/android/settings/ChooseLockPassword.java
+++ b/src/com/android/settings/ChooseLockPassword.java
@@ -48,6 +48,12 @@
     private TextView mPasswordEntry;
     private int mPasswordMinLength = 4;
     private int mPasswordMaxLength = 16;
+    private int mPasswordMinLetters = 0;
+    private int mPasswordMinUpperCase = 0;
+    private int mPasswordMinLowerCase = 0;
+    private int mPasswordMinSymbols = 0;
+    private int mPasswordMinNumeric = 0;
+    private int mPasswordMinNonLetter = 0;
     private LockPatternUtils mLockPatternUtils;
     private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
     private ChooseLockSettingsHelper mChooseLockSettingsHelper;
@@ -61,6 +67,12 @@
     private Button mNextButton;
     public static final String PASSWORD_MIN_KEY = "lockscreen.password_min";
     public static final String PASSWORD_MAX_KEY = "lockscreen.password_max";
+    public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters";
+    public static final String PASSWORD_MIN_LOWERCASE_KEY = "lockscreen.password_min_lowercase";
+    public static final String PASSWORD_MIN_UPPERCASE_KEY = "lockscreen.password_min_uppercase";
+    public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric";
+    public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols";
+    public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter";
     private static Handler mHandler = new Handler();
     private static final int CONFIRM_EXISTING_REQUEST = 58;
     static final int RESULT_FINISHED = RESULT_FIRST_USER;
@@ -101,19 +113,27 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mLockPatternUtils = new LockPatternUtils(this);
-        mRequestedQuality = getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, mRequestedQuality);
-        mPasswordMinLength = getIntent().getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength);
+        mRequestedQuality = Math.max(getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
+                mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality());
+        mPasswordMinLength = Math.max(
+                getIntent().getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength), mLockPatternUtils
+                        .getRequestedMinimumPasswordLength());
         mPasswordMaxLength = getIntent().getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
-
+        mPasswordMinLetters = Math.max(getIntent().getIntExtra(PASSWORD_MIN_LETTERS_KEY,
+                mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters());
+        mPasswordMinUpperCase = Math.max(getIntent().getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
+                mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase());
+        mPasswordMinLowerCase = Math.max(getIntent().getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
+                mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase());
+        mPasswordMinNumeric = Math.max(getIntent().getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
+                mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric());
+        mPasswordMinSymbols = Math.max(getIntent().getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
+                mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols());
+        mPasswordMinNonLetter = Math.max(getIntent().getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
+                mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter());
         final boolean confirmCredentials = getIntent().getBooleanExtra("confirm_credentials", true);
-        int minMode = mLockPatternUtils.getRequestedPasswordQuality();
-        if (mRequestedQuality < minMode) {
-            mRequestedQuality = minMode;
-        }
-        int minLength = mLockPatternUtils.getRequestedMinimumPasswordLength();
-        if (mPasswordMinLength < minLength) {
-            mPasswordMinLength = minLength;
-        }
+
+
         initViews();
         mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
         if (savedInstanceState == null) {
@@ -142,7 +162,8 @@
         mPasswordEntry.addTextChangedListener(this);
 
         mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
-            || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality;
+                || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality
+                || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
         mKeyboardHelper = new PasswordEntryKeyboardHelper(this, mKeyboardView, mPasswordEntry);
         mKeyboardHelper.setKeyboardMode(mIsAlphaMode ?
                 PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
@@ -212,9 +233,12 @@
                     R.string.lockpassword_password_too_long
                     : R.string.lockpassword_pin_too_long, mPasswordMaxLength);
         }
-        boolean hasAlpha = false;
-        boolean hasDigit = false;
-        boolean hasSymbol = false;
+        int letters = 0;
+        int numbers = 0;
+        int lowercase = 0;
+        int symbols = 0;
+        int uppercase = 0;
+        int nonletter = 0;
         for (int i = 0; i < password.length(); i++) {
             char c = password.charAt(i);
             // allow non white space Latin-1 characters only
@@ -222,32 +246,65 @@
                 return getString(R.string.lockpassword_illegal_character);
             }
             if (c >= '0' && c <= '9') {
-                hasDigit = true;
-            } else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
-                hasAlpha = true;
+                numbers++;
+                nonletter++;
+            } else if (c >= 'A' && c <= 'Z') {
+                letters++;
+                uppercase++;
+            } else if (c >= 'a' && c <= 'z') {
+                letters++;
+                lowercase++;
             } else {
-                hasSymbol = true;
+                symbols++;
+                nonletter++;
             }
         }
         if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality
-                && (hasAlpha | hasSymbol)) {
-            // This shouldn't be possible unless user finds some way to bring up soft keyboard
+                && (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);
+        } 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;
-            final boolean symbolic = false; // not yet
-            if ((alphabetic || alphanumeric) && !hasAlpha) {
+            if ((alphabetic || alphanumeric) && letters == 0) {
                 return getString(R.string.lockpassword_password_requires_alpha);
             }
-            if (alphanumeric && !hasDigit) {
+            if (alphanumeric && numbers == 0) {
                 return getString(R.string.lockpassword_password_requires_digit);
             }
-            if (symbolic && !hasSymbol) {
-                return getString(R.string.lockpassword_password_requires_symbol);
-            }
+        }
+        if(mLockPatternUtils.checkPasswordHistory(password)) {
+            return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used
+                    : R.string.lockpassword_pin_recently_used);
         }
         return null;
     }
@@ -306,7 +363,7 @@
 
     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
         // Check if this was the result of hitting the enter key
-        if (actionId == EditorInfo.IME_NULL) {
+        if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN) {
             handleNext();
             return true;
         }
diff --git a/src/com/android/settings/ChooseLockPatternExample.java b/src/com/android/settings/ChooseLockPatternExample.java
index cba88b0..3c96d53 100644
--- a/src/com/android/settings/ChooseLockPatternExample.java
+++ b/src/com/android/settings/ChooseLockPatternExample.java
@@ -66,6 +66,7 @@
             stopAnimation(mAnimation);
             Intent intent = new Intent(this, ChooseLockPattern.class);
             intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            intent.putExtra("confirm_credentials", false);
             startActivity(intent);
             finish();
         }
diff --git a/src/com/android/settings/ChooseLockPatternTutorial.java b/src/com/android/settings/ChooseLockPatternTutorial.java
index ee0019f..e699abf 100644
--- a/src/com/android/settings/ChooseLockPatternTutorial.java
+++ b/src/com/android/settings/ChooseLockPatternTutorial.java
@@ -35,6 +35,7 @@
         if (savedInstanceState == null && lockPatternUtils.isPatternEverChosen()) {
             Intent intent = new Intent(this, ChooseLockPattern.class);
             intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            intent.putExtra("confirm_credentials", false);
             startActivity(intent);
             finish();
         } else {
diff --git a/src/com/android/settings/ChooseLockSettingsHelper.java b/src/com/android/settings/ChooseLockSettingsHelper.java
index ba83f8e..abcfc05 100644
--- a/src/com/android/settings/ChooseLockSettingsHelper.java
+++ b/src/com/android/settings/ChooseLockSettingsHelper.java
@@ -52,6 +52,7 @@
             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
             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);
                 break;
diff --git a/src/com/android/settings/ConfirmLockPassword.java b/src/com/android/settings/ConfirmLockPassword.java
index 6bc135b..0e893fa 100644
--- a/src/com/android/settings/ConfirmLockPassword.java
+++ b/src/com/android/settings/ConfirmLockPassword.java
@@ -65,7 +65,8 @@
         mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
         mHeaderText = (TextView) findViewById(R.id.headerText);
         final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
-                || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality;
+                || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
+                || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality;
         mHeaderText.setText(isAlpha ? R.string.lockpassword_confirm_your_password_header
                 : R.string.lockpassword_confirm_your_pin_header);
         mKeyboardHelper = new PasswordEntryKeyboardHelper(this, mKeyboardView, mPasswordEntry);
diff --git a/src/com/android/settings/ConfirmLockPattern.java b/src/com/android/settings/ConfirmLockPattern.java
index eb9a4d8..8d44875 100644
--- a/src/com/android/settings/ConfirmLockPattern.java
+++ b/src/com/android/settings/ConfirmLockPattern.java
@@ -144,6 +144,11 @@
         long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
         if (deadline != 0) {
             handleAttemptLockout(deadline);
+        } else if (!mLockPatternView.isEnabled()) {
+            // The deadline has passed, but the timer was cancelled...
+            // Need to clean up.
+            mNumWrongConfirmAttempts = 0;
+            updateStage(Stage.NeedToUnlock);
         }
     }
 
diff --git a/src/com/android/settings/LanguageSettings.java b/src/com/android/settings/LanguageSettings.java
index 1252eec..6036ac3 100644
--- a/src/com/android/settings/LanguageSettings.java
+++ b/src/com/android/settings/LanguageSettings.java
@@ -44,6 +44,8 @@
 public class LanguageSettings extends PreferenceActivity {
     
     private static final String KEY_PHONE_LANGUAGE = "phone_language";
+    private static final String KEY_INPUT_METHOD = "input_method";
+
     private boolean mHaveHardKeyboard;
 
     private List<InputMethodInfo> mInputMethodProperties;
@@ -290,7 +292,10 @@
                 mLastTickedInputMethodId = null;
             }
         } else if (preference instanceof PreferenceScreen) {
-            if (preference.getIntent() == null) {
+            if (KEY_INPUT_METHOD.equals(preference.getKey())) {
+                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+                imm.showInputMethodPicker();
+            } else if (preference.getIntent() == null) {
                 PreferenceScreen pref = (PreferenceScreen) preference;
                 String activityName = pref.getKey();
                 String packageName = activityName.substring(0, activityName
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index 5ebc1f1..f86af91 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -31,6 +31,7 @@
 import android.database.Cursor;
 import android.location.LocationManager;
 import android.os.Bundle;
+import android.os.SystemProperties;
 import android.preference.CheckBoxPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
@@ -150,6 +151,7 @@
                     break;
                 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
                 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
                     addPreferencesFromResource(R.xml.security_settings_password);
                     break;
             }
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index 9eee4e0..6764c4c 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -86,7 +86,7 @@
                 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
 
         mUsbRegexs = cm.getTetherableUsbRegexs();
-        if (mUsbRegexs.length == 0) {
+        if (mUsbRegexs.length == 0 || Utils.isMonkeyRunning()) {
             getPreferenceScreen().removePreference(mUsbTether);
 
             setTitle(R.string.tether_settings_title_wifi);
diff --git a/src/com/android/settings/bluetooth/BluetoothEventRedirector.java b/src/com/android/settings/bluetooth/BluetoothEventRedirector.java
index dbdf6c1..dc8ab78 100644
--- a/src/com/android/settings/bluetooth/BluetoothEventRedirector.java
+++ b/src/com/android/settings/bluetooth/BluetoothEventRedirector.java
@@ -24,6 +24,7 @@
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothInputDevice;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -119,6 +120,19 @@
                 mManager.getCachedDeviceManager().onProfileStateChanged(device,
                         Profile.A2DP, newState);
 
+            } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) {
+                final int newState = intent.getIntExtra(
+                        BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0);
+                final int oldState = intent.getIntExtra(
+                        BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, 0);
+                if (newState == BluetoothInputDevice.STATE_DISCONNECTED &&
+                        oldState == BluetoothInputDevice.STATE_CONNECTING) {
+                    Log.i(TAG, "Failed to connect BT HID");
+                }
+
+                mManager.getCachedDeviceManager().onProfileStateChanged(device,
+                        Profile.HID, newState);
+
             } else if (action.equals(BluetoothDevice.ACTION_CLASS_CHANGED)) {
                 mManager.getCachedDeviceManager().onBtClassChanged(device);
 
diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
index b78427b..bdaffca 100644
--- a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
+++ b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
@@ -559,6 +559,12 @@
                     Log.v(TAG, "opp classbits != uuid");
                     printUuids = true;
                 }
+
+                if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HID) !=
+                    mProfiles.contains(Profile.HID)) {
+                    Log.v(TAG, "hid classbits != uuid");
+                    printUuids = true;
+                }
             }
 
             if (printUuids) {
@@ -648,7 +654,10 @@
      * @return A one-off summary that is applicable for the current state, or 0.
      */
     private int getOneOffSummary() {
-        boolean isA2dpConnected = false, isHeadsetConnected = false, isConnecting = false;
+        boolean isA2dpConnected = false;
+        boolean isHeadsetConnected = false;
+        boolean isHidConnected = false;
+        boolean isConnecting = false;
 
         if (mProfiles.contains(Profile.A2DP)) {
             LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
@@ -666,6 +675,14 @@
             isHeadsetConnected = profileManager.isConnected(mDevice);
         }
 
+        if (mProfiles.contains(Profile.HID)) {
+            LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
+                    .getProfileManager(mLocalManager, Profile.HID);
+            isConnecting |= profileManager.getConnectionStatus(mDevice) ==
+                    SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
+            isHidConnected = profileManager.isConnected(mDevice);
+        }
+
         if (isConnecting) {
             // If any of these important profiles is connecting, prefer that
             return SettingsBtStatus.getConnectionStatusSummary(
@@ -676,6 +693,8 @@
             return R.string.bluetooth_summary_connected_to_a2dp;
         } else if (isHeadsetConnected) {
             return R.string.bluetooth_summary_connected_to_headset;
+        } else if (isHidConnected) {
+            return R.string.bluetooth_summary_connected_to_hid;
         } else {
             return 0;
         }
@@ -692,7 +711,8 @@
     }
 
     private boolean isConnectableProfile(Profile profile) {
-        return profile.equals(Profile.HEADSET) || profile.equals(Profile.A2DP);
+        return profile.equals(Profile.HEADSET) || profile.equals(Profile.A2DP) ||
+                profile.equals(Profile.HID);
     }
 
     public void onCreateContextMenu(ContextMenu menu) {
diff --git a/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java b/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java
index 2a8af5f..62c6f79 100644
--- a/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java
+++ b/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java
@@ -299,6 +299,8 @@
                 return R.string.bluetooth_a2dp_profile_summary_use_for;
             case HEADSET:
                 return R.string.bluetooth_headset_profile_summary_use_for;
+            case HID:
+                return R.string.bluetooth_hid_profile_summary_use_for;
             default:
                 return 0;
         }
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
index 01714fe..b09e144 100644
--- a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
+++ b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothInputDevice;
 import android.bluetooth.BluetoothUuid;
 import android.os.Handler;
 import android.os.ParcelUuid;
@@ -55,6 +56,10 @@
         BluetoothUuid.ObexObjectPush
     };
 
+    /* package */ static final ParcelUuid[] HID_PROFILE_UUIDS = new ParcelUuid[] {
+        BluetoothUuid.Hid
+    };
+
     /**
      * An interface for notifying BluetoothHeadset IPC clients when they have
      * been connected to the BluetoothHeadset service.
@@ -97,6 +102,9 @@
 
                 profileManager = new OppProfileManager(localManager);
                 sProfileMap.put(Profile.OPP, profileManager);
+
+                profileManager = new HidProfileManager(localManager);
+                sProfileMap.put(Profile.HID, profileManager);
             }
         }
     }
@@ -161,6 +169,10 @@
         if (BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS)) {
             profiles.add(Profile.OPP);
         }
+
+        if (BluetoothUuid.containsAnyUuid(uuids, HID_PROFILE_UUIDS)) {
+            profiles.add(Profile.HID);
+        }
     }
 
     protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) {
@@ -195,7 +207,8 @@
     public enum Profile {
         HEADSET(R.string.bluetooth_profile_headset),
         A2DP(R.string.bluetooth_profile_a2dp),
-        OPP(R.string.bluetooth_profile_opp);
+        OPP(R.string.bluetooth_profile_opp),
+        HID(R.string.bluetooth_profile_hid);
 
         public final int localizedString;
 
@@ -518,4 +531,86 @@
             }
         }
     }
+
+    private static class HidProfileManager extends LocalBluetoothProfileManager {
+        private BluetoothInputDevice mService;
+
+        public HidProfileManager(LocalBluetoothManager localManager) {
+            super(localManager);
+            mService = new BluetoothInputDevice(localManager.getContext());
+        }
+
+        @Override
+        public boolean connect(BluetoothDevice device) {
+            return mService.connectInputDevice(device);
+        }
+
+        @Override
+        public int convertState(int hidState) {
+            switch (hidState) {
+            case BluetoothInputDevice.STATE_CONNECTED:
+                return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
+            case BluetoothInputDevice.STATE_CONNECTING:
+                return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
+            case BluetoothInputDevice.STATE_DISCONNECTED:
+                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
+            case BluetoothInputDevice.STATE_DISCONNECTING:
+                return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
+            default:
+                return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
+            }
+        }
+
+        @Override
+        public boolean disconnect(BluetoothDevice device) {
+            return mService.disconnectInputDevice(device);
+        }
+
+        @Override
+        public Set<BluetoothDevice> getConnectedDevices() {
+            return mService.getConnectedInputDevices();
+        }
+
+        @Override
+        public int getConnectionStatus(BluetoothDevice device) {
+            return convertState(mService.getInputDeviceState(device));
+        }
+
+        @Override
+        public int getPreferred(BluetoothDevice device) {
+            return mService.getInputDevicePriority(device);
+        }
+
+        @Override
+        public int getSummary(BluetoothDevice device) {
+            final int connectionStatus = getConnectionStatus(device);
+
+            if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
+                return R.string.bluetooth_hid_profile_summary_connected;
+            } else {
+                return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
+            }
+        }
+
+        @Override
+        public boolean isPreferred(BluetoothDevice device) {
+            return mService.getInputDevicePriority(device) > BluetoothInputDevice.PRIORITY_OFF;
+        }
+
+        @Override
+        public boolean isProfileReady() {
+            return true;
+        }
+
+        @Override
+        public void setPreferred(BluetoothDevice device, boolean preferred) {
+            if (preferred) {
+                if (mService.getInputDevicePriority(device) < BluetoothInputDevice.PRIORITY_ON) {
+                    mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
+                }
+            } else {
+                mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_OFF);
+            }
+        }
+    }
 }
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index d389cae..45bc9da 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -24,6 +24,7 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.wifi.ScanResult;
@@ -62,6 +63,9 @@
     private static final int MENU_ID_FORGET = Menu.FIRST + 3;
     private static final int MENU_ID_MODIFY = Menu.FIRST + 4;
 
+    // this boolean extra specifies whether to disable the Next button when not connected
+    private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
+
     private final IntentFilter mFilter;
     private final BroadcastReceiver mReceiver;
     private final Scanner mScanner;
@@ -82,6 +86,9 @@
     private AccessPoint mSelected;
     private WifiDialog mDialog;
 
+    // should Next button only be enabled when we have a connection?
+    private boolean mEnableNextOnConnection;
+
     public WifiSettings() {
         mFilter = new IntentFilter();
         mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
@@ -107,6 +114,18 @@
 
         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
 
+        // if we're supposed to enable/disable the Next button based on our current connection
+        // state, start it off in the right state
+        mEnableNextOnConnection = getIntent().getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
+        if (mEnableNextOnConnection && hasNextButton()) {
+            ConnectivityManager connectivity = (ConnectivityManager)
+                getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+            if (connectivity != null) {
+                NetworkInfo info = connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+                getNextButton().setEnabled(info.isConnected());
+            }
+        }
+
         if (getIntent().getBooleanExtra("only_access_points", false)) {
             addPreferencesFromResource(R.xml.wifi_access_points);
         } else {
@@ -422,8 +441,12 @@
             updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState)
                     intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
         } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
-            updateConnectionState(((NetworkInfo) intent.getParcelableExtra(
-                    WifiManager.EXTRA_NETWORK_INFO)).getDetailedState());
+            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
+                    WifiManager.EXTRA_NETWORK_INFO);
+            if (mEnableNextOnConnection && hasNextButton()) {
+                getNextButton().setEnabled(info.isConnected());
+            }
+            updateConnectionState(info.getDetailedState());
         } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
             updateConnectionState(null);
         }