ChooseLockPassword.java revision 9ba765b875ebeb66d8a870a29a00fe53e9da306d
1bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang/* 2bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * Copyright (C) 2010 The Android Open Source Project 3bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * 4bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * Licensed under the Apache License, Version 2.0 (the "License"); 5bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * you may not use this file except in compliance with the License. 6bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * You may obtain a copy of the License at 7bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * 8bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * http://www.apache.org/licenses/LICENSE-2.0 9bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * 10bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * Unless required by applicable law or agreed to in writing, software 11bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * distributed under the License is distributed on an "AS IS" BASIS, 12bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * See the License for the specific language governing permissions and 14bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * limitations under the License. 15bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang */ 16bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 179a3cb14e28536e4133dddbe952f47189fe344ec1Mason Tangpackage com.android.settings; 18bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 19a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawaimport android.app.Activity; 20a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawaimport android.app.Fragment; 214143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport android.app.admin.DevicePolicyManager; 224143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport android.content.Context; 234143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport android.content.Intent; 244143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport android.graphics.drawable.InsetDrawable; 254143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport android.os.Bundle; 264143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport android.os.Handler; 27c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.os.Message; 28c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.support.v7.widget.LinearLayoutManager; 29bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.support.v7.widget.RecyclerView; 30bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.text.Editable; 31c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.text.InputType; 32bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.text.Selection; 33c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.text.Spannable; 34c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.text.TextUtils; 35a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawaimport android.text.TextWatcher; 36bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.util.Log; 3747d40324272ae39af0872bf5cbf27e1800478021Mason Tangimport android.view.inputmethod.EditorInfo; 38bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.view.KeyEvent; 39c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.view.LayoutInflater; 40c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.view.View; 41bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.view.View.OnClickListener; 42bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.view.ViewGroup; 43bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.widget.Button; 44bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.widget.EditText; 45bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.widget.LinearLayout; 46bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.widget.TextView; 47bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.widget.TextView.OnEditorActionListener; 48bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 4947d40324272ae39af0872bf5cbf27e1800478021Mason Tangimport com.android.internal.logging.MetricsProto.MetricsEvent; 5047d40324272ae39af0872bf5cbf27e1800478021Mason Tangimport com.android.internal.widget.LockPatternUtils; 51bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport com.android.internal.widget.LockPatternUtils.RequestThrottledException; 5247d40324272ae39af0872bf5cbf27e1800478021Mason Tangimport com.android.internal.widget.TextViewInputDisabler; 530c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawaimport com.android.settings.notification.RedactionInterstitial; 54bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport com.android.settings.password.PasswordRequirementAdapter; 553ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport com.android.setupwizardlib.GlifLayout; 563ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang 57c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport java.util.ArrayList; 58c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport java.util.List; 5947d40324272ae39af0872bf5cbf27e1800478021Mason Tang 60bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; 61bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; 623ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; 63bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 64bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; 65bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 66bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangpublic class ChooseLockPassword extends SettingsActivity { 67bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public static final String PASSWORD_MIN_KEY = "lockscreen.password_min"; 68bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public static final String PASSWORD_MAX_KEY = "lockscreen.password_max"; 69bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters"; 70bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public static final String PASSWORD_MIN_LOWERCASE_KEY = "lockscreen.password_min_lowercase"; 71bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public static final String PASSWORD_MIN_UPPERCASE_KEY = "lockscreen.password_min_uppercase"; 72bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric"; 73bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols"; 74bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter"; 753ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang 76a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa private static final String TAG = "ChooseLockPassword"; 77a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa 78bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang @Override 79bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public Intent getIntent() { 80bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang Intent modIntent = new Intent(super.getIntent()); 81bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName()); 82bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang return modIntent; 83bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 84bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 85bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public static Intent createIntent(Context context, int quality, 863ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang int minLength, final int maxLength, boolean requirePasswordToDecrypt, 873ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang boolean confirmCredentials) { 88a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa Intent intent = new Intent().setClass(context, ChooseLockPassword.class); 89bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality); 903ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang intent.putExtra(PASSWORD_MIN_KEY, minLength); 913ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang intent.putExtra(PASSWORD_MAX_KEY, maxLength); 923ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, confirmCredentials); 93bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang intent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, requirePasswordToDecrypt); 940c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa return intent; 950c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa } 960c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa 970c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa public static Intent createIntent(Context context, int quality, 980c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa int minLength, final int maxLength, boolean requirePasswordToDecrypt, 99bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang boolean confirmCredentials, int userId) { 10047d40324272ae39af0872bf5cbf27e1800478021Mason Tang Intent intent = createIntent(context, quality, minLength, maxLength, 10147d40324272ae39af0872bf5cbf27e1800478021Mason Tang requirePasswordToDecrypt, confirmCredentials); 10247d40324272ae39af0872bf5cbf27e1800478021Mason Tang intent.putExtra(Intent.EXTRA_USER_ID, userId); 103bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang return intent; 104bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 105c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 106c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public static Intent createIntent(Context context, int quality, 107f9df037f350fad73659307ba05f230d2db69051aMason Tang int minLength, final int maxLength, boolean requirePasswordToDecrypt, String password) { 1080c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa Intent intent = createIntent(context, quality, minLength, maxLength, 1090c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa requirePasswordToDecrypt, false); 1100c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password); 1110c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa return intent; 1120c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa } 1130c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa 1140c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa public static Intent createIntent(Context context, int quality, int minLength, 11547d40324272ae39af0872bf5cbf27e1800478021Mason Tang int maxLength, boolean requirePasswordToDecrypt, String password, int userId) { 116a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa Intent intent = createIntent(context, quality, minLength, maxLength, 11747d40324272ae39af0872bf5cbf27e1800478021Mason Tang requirePasswordToDecrypt, password); 11847d40324272ae39af0872bf5cbf27e1800478021Mason Tang intent.putExtra(Intent.EXTRA_USER_ID, userId); 119c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik return intent; 120c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 121c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 122c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public static Intent createIntent(Context context, int quality, 123c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik int minLength, final int maxLength, boolean requirePasswordToDecrypt, long challenge) { 124c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik Intent intent = createIntent(context, quality, minLength, maxLength, 125c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik requirePasswordToDecrypt, false); 126c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true); 127c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge); 128c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik return intent; 129c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 130c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 131c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public static Intent createIntent(Context context, int quality, int minLength, 132c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik int maxLength, boolean requirePasswordToDecrypt, long challenge, int userId) { 133c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik Intent intent = createIntent(context, quality, minLength, maxLength, 134c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik requirePasswordToDecrypt, challenge); 135c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik intent.putExtra(Intent.EXTRA_USER_ID, userId); 136c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik return intent; 137c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 138c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 139c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik @Override 140c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik protected boolean isValidFragment(String fragmentName) { 141c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (ChooseLockPasswordFragment.class.getName().equals(fragmentName)) return true; 14247d40324272ae39af0872bf5cbf27e1800478021Mason Tang return false; 14347d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 14447d40324272ae39af0872bf5cbf27e1800478021Mason Tang 145a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa /* package */ Class<? extends Fragment> getFragmentClass() { 146c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik return ChooseLockPasswordFragment.class; 147c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 148bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 149bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang @Override 15047d40324272ae39af0872bf5cbf27e1800478021Mason Tang protected void onCreate(Bundle savedInstanceState) { 15147d40324272ae39af0872bf5cbf27e1800478021Mason Tang super.onCreate(savedInstanceState); 152c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik CharSequence msg = getText(R.string.lockpassword_choose_your_password_header); 153bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang setTitle(msg); 154bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang LinearLayout layout = (LinearLayout) findViewById(R.id.content_parent); 15547d40324272ae39af0872bf5cbf27e1800478021Mason Tang layout.setFitsSystemWindows(false); 156d4b2dbb63039e8e63ef92b4b984aae9c68e1a3b5Winson Chung } 157d4b2dbb63039e8e63ef92b4b984aae9c68e1a3b5Winson Chung 158d4b2dbb63039e8e63ef92b4b984aae9c68e1a3b5Winson Chung public static class ChooseLockPasswordFragment extends InstrumentedFragment 159d4b2dbb63039e8e63ef92b4b984aae9c68e1a3b5Winson Chung implements OnClickListener, OnEditorActionListener, TextWatcher, 16047d40324272ae39af0872bf5cbf27e1800478021Mason Tang SaveAndFinishWorker.Listener { 16147d40324272ae39af0872bf5cbf27e1800478021Mason Tang private static final String KEY_FIRST_PIN = "first_pin"; 162c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik private static final String KEY_UI_STAGE = "ui_stage"; 163c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik private static final String KEY_CURRENT_PASSWORD = "current_password"; 164bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker"; 165bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 16647d40324272ae39af0872bf5cbf27e1800478021Mason Tang private String mCurrentPassword; 16747d40324272ae39af0872bf5cbf27e1800478021Mason Tang private String mChosenPassword; 16847d40324272ae39af0872bf5cbf27e1800478021Mason Tang private boolean mHasChallenge; 16947d40324272ae39af0872bf5cbf27e1800478021Mason Tang private long mChallenge; 17047d40324272ae39af0872bf5cbf27e1800478021Mason Tang private EditText mPasswordEntry; 171bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang private TextViewInputDisabler mPasswordEntryInputDisabler; 172bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE; 17347d40324272ae39af0872bf5cbf27e1800478021Mason Tang private int mPasswordMaxLength = 16; 17447d40324272ae39af0872bf5cbf27e1800478021Mason Tang private int mPasswordMinLetters = 0; 17547d40324272ae39af0872bf5cbf27e1800478021Mason Tang private int mPasswordMinUpperCase = 0; 17647d40324272ae39af0872bf5cbf27e1800478021Mason Tang private int mPasswordMinLowerCase = 0; 17747d40324272ae39af0872bf5cbf27e1800478021Mason Tang private int mPasswordMinSymbols = 0; 17847d40324272ae39af0872bf5cbf27e1800478021Mason Tang private int mPasswordMinNumeric = 0; 179bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang private int mPasswordMinNonLetter = 0; 180c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik private int mPasswordMinLengthToFulfillAllPolicies = 0; 1813ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private int mUserId; 1823ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private boolean mHideDrawer = false; 183bdbf15078ad5efdf27c021d7aca8c8aa4693878cMichael Chan /** 1844143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawa * Password requirements that we need to verify. 1853ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang */ 1863ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private int[] mPasswordRequirements; 1873ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang 1883ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private LockPatternUtils mLockPatternUtils; 1893ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private SaveAndFinishWorker mSaveAndFinishWorker; 1903ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 1913ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private ChooseLockSettingsHelper mChooseLockSettingsHelper; 1923ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private Stage mUiStage = Stage.Introduction; 1933ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private PasswordRequirementAdapter mPasswordRequirementAdapter; 1943ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang 1953ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private TextView mHeaderText; 196a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa private String mFirstPin; 19747d40324272ae39af0872bf5cbf27e1800478021Mason Tang private RecyclerView mPasswordRestrictionView; 198a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa private boolean mIsAlphaMode; 199a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa private Button mCancelButton; 200a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa private Button mNextButton; 201a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa 202a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa private TextChangedHandler mTextChangedHandler; 203a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa 204ffaeace621183dfe8770471a30b2f1138aac5f86Daisuke Miyakawa private static final int CONFIRM_EXISTING_REQUEST = 58; 205309c34fcce4912a9c6f1c0a39c090cebf61296beMichael Chan static final int RESULT_FINISHED = RESULT_FIRST_USER; 206309c34fcce4912a9c6f1c0a39c090cebf61296beMichael Chan 207a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa private static final int MIN_LETTER_IN_PASSWORD = 0; 208a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa private static final int MIN_UPPER_LETTERS_IN_PASSWORD = 1; 209a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa private static final int MIN_LOWER_LETTERS_IN_PASSWORD = 2; 210a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa private static final int MIN_SYMBOLS_IN_PASSWORD = 3; 211a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa private static final int MIN_NUMBER_IN_PASSWORD = 4; 212bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang private static final int MIN_NON_LETTER_IN_PASSWORD = 5; 213a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa 214a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa // Error code returned from {@link #validatePassword(String)}. 215bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang private static final int NO_ERROR = 0; 2164143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawa private static final int CONTAIN_INVALID_CHARACTERS = 1 << 0; 217bdbf15078ad5efdf27c021d7aca8c8aa4693878cMichael Chan private static final int TOO_SHORT = 1 << 1; 218bdbf15078ad5efdf27c021d7aca8c8aa4693878cMichael Chan private static final int TOO_LONG = 1 << 2; 2194143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawa private static final int CONTAIN_NON_DIGITS = 1 << 3; 22047d40324272ae39af0872bf5cbf27e1800478021Mason Tang private static final int CONTAIN_SEQUENTIAL_DIGITS = 1 << 4; 22147d40324272ae39af0872bf5cbf27e1800478021Mason Tang private static final int RECENTLY_USED = 1 << 5; 222bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang private static final int NOT_ENOUGH_LETTER = 1 << 6; 223bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang private static final int NOT_ENOUGH_UPPER_CASE = 1 << 7; 22447d40324272ae39af0872bf5cbf27e1800478021Mason Tang private static final int NOT_ENOUGH_LOWER_CASE = 1 << 8; 22547d40324272ae39af0872bf5cbf27e1800478021Mason Tang private static final int NOT_ENOUGH_DIGITS = 1 << 9; 2263ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private static final int NOT_ENOUGH_SYMBOLS = 1 << 10; 22747d40324272ae39af0872bf5cbf27e1800478021Mason Tang private static final int NOT_ENOUGH_NON_LETTER = 1 << 11; 228bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 22947d40324272ae39af0872bf5cbf27e1800478021Mason Tang /** 23047d40324272ae39af0872bf5cbf27e1800478021Mason Tang * Keep track internally of where the user is in choosing a pattern. 23147d40324272ae39af0872bf5cbf27e1800478021Mason Tang */ 23247d40324272ae39af0872bf5cbf27e1800478021Mason Tang protected enum Stage { 2333ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang 23447d40324272ae39af0872bf5cbf27e1800478021Mason Tang Introduction(R.string.lockpassword_choose_your_password_header, 235bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang R.string.lockpassword_choose_your_pin_header, 23647d40324272ae39af0872bf5cbf27e1800478021Mason Tang R.string.lockpassword_continue_label), 23747d40324272ae39af0872bf5cbf27e1800478021Mason Tang 238c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik NeedToConfirm(R.string.lockpassword_confirm_your_password_header, 239c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik R.string.lockpassword_confirm_your_pin_header, 240c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik R.string.lockpassword_ok_label), 241c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 242c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik ConfirmWrong(R.string.lockpassword_confirm_passwords_dont_match, 243c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik R.string.lockpassword_confirm_pins_dont_match, 244c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik R.string.lockpassword_continue_label); 245c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 246c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) { 247c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik this.alphaHint = hintInAlpha; 24847d40324272ae39af0872bf5cbf27e1800478021Mason Tang this.numericHint = hintInNumeric; 249bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang this.buttonText = nextButtonText; 25047d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 25147d40324272ae39af0872bf5cbf27e1800478021Mason Tang 25247d40324272ae39af0872bf5cbf27e1800478021Mason Tang public final int alphaHint; 253bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public final int numericHint; 254bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang public final int buttonText; 25547d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 256c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 257c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik // required constructor for fragments 258c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public ChooseLockPasswordFragment() { 259c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 260c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 261c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 26247d40324272ae39af0872bf5cbf27e1800478021Mason Tang @Override 26347d40324272ae39af0872bf5cbf27e1800478021Mason Tang public void onCreate(Bundle savedInstanceState) { 26447d40324272ae39af0872bf5cbf27e1800478021Mason Tang super.onCreate(savedInstanceState); 26547d40324272ae39af0872bf5cbf27e1800478021Mason Tang mLockPatternUtils = new LockPatternUtils(getActivity()); 26647d40324272ae39af0872bf5cbf27e1800478021Mason Tang Intent intent = getActivity().getIntent(); 26747d40324272ae39af0872bf5cbf27e1800478021Mason Tang if (!(getActivity() instanceof ChooseLockPassword)) { 26847d40324272ae39af0872bf5cbf27e1800478021Mason Tang throw new SecurityException("Fragment contained in wrong activity"); 26947d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 270c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik // Only take this argument into account if it belongs to the current profile. 271c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras()); 272c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik processPasswordRequirements(intent); 273c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity()); 274c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mHideDrawer = getActivity().getIntent().getBooleanExtra(EXTRA_HIDE_DRAWER, false); 275c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 276c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (intent.getBooleanExtra( 277c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT, false)) { 278c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik SaveAndFinishWorker w = new SaveAndFinishWorker(); 279c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik final boolean required = getActivity().getIntent().getBooleanExtra( 280c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true); 281c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik String current = intent.getStringExtra( 282c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 283c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik w.setBlocking(true); 284c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik w.setListener(this); 28547d40324272ae39af0872bf5cbf27e1800478021Mason Tang w.start(mChooseLockSettingsHelper.utils(), required, 286c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik false, 0, current, current, mRequestedQuality, mUserId); 287c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 288c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mTextChangedHandler = new TextChangedHandler(); 289c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 290c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 291c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik @Override 292c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public View onCreateView(LayoutInflater inflater, ViewGroup container, 293c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik Bundle savedInstanceState) { 294c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik return inflater.inflate(R.layout.choose_lock_password, container, false); 295bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 296c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 297c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik @Override 298c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public void onViewCreated(View view, Bundle savedInstanceState) { 299c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik super.onViewCreated(view, savedInstanceState); 300c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 30147d40324272ae39af0872bf5cbf27e1800478021Mason Tang mCancelButton = (Button) view.findViewById(R.id.cancel_button); 30247d40324272ae39af0872bf5cbf27e1800478021Mason Tang mCancelButton.setOnClickListener(this); 303c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mNextButton = (Button) view.findViewById(R.id.next_button); 304bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang mNextButton.setOnClickListener(this); 305e0cb5ba884c52e9d36875fb4a9ebdf40a81cb642Michael Chan 306c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality 30721a183875fbbfa54f5a2a87779888a5fb7d1af44Erik || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality 30821a183875fbbfa54f5a2a87779888a5fb7d1af44Erik || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality; 30947d40324272ae39af0872bf5cbf27e1800478021Mason Tang 31047d40324272ae39af0872bf5cbf27e1800478021Mason Tang setupPasswordRequirementsView(view); 31121a183875fbbfa54f5a2a87779888a5fb7d1af44Erik 312ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity())); 31321a183875fbbfa54f5a2a87779888a5fb7d1af44Erik mPasswordEntry = (EditText) view.findViewById(R.id.password_entry); 31447d40324272ae39af0872bf5cbf27e1800478021Mason Tang mPasswordEntry.setOnEditorActionListener(this); 315bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang mPasswordEntry.addTextChangedListener(this); 316bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang mPasswordEntry.requestFocus(); 31747d40324272ae39af0872bf5cbf27e1800478021Mason Tang mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry); 3180c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa 31947d40324272ae39af0872bf5cbf27e1800478021Mason Tang final Activity activity = getActivity(); 320ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan mHeaderText = (TextView) view.findViewById(R.id.headerText); 3210c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa 322ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan int currentType = mPasswordEntry.getInputType(); 3230c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa mPasswordEntry.setInputType(mIsAlphaMode ? currentType 3240c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD)); 3250c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa 3260c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa Intent intent = getActivity().getIntent(); 32747d40324272ae39af0872bf5cbf27e1800478021Mason Tang final boolean confirmCredentials = intent.getBooleanExtra( 3280c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa ChooseLockGeneric.CONFIRM_CREDENTIALS, true); 32947d40324272ae39af0872bf5cbf27e1800478021Mason Tang mCurrentPassword = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 3300c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa mHasChallenge = intent.getBooleanExtra( 3310c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false); 3320c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0); 3330c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa if (savedInstanceState == null) { 3340c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa updateStage(Stage.Introduction); 335bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang if (confirmCredentials) { 336bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, 3370c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa getString(R.string.unlock_set_unlock_launch_picker_title), true, 3380c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa mUserId); 3390c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa } 3400c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa } else { 3410c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa // restore from previous state 3420c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN); 343bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang final String state = savedInstanceState.getString(KEY_UI_STAGE); 3440c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa if (state != null) { 345bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang mUiStage = Stage.valueOf(state); 346bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang updateStage(mUiStage); 347ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan } 34847d40324272ae39af0872bf5cbf27e1800478021Mason Tang 34947d40324272ae39af0872bf5cbf27e1800478021Mason Tang if (mCurrentPassword == null) { 35047d40324272ae39af0872bf5cbf27e1800478021Mason Tang mCurrentPassword = savedInstanceState.getString(KEY_CURRENT_PASSWORD); 35147d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 35247d40324272ae39af0872bf5cbf27e1800478021Mason Tang 35347d40324272ae39af0872bf5cbf27e1800478021Mason Tang // Re-attach to the exiting worker if there is one. 354ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan mSaveAndFinishWorker = (SaveAndFinishWorker) getFragmentManager().findFragmentByTag( 355ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan FRAGMENT_TAG_SAVE_AND_FINISH); 356ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan } 357ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan 358ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan // Workaround to show one password requirement below EditText when IME is shown. 359ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan // By adding an inset to the edit text background, we make the EditText occupy more 360ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan // vertical space, and the keyboard will then avoid hiding it. We have also set 361ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan // negative margin in the layout below in order to have them show in the correct 362ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan // position. 363ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan final int visibleVerticalSpaceBelowPassword = 364ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan getResources().getDimensionPixelOffset( 365bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang R.dimen.visible_vertical_space_below_password); 366bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang InsetDrawable drawable = 36747d40324272ae39af0872bf5cbf27e1800478021Mason Tang new InsetDrawable( 36847d40324272ae39af0872bf5cbf27e1800478021Mason Tang mPasswordEntry.getBackground(), 0, 0, 0, visibleVerticalSpaceBelowPassword); 36947d40324272ae39af0872bf5cbf27e1800478021Mason Tang mPasswordEntry.setBackgroundDrawable(drawable); 37047d40324272ae39af0872bf5cbf27e1800478021Mason Tang LinearLayout bottomContainer = (LinearLayout) view.findViewById(R.id.bottom_container); 37147d40324272ae39af0872bf5cbf27e1800478021Mason Tang LinearLayout.LayoutParams bottomContainerLp = 37247d40324272ae39af0872bf5cbf27e1800478021Mason Tang (LinearLayout.LayoutParams) bottomContainer.getLayoutParams(); 373c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik bottomContainerLp.setMargins(0, -visibleVerticalSpaceBelowPassword, 0, 0); 374c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 375c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (activity instanceof SettingsActivity) { 376c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik final SettingsActivity sa = (SettingsActivity) activity; 377c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik int id = mIsAlphaMode ? R.string.lockpassword_choose_your_password_header 378c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik : R.string.lockpassword_choose_your_pin_header; 379c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik CharSequence title = getText(id); 380c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik sa.setTitle(title); 381c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik ((GlifLayout) view).setHeaderText(title); 382c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 383c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 384c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 385c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik private void setupPasswordRequirementsView(View view) { 386c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik // Construct passwordRequirements and requirementDescriptions. 387c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik List<Integer> passwordRequirements = new ArrayList<>(); 388c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik List<String> requirementDescriptions = new ArrayList<>(); 389c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (mPasswordMinUpperCase > 0) { 390c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik passwordRequirements.add(MIN_UPPER_LETTERS_IN_PASSWORD); 391c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik requirementDescriptions.add(getResources().getQuantityString( 392c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase, 393c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mPasswordMinUpperCase)); 394c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 395c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (mPasswordMinLowerCase > 0) { 396c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik passwordRequirements.add(MIN_LOWER_LETTERS_IN_PASSWORD); 397c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik requirementDescriptions.add(getResources().getQuantityString( 398c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase, 399c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mPasswordMinLowerCase)); 400c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 401c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (mPasswordMinLetters > 0) { 402c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (mPasswordMinLetters > mPasswordMinUpperCase + mPasswordMinLowerCase) { 403c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik passwordRequirements.add(MIN_LETTER_IN_PASSWORD); 404c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik requirementDescriptions.add(getResources().getQuantityString( 405c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters, 406c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mPasswordMinLetters)); 407c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 408c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 409c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (mPasswordMinNumeric > 0) { 410c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik passwordRequirements.add(MIN_NUMBER_IN_PASSWORD); 411c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik requirementDescriptions.add(getResources().getQuantityString( 412c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric, 413c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mPasswordMinNumeric)); 414c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 415c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (mPasswordMinSymbols > 0) { 416c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik passwordRequirements.add(MIN_SYMBOLS_IN_PASSWORD); 417c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik requirementDescriptions.add(getResources().getQuantityString( 418c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols, 4193ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang mPasswordMinSymbols)); 420bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 4213ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang if (mPasswordMinNonLetter > 0) { 4223ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang if (mPasswordMinNonLetter > mPasswordMinNumeric + mPasswordMinSymbols) { 4233ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang passwordRequirements.add(MIN_NON_LETTER_IN_PASSWORD); 4243ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang requirementDescriptions.add(getResources().getQuantityString( 4253ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter, 4263ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang 4273ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang mPasswordMinNonLetter)); 4283ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang } 4293ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang } 4303ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang // Convert list to array. 4313ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang mPasswordRequirements = passwordRequirements.stream().mapToInt(i -> i).toArray(); 4323ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang mPasswordRestrictionView = 4333ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang (RecyclerView) view.findViewById(R.id.password_requirements_view); 4343ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang mPasswordRestrictionView.setLayoutManager(new LinearLayoutManager(getActivity())); 4353ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang mPasswordRequirementAdapter = new PasswordRequirementAdapter(); 4363ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang mPasswordRestrictionView.setAdapter(mPasswordRequirementAdapter); 4373ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang } 4383ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang 4393ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang @Override 4403ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang protected int getMetricsCategory() { 441bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang return MetricsEvent.CHOOSE_LOCK_PASSWORD; 442bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 443bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 444 @Override 445 public void onResume() { 446 super.onResume(); 447 updateStage(mUiStage); 448 if (mSaveAndFinishWorker != null) { 449 mSaveAndFinishWorker.setListener(this); 450 } else { 451 mPasswordEntry.requestFocus(); 452 } 453 } 454 455 @Override 456 public void onPause() { 457 if (mSaveAndFinishWorker != null) { 458 mSaveAndFinishWorker.setListener(null); 459 } 460 super.onPause(); 461 } 462 463 @Override 464 public void onSaveInstanceState(Bundle outState) { 465 super.onSaveInstanceState(outState); 466 outState.putString(KEY_UI_STAGE, mUiStage.name()); 467 outState.putString(KEY_FIRST_PIN, mFirstPin); 468 outState.putString(KEY_CURRENT_PASSWORD, mCurrentPassword); 469 } 470 471 @Override 472 public void onActivityResult(int requestCode, int resultCode, 473 Intent data) { 474 super.onActivityResult(requestCode, resultCode, data); 475 switch (requestCode) { 476 case CONFIRM_EXISTING_REQUEST: 477 if (resultCode != Activity.RESULT_OK) { 478 getActivity().setResult(RESULT_FINISHED); 479 getActivity().finish(); 480 } else { 481 mCurrentPassword = data.getStringExtra( 482 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 483 } 484 break; 485 } 486 } 487 488 protected Intent getRedactionInterstitialIntent(Context context) { 489 return RedactionInterstitial.createStartIntent(context, mUserId); 490 } 491 492 protected void updateStage(Stage stage) { 493 final Stage previousStage = mUiStage; 494 mUiStage = stage; 495 updateUi(); 496 497 // If the stage changed, announce the header for accessibility. This 498 // is a no-op when accessibility is disabled. 499 if (previousStage != stage) { 500 mHeaderText.announceForAccessibility(mHeaderText.getText()); 501 } 502 } 503 504 /** 505 * Read the requirements from {@link DevicePolicyManager} and intent and aggregate them. 506 * 507 * @param intent the incoming intent 508 */ 509 private void processPasswordRequirements(Intent intent) { 510 final int dpmPasswordQuality = mLockPatternUtils.getRequestedPasswordQuality(mUserId); 511 mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, 512 mRequestedQuality), dpmPasswordQuality); 513 mPasswordMinLength = Math.max(Math.max( 514 LockPatternUtils.MIN_LOCK_PASSWORD_SIZE, 515 intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength)), 516 mLockPatternUtils.getRequestedMinimumPasswordLength(mUserId)); 517 mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength); 518 mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY, 519 mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters( 520 mUserId)); 521 mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY, 522 mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase( 523 mUserId)); 524 mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY, 525 mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase( 526 mUserId)); 527 mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY, 528 mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric( 529 mUserId)); 530 mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY, 531 mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols( 532 mUserId)); 533 mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY, 534 mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter( 535 mUserId)); 536 537 // Modify the value based on dpm policy. 538 switch (dpmPasswordQuality) { 539 case PASSWORD_QUALITY_ALPHABETIC: 540 if (mPasswordMinLetters == 0) { 541 mPasswordMinLetters = 1; 542 } 543 break; 544 case PASSWORD_QUALITY_ALPHANUMERIC: 545 if (mPasswordMinLetters == 0) { 546 mPasswordMinLetters = 1; 547 } 548 if (mPasswordMinNumeric == 0) { 549 mPasswordMinNumeric = 1; 550 } 551 break; 552 case PASSWORD_QUALITY_COMPLEX: 553 // Reserve all the requirements. 554 break; 555 default: 556 mPasswordMinNumeric = 0; 557 mPasswordMinLetters = 0; 558 mPasswordMinUpperCase = 0; 559 mPasswordMinLowerCase = 0; 560 mPasswordMinSymbols = 0; 561 mPasswordMinNonLetter = 0; 562 } 563 mPasswordMinLengthToFulfillAllPolicies = getMinLengthToFulfillAllPolicies(); 564 } 565 566 /** 567 * Validates PIN and returns the validation result. 568 * 569 * @param password the raw password the user typed in 570 * @return the validation result. 571 */ 572 private int validatePassword(String password) { 573 int errorCode = NO_ERROR; 574 575 if (password.length() < mPasswordMinLength) { 576 if (mPasswordMinLength > mPasswordMinLengthToFulfillAllPolicies) { 577 errorCode |= TOO_SHORT; 578 } 579 } else if (password.length() > mPasswordMaxLength) { 580 errorCode |= TOO_LONG; 581 } else { 582 // The length requirements are fulfilled. 583 if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) { 584 // Check for repeated characters or sequences (e.g. '1234', '0000', '2468') 585 final int sequence = LockPatternUtils.maxLengthSequence(password); 586 if (sequence > LockPatternUtils.MAX_ALLOWED_SEQUENCE) { 587 errorCode |= CONTAIN_SEQUENTIAL_DIGITS; 588 } 589 } 590 // Is the password recently used? 591 if (mLockPatternUtils.checkPasswordHistory(password, mUserId)) { 592 errorCode |= RECENTLY_USED; 593 } 594 } 595 596 // Count different types of character. 597 int letters = 0; 598 int numbers = 0; 599 int lowercase = 0; 600 int symbols = 0; 601 int uppercase = 0; 602 int nonletter = 0; 603 for (int i = 0; i < password.length(); i++) { 604 char c = password.charAt(i); 605 // allow non control Latin-1 characters only 606 if (c < 32 || c > 127) { 607 errorCode |= CONTAIN_INVALID_CHARACTERS; 608 continue; 609 } 610 if (c >= '0' && c <= '9') { 611 numbers++; 612 nonletter++; 613 } else if (c >= 'A' && c <= 'Z') { 614 letters++; 615 uppercase++; 616 } else if (c >= 'a' && c <= 'z') { 617 letters++; 618 lowercase++; 619 } else { 620 symbols++; 621 nonletter++; 622 } 623 } 624 625 // Ensure no non-digits if we are requesting numbers. This shouldn't be possible unless 626 // user finds some way to bring up soft keyboard. 627 if (mRequestedQuality == PASSWORD_QUALITY_NUMERIC 628 || mRequestedQuality == PASSWORD_QUALITY_NUMERIC_COMPLEX) { 629 if (letters > 0 || symbols > 0) { 630 errorCode |= CONTAIN_NON_DIGITS; 631 } 632 } 633 634 // Check the requirements one by one. 635 for (int i = 0; i < mPasswordRequirements.length; i++) { 636 int passwordRestriction = mPasswordRequirements[i]; 637 switch (passwordRestriction) { 638 case MIN_LETTER_IN_PASSWORD: 639 if (letters < mPasswordMinLetters) { 640 errorCode |= NOT_ENOUGH_LETTER; 641 } 642 break; 643 case MIN_UPPER_LETTERS_IN_PASSWORD: 644 if (uppercase < mPasswordMinUpperCase) { 645 errorCode |= NOT_ENOUGH_UPPER_CASE; 646 } 647 break; 648 case MIN_LOWER_LETTERS_IN_PASSWORD: 649 if (lowercase < mPasswordMinLowerCase) { 650 errorCode |= NOT_ENOUGH_LOWER_CASE; 651 } 652 break; 653 case MIN_SYMBOLS_IN_PASSWORD: 654 if (symbols < mPasswordMinSymbols) { 655 errorCode |= NOT_ENOUGH_SYMBOLS; 656 } 657 break; 658 case MIN_NUMBER_IN_PASSWORD: 659 if (numbers < mPasswordMinNumeric) { 660 errorCode |= NOT_ENOUGH_DIGITS; 661 } 662 break; 663 case MIN_NON_LETTER_IN_PASSWORD: 664 if (nonletter < mPasswordMinNonLetter) { 665 errorCode |= NOT_ENOUGH_NON_LETTER; 666 } 667 break; 668 } 669 } 670 return errorCode; 671 } 672 673 public void handleNext() { 674 if (mSaveAndFinishWorker != null) return; 675 mChosenPassword = mPasswordEntry.getText().toString(); 676 if (TextUtils.isEmpty(mChosenPassword)) { 677 return; 678 } 679 if (mUiStage == Stage.Introduction) { 680 if (validatePassword(mChosenPassword) == NO_ERROR) { 681 mFirstPin = mChosenPassword; 682 mPasswordEntry.setText(""); 683 updateStage(Stage.NeedToConfirm); 684 } 685 } else if (mUiStage == Stage.NeedToConfirm) { 686 if (mFirstPin.equals(mChosenPassword)) { 687 startSaveAndFinish(); 688 } else { 689 CharSequence tmp = mPasswordEntry.getText(); 690 if (tmp != null) { 691 Selection.setSelection((Spannable) tmp, 0, tmp.length()); 692 } 693 updateStage(Stage.ConfirmWrong); 694 } 695 } 696 } 697 698 protected void setNextEnabled(boolean enabled) { 699 mNextButton.setEnabled(enabled); 700 } 701 702 protected void setNextText(int text) { 703 mNextButton.setText(text); 704 } 705 706 public void onClick(View v) { 707 switch (v.getId()) { 708 case R.id.next_button: 709 handleNext(); 710 break; 711 712 case R.id.cancel_button: 713 getActivity().finish(); 714 break; 715 } 716 } 717 718 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 719 // Check if this was the result of hitting the enter or "done" key 720 if (actionId == EditorInfo.IME_NULL 721 || actionId == EditorInfo.IME_ACTION_DONE 722 || actionId == EditorInfo.IME_ACTION_NEXT) { 723 handleNext(); 724 return true; 725 } 726 return false; 727 } 728 729 /** 730 * @param errorCode error code returned from {@link #validatePassword(String)}. 731 * @return an array of messages describing the error, important messages come first. 732 */ 733 private String[] convertErrorCodeToMessages(int errorCode) { 734 List<String> messages = new ArrayList<>(); 735 if ((errorCode & CONTAIN_INVALID_CHARACTERS) > 0) { 736 messages.add(getString(R.string.lockpassword_illegal_character)); 737 } 738 if ((errorCode & CONTAIN_NON_DIGITS) > 0) { 739 messages.add(getString(R.string.lockpassword_pin_contains_non_digits)); 740 } 741 if ((errorCode & NOT_ENOUGH_UPPER_CASE) > 0) { 742 messages.add(getResources().getQuantityString( 743 R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase, 744 mPasswordMinUpperCase)); 745 } 746 if ((errorCode & NOT_ENOUGH_LOWER_CASE) > 0) { 747 messages.add(getResources().getQuantityString( 748 R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase, 749 mPasswordMinLowerCase)); 750 } 751 if ((errorCode & NOT_ENOUGH_LETTER) > 0) { 752 messages.add(getResources().getQuantityString( 753 R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters, 754 mPasswordMinLetters)); 755 } 756 if ((errorCode & NOT_ENOUGH_DIGITS) > 0) { 757 messages.add(getResources().getQuantityString( 758 R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric, 759 mPasswordMinNumeric)); 760 } 761 if ((errorCode & NOT_ENOUGH_SYMBOLS) > 0) { 762 messages.add(getResources().getQuantityString( 763 R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols, 764 mPasswordMinSymbols)); 765 } 766 if ((errorCode & NOT_ENOUGH_NON_LETTER) > 0) { 767 messages.add(getResources().getQuantityString( 768 R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter, 769 mPasswordMinNonLetter)); 770 } 771 if ((errorCode & TOO_SHORT) > 0) { 772 messages.add(getString(mIsAlphaMode ? 773 R.string.lockpassword_password_too_short 774 : R.string.lockpassword_pin_too_short, mPasswordMinLength)); 775 } 776 if ((errorCode & TOO_LONG) > 0) { 777 messages.add(getString(mIsAlphaMode ? 778 R.string.lockpassword_password_too_long 779 : R.string.lockpassword_pin_too_long, mPasswordMaxLength + 1)); 780 } 781 if ((errorCode & CONTAIN_SEQUENTIAL_DIGITS) > 0) { 782 messages.add(getString(R.string.lockpassword_pin_no_sequential_digits)); 783 } 784 if ((errorCode & RECENTLY_USED) > 0) { 785 messages.add(getString((mIsAlphaMode) ? R.string.lockpassword_password_recently_used 786 : R.string.lockpassword_pin_recently_used)); 787 } 788 return messages.toArray(new String[0]); 789 } 790 791 private int getMinLengthToFulfillAllPolicies() { 792 final int minLengthForLetters = Math.max(mPasswordMinLetters, 793 mPasswordMinUpperCase + mPasswordMinLowerCase); 794 final int minLengthForNonLetters = Math.max(mPasswordMinNonLetter, 795 mPasswordMinSymbols + mPasswordMinNumeric); 796 return minLengthForLetters + minLengthForNonLetters; 797 } 798 799 /** 800 * Update the hint based on current Stage and length of password entry 801 */ 802 private void updateUi() { 803 final boolean canInput = mSaveAndFinishWorker == null; 804 String password = mPasswordEntry.getText().toString(); 805 final int length = password.length(); 806 if (mUiStage == Stage.Introduction) { 807 mPasswordRestrictionView.setVisibility(View.VISIBLE); 808 final int errorCode = validatePassword(password); 809 String[] messages = convertErrorCodeToMessages(errorCode); 810 // Update the fulfillment of requirements. 811 mPasswordRequirementAdapter.setRequirements(messages); 812 // Enable/Disable the next button accordingly. 813 setNextEnabled(errorCode == NO_ERROR); 814 } else { 815 // Hide password requirement view when we are just asking user to confirm the pw. 816 mPasswordRestrictionView.setVisibility(View.GONE); 817 setHeaderText(getString( 818 mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint)); 819 setNextEnabled(canInput && length > 0); 820 } 821 setNextText(mUiStage.buttonText); 822 mPasswordEntryInputDisabler.setInputEnabled(canInput); 823 } 824 825 private void setHeaderText(String text) { 826 // Only set the text if it is different than the existing one to avoid announcing again. 827 if (!TextUtils.isEmpty(mHeaderText.getText()) 828 && mHeaderText.getText().toString().equals(text)) { 829 return; 830 } 831 mHeaderText.setText(text); 832 } 833 834 public void afterTextChanged(Editable s) { 835 // Changing the text while error displayed resets to NeedToConfirm state 836 if (mUiStage == Stage.ConfirmWrong) { 837 mUiStage = Stage.NeedToConfirm; 838 } 839 // Schedule the UI update. 840 mTextChangedHandler.notifyAfterTextChanged(); 841 } 842 843 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 844 845 } 846 847 public void onTextChanged(CharSequence s, int start, int before, int count) { 848 849 } 850 851 private void startSaveAndFinish() { 852 if (mSaveAndFinishWorker != null) { 853 Log.w(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker."); 854 return; 855 } 856 857 mPasswordEntryInputDisabler.setInputEnabled(false); 858 setNextEnabled(false); 859 860 mSaveAndFinishWorker = new SaveAndFinishWorker(); 861 mSaveAndFinishWorker.setListener(this); 862 863 getFragmentManager().beginTransaction().add(mSaveAndFinishWorker, 864 FRAGMENT_TAG_SAVE_AND_FINISH).commit(); 865 getFragmentManager().executePendingTransactions(); 866 867 final boolean required = getActivity().getIntent().getBooleanExtra( 868 EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true); 869 mSaveAndFinishWorker.start(mLockPatternUtils, required, mHasChallenge, mChallenge, 870 mChosenPassword, mCurrentPassword, mRequestedQuality, mUserId); 871 } 872 873 @Override 874 public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) { 875 getActivity().setResult(RESULT_FINISHED, resultData); 876 877 if (!wasSecureBefore) { 878 Intent intent = getRedactionInterstitialIntent(getActivity()); 879 if (intent != null) { 880 intent.putExtra(EXTRA_HIDE_DRAWER, mHideDrawer); 881 startActivity(intent); 882 } 883 } 884 getActivity().finish(); 885 } 886 887 class TextChangedHandler extends Handler { 888 private static final int ON_TEXT_CHANGED = 1; 889 private static final int DELAY_IN_MILLISECOND = 100; 890 891 /** 892 * With the introduction of delay, we batch processing the text changed event to reduce 893 * unnecessary UI updates. 894 */ 895 private void notifyAfterTextChanged() { 896 removeMessages(ON_TEXT_CHANGED); 897 sendEmptyMessageDelayed(ON_TEXT_CHANGED, DELAY_IN_MILLISECOND); 898 } 899 900 @Override 901 public void handleMessage(Message msg) { 902 if (msg.what == ON_TEXT_CHANGED) { 903 updateUi(); 904 } 905 } 906 } 907 } 908 909 private static class SaveAndFinishWorker extends SaveChosenLockWorkerBase { 910 911 private String mChosenPassword; 912 private String mCurrentPassword; 913 private int mRequestedQuality; 914 915 public void start(LockPatternUtils utils, boolean required, 916 boolean hasChallenge, long challenge, 917 String chosenPassword, String currentPassword, int requestedQuality, int userId) { 918 prepare(utils, required, hasChallenge, challenge, userId); 919 920 mChosenPassword = chosenPassword; 921 mCurrentPassword = currentPassword; 922 mRequestedQuality = requestedQuality; 923 mUserId = userId; 924 925 start(); 926 } 927 928 @Override 929 protected Intent saveAndVerifyInBackground() { 930 Intent result = null; 931 mUtils.saveLockPassword(mChosenPassword, mCurrentPassword, mRequestedQuality, 932 mUserId); 933 934 if (mHasChallenge) { 935 byte[] token; 936 try { 937 token = mUtils.verifyPassword(mChosenPassword, mChallenge, mUserId); 938 } catch (RequestThrottledException e) { 939 token = null; 940 } 941 942 if (token == null) { 943 Log.e(TAG, "critical: no token returned for known good password."); 944 } 945 946 result = new Intent(); 947 result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token); 948 } 949 950 return result; 951 } 952 } 953} 954