2 * Copyright (C) 2010 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.settings;
19 import com.android.internal.logging.MetricsLogger;
20 import com.android.internal.widget.LockPatternChecker;
21 import com.android.internal.widget.LockPatternUtils;
22 import com.android.internal.widget.PasswordEntryKeyboardHelper;
23 import com.android.internal.widget.PasswordEntryKeyboardView;
24 import com.android.internal.widget.TextViewInputDisabler;
25 import com.android.settings.notification.RedactionInterstitial;
27 import android.app.Activity;
28 import android.app.Fragment;
29 import android.app.admin.DevicePolicyManager;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.inputmethodservice.KeyboardView;
33 import android.os.AsyncTask;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.os.Message;
37 import android.os.UserHandle;
38 import android.text.Editable;
39 import android.text.InputType;
40 import android.text.Selection;
41 import android.text.Spannable;
42 import android.text.TextUtils;
43 import android.text.TextWatcher;
44 import android.util.Log;
45 import android.view.KeyEvent;
46 import android.view.LayoutInflater;
47 import android.view.View;
48 import android.view.ViewGroup;
49 import android.view.View.OnClickListener;
50 import android.view.inputmethod.EditorInfo;
51 import android.widget.Button;
52 import android.widget.TextView;
53 import android.widget.TextView.OnEditorActionListener;
55 public class ChooseLockPassword extends SettingsActivity {
56 public static final String PASSWORD_MIN_KEY = "lockscreen.password_min";
57 public static final String PASSWORD_MAX_KEY = "lockscreen.password_max";
58 public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters";
59 public static final String PASSWORD_MIN_LOWERCASE_KEY = "lockscreen.password_min_lowercase";
60 public static final String PASSWORD_MIN_UPPERCASE_KEY = "lockscreen.password_min_uppercase";
61 public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric";
62 public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols";
63 public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter";
65 private static final String TAG = "ChooseLockPassword";
68 public Intent getIntent() {
69 Intent modIntent = new Intent(super.getIntent());
70 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
74 public static Intent createIntent(Context context, int quality,
75 int minLength, final int maxLength, boolean requirePasswordToDecrypt,
76 boolean confirmCredentials) {
77 Intent intent = new Intent().setClass(context, ChooseLockPassword.class);
78 intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
79 intent.putExtra(PASSWORD_MIN_KEY, minLength);
80 intent.putExtra(PASSWORD_MAX_KEY, maxLength);
81 intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, confirmCredentials);
82 intent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, requirePasswordToDecrypt);
86 public static Intent createIntent(Context context, int quality,
87 int minLength, final int maxLength, boolean requirePasswordToDecrypt, String password) {
88 Intent intent = createIntent(context, quality, minLength, maxLength,
89 requirePasswordToDecrypt, false);
90 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password);
94 public static Intent createIntent(Context context, int quality,
95 int minLength, final int maxLength, boolean requirePasswordToDecrypt, long challenge) {
96 Intent intent = createIntent(context, quality, minLength, maxLength,
97 requirePasswordToDecrypt, false);
98 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
99 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
104 protected boolean isValidFragment(String fragmentName) {
105 if (ChooseLockPasswordFragment.class.getName().equals(fragmentName)) return true;
109 /* package */ Class<? extends Fragment> getFragmentClass() {
110 return ChooseLockPasswordFragment.class;
114 public void onCreate(Bundle savedInstanceState) {
115 // TODO: Fix on phones
116 // Disable IME on our window since we provide our own keyboard
117 //getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
118 //WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
119 super.onCreate(savedInstanceState);
120 CharSequence msg = getText(R.string.lockpassword_choose_your_password_header);
124 public static class ChooseLockPasswordFragment extends InstrumentedFragment
125 implements OnClickListener, OnEditorActionListener, TextWatcher {
126 private static final String KEY_FIRST_PIN = "first_pin";
127 private static final String KEY_UI_STAGE = "ui_stage";
128 private static final String KEY_CURRENT_PASSWORD = "current_password";
130 private String mCurrentPassword;
131 private boolean mHasChallenge;
132 private long mChallenge;
133 private TextView mPasswordEntry;
134 private TextViewInputDisabler mPasswordEntryInputDisabler;
135 private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE;
136 private int mPasswordMaxLength = 16;
137 private int mPasswordMinLetters = 0;
138 private int mPasswordMinUpperCase = 0;
139 private int mPasswordMinLowerCase = 0;
140 private int mPasswordMinSymbols = 0;
141 private int mPasswordMinNumeric = 0;
142 private int mPasswordMinNonLetter = 0;
143 private LockPatternUtils mLockPatternUtils;
144 private AsyncTask<?, ?, ?> mPendingLockCheck;
145 private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
146 private ChooseLockSettingsHelper mChooseLockSettingsHelper;
147 private Stage mUiStage = Stage.Introduction;
148 private boolean mDone = false;
149 private TextView mHeaderText;
150 private String mFirstPin;
151 private KeyboardView mKeyboardView;
152 private PasswordEntryKeyboardHelper mKeyboardHelper;
153 private boolean mIsAlphaMode;
154 private Button mCancelButton;
155 private Button mNextButton;
156 private static final int CONFIRM_EXISTING_REQUEST = 58;
157 static final int RESULT_FINISHED = RESULT_FIRST_USER;
158 private static final long ERROR_MESSAGE_TIMEOUT = 3000;
159 private static final int MSG_SHOW_ERROR = 1;
161 private Handler mHandler = new Handler() {
163 public void handleMessage(Message msg) {
164 if (msg.what == MSG_SHOW_ERROR) {
165 updateStage((Stage) msg.obj);
171 * Keep track internally of where the user is in choosing a pattern.
173 protected enum Stage {
175 Introduction(R.string.lockpassword_choose_your_password_header,
176 R.string.lockpassword_choose_your_pin_header,
177 R.string.lockpassword_continue_label),
179 NeedToConfirm(R.string.lockpassword_confirm_your_password_header,
180 R.string.lockpassword_confirm_your_pin_header,
181 R.string.lockpassword_ok_label),
183 ConfirmWrong(R.string.lockpassword_confirm_passwords_dont_match,
184 R.string.lockpassword_confirm_pins_dont_match,
185 R.string.lockpassword_continue_label);
187 Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) {
188 this.alphaHint = hintInAlpha;
189 this.numericHint = hintInNumeric;
190 this.buttonText = nextButtonText;
193 public final int alphaHint;
194 public final int numericHint;
195 public final int buttonText;
198 // required constructor for fragments
199 public ChooseLockPasswordFragment() {
204 public void onCreate(Bundle savedInstanceState) {
205 super.onCreate(savedInstanceState);
206 mLockPatternUtils = new LockPatternUtils(getActivity());
207 Intent intent = getActivity().getIntent();
208 if (!(getActivity() instanceof ChooseLockPassword)) {
209 throw new SecurityException("Fragment contained in wrong activity");
211 mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
212 mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality(
213 UserHandle.myUserId()));
214 mPasswordMinLength = Math.max(Math.max(
215 LockPatternUtils.MIN_LOCK_PASSWORD_SIZE,
216 intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength)),
217 mLockPatternUtils.getRequestedMinimumPasswordLength(UserHandle.myUserId()));
218 mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
219 mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY,
220 mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters(
221 UserHandle.myUserId()));
222 mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
223 mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase(
224 UserHandle.myUserId()));
225 mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
226 mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase(
227 UserHandle.myUserId()));
228 mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
229 mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric(
230 UserHandle.myUserId()));
231 mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
232 mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols(
233 UserHandle.myUserId()));
234 mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
235 mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter(
236 UserHandle.myUserId()));
238 mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
242 public View onCreateView(LayoutInflater inflater, ViewGroup container,
243 Bundle savedInstanceState) {
244 return inflater.inflate(R.layout.choose_lock_password, container, false);
248 public void onViewCreated(View view, Bundle savedInstanceState) {
249 super.onViewCreated(view, savedInstanceState);
251 mCancelButton = (Button) view.findViewById(R.id.cancel_button);
252 mCancelButton.setOnClickListener(this);
253 mNextButton = (Button) view.findViewById(R.id.next_button);
254 mNextButton.setOnClickListener(this);
256 mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
257 || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality
258 || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
259 mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
260 mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
261 mPasswordEntry.setOnEditorActionListener(this);
262 mPasswordEntry.addTextChangedListener(this);
263 mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
265 final Activity activity = getActivity();
266 mKeyboardHelper = new PasswordEntryKeyboardHelper(activity,
267 mKeyboardView, mPasswordEntry);
268 mKeyboardHelper.setKeyboardMode(mIsAlphaMode ?
269 PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
270 : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
272 mHeaderText = (TextView) view.findViewById(R.id.headerText);
273 mKeyboardView.requestFocus();
275 int currentType = mPasswordEntry.getInputType();
276 mPasswordEntry.setInputType(mIsAlphaMode ? currentType
277 : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
279 Intent intent = getActivity().getIntent();
280 final boolean confirmCredentials = intent.getBooleanExtra(
281 ChooseLockGeneric.CONFIRM_CREDENTIALS, true);
282 mCurrentPassword = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
283 mHasChallenge = intent.getBooleanExtra(
284 ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
285 mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
286 if (savedInstanceState == null) {
287 updateStage(Stage.Introduction);
288 if (confirmCredentials) {
289 mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
290 getString(R.string.unlock_set_unlock_launch_picker_title), true);
293 // restore from previous state
294 mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
295 final String state = savedInstanceState.getString(KEY_UI_STAGE);
297 mUiStage = Stage.valueOf(state);
298 updateStage(mUiStage);
301 if (mCurrentPassword == null) {
302 mCurrentPassword = savedInstanceState.getString(KEY_CURRENT_PASSWORD);
306 if (activity instanceof SettingsActivity) {
307 final SettingsActivity sa = (SettingsActivity) activity;
308 int id = mIsAlphaMode ? R.string.lockpassword_choose_your_password_header
309 : R.string.lockpassword_choose_your_pin_header;
310 CharSequence title = getText(id);
316 protected int getMetricsCategory() {
317 return MetricsLogger.CHOOSE_LOCK_PASSWORD;
321 public void onResume() {
323 updateStage(mUiStage);
324 mPasswordEntryInputDisabler.setInputEnabled(true);
325 mKeyboardView.requestFocus();
329 public void onPause() {
330 mHandler.removeMessages(MSG_SHOW_ERROR);
331 if (mPendingLockCheck != null) {
332 mPendingLockCheck.cancel(false);
333 mPendingLockCheck = null;
340 public void onSaveInstanceState(Bundle outState) {
341 super.onSaveInstanceState(outState);
342 outState.putString(KEY_UI_STAGE, mUiStage.name());
343 outState.putString(KEY_FIRST_PIN, mFirstPin);
344 outState.putString(KEY_CURRENT_PASSWORD, mCurrentPassword);
348 public void onActivityResult(int requestCode, int resultCode,
350 super.onActivityResult(requestCode, resultCode, data);
351 switch (requestCode) {
352 case CONFIRM_EXISTING_REQUEST:
353 if (resultCode != Activity.RESULT_OK) {
354 getActivity().setResult(RESULT_FINISHED);
355 getActivity().finish();
357 mCurrentPassword = data.getStringExtra(
358 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
364 protected Intent getRedactionInterstitialIntent(Context context) {
365 return RedactionInterstitial.createStartIntent(context);
368 protected void updateStage(Stage stage) {
369 final Stage previousStage = mUiStage;
373 // If the stage changed, announce the header for accessibility. This
374 // is a no-op when accessibility is disabled.
375 if (previousStage != stage) {
376 mHeaderText.announceForAccessibility(mHeaderText.getText());
381 * Validates PIN and returns a message to display if PIN fails test.
382 * @param password the raw password the user typed in
383 * @return error message to show to user or null if password is OK
385 private String validatePassword(String password) {
386 if (password.length() < mPasswordMinLength) {
387 return getString(mIsAlphaMode ?
388 R.string.lockpassword_password_too_short
389 : R.string.lockpassword_pin_too_short, mPasswordMinLength);
391 if (password.length() > mPasswordMaxLength) {
392 return getString(mIsAlphaMode ?
393 R.string.lockpassword_password_too_long
394 : R.string.lockpassword_pin_too_long, mPasswordMaxLength + 1);
402 for (int i = 0; i < password.length(); i++) {
403 char c = password.charAt(i);
404 // allow non control Latin-1 characters only
405 if (c < 32 || c > 127) {
406 return getString(R.string.lockpassword_illegal_character);
408 if (c >= '0' && c <= '9') {
411 } else if (c >= 'A' && c <= 'Z') {
414 } else if (c >= 'a' && c <= 'z') {
422 if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality
423 || DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX == mRequestedQuality) {
424 if (letters > 0 || symbols > 0) {
425 // This shouldn't be possible unless user finds some way to bring up
427 return getString(R.string.lockpassword_pin_contains_non_digits);
429 // Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
430 final int sequence = LockPatternUtils.maxLengthSequence(password);
431 if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX == mRequestedQuality
432 && sequence > LockPatternUtils.MAX_ALLOWED_SEQUENCE) {
433 return getString(R.string.lockpassword_pin_no_sequential_digits);
435 } else if (DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality) {
436 if (letters < mPasswordMinLetters) {
437 return String.format(getResources().getQuantityString(
438 R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters),
439 mPasswordMinLetters);
440 } else if (numbers < mPasswordMinNumeric) {
441 return String.format(getResources().getQuantityString(
442 R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric),
443 mPasswordMinNumeric);
444 } else if (lowercase < mPasswordMinLowerCase) {
445 return String.format(getResources().getQuantityString(
446 R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase),
447 mPasswordMinLowerCase);
448 } else if (uppercase < mPasswordMinUpperCase) {
449 return String.format(getResources().getQuantityString(
450 R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase),
451 mPasswordMinUpperCase);
452 } else if (symbols < mPasswordMinSymbols) {
453 return String.format(getResources().getQuantityString(
454 R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols),
455 mPasswordMinSymbols);
456 } else if (nonletter < mPasswordMinNonLetter) {
457 return String.format(getResources().getQuantityString(
458 R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter),
459 mPasswordMinNonLetter);
462 final boolean alphabetic = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
463 == mRequestedQuality;
464 final boolean alphanumeric = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
465 == mRequestedQuality;
466 if ((alphabetic || alphanumeric) && letters == 0) {
467 return getString(R.string.lockpassword_password_requires_alpha);
469 if (alphanumeric && numbers == 0) {
470 return getString(R.string.lockpassword_password_requires_digit);
473 if(mLockPatternUtils.checkPasswordHistory(password, UserHandle.myUserId())) {
474 return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used
475 : R.string.lockpassword_pin_recently_used);
481 public void handleNext() {
484 final String pin = mPasswordEntry.getText().toString();
485 if (TextUtils.isEmpty(pin)) {
488 String errorMsg = null;
489 if (mUiStage == Stage.Introduction) {
490 errorMsg = validatePassword(pin);
491 if (errorMsg == null) {
493 mPasswordEntry.setText("");
494 updateStage(Stage.NeedToConfirm);
496 } else if (mUiStage == Stage.NeedToConfirm) {
497 if (mFirstPin.equals(pin)) {
498 boolean wasSecureBefore = mLockPatternUtils.isSecure(UserHandle.myUserId());
499 final boolean required = getActivity().getIntent().getBooleanExtra(
500 EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
501 mLockPatternUtils.setCredentialRequiredToDecrypt(required);
502 mLockPatternUtils.saveLockPassword(pin, mCurrentPassword, mRequestedQuality,
503 UserHandle.myUserId());
506 startVerifyPassword(pin, wasSecureBefore);
509 getActivity().setResult(RESULT_FINISHED);
511 finishConfirmStage(wasSecureBefore);
513 CharSequence tmp = mPasswordEntry.getText();
515 Selection.setSelection((Spannable) tmp, 0, tmp.length());
517 updateStage(Stage.ConfirmWrong);
520 if (errorMsg != null) {
521 showError(errorMsg, mUiStage);
525 private void startVerifyPassword(final String pin, final boolean wasSecureBefore) {
526 mPasswordEntryInputDisabler.setInputEnabled(false);
527 setNextEnabled(false);
528 if (mPendingLockCheck != null) {
529 mPendingLockCheck.cancel(false);
532 mPendingLockCheck = LockPatternChecker.verifyPassword(
536 UserHandle.myUserId(),
537 new LockPatternChecker.OnVerifyCallback() {
539 public void onVerified(byte[] token, int timeoutMs) {
541 Log.e(TAG, "critical: no token returned from known good password");
544 mPasswordEntryInputDisabler.setInputEnabled(true);
545 setNextEnabled(true);
546 mPendingLockCheck = null;
548 Intent intent = new Intent();
550 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
552 getActivity().setResult(RESULT_FINISHED, intent);
553 finishConfirmStage(wasSecureBefore);
558 private void finishConfirmStage(boolean wasSecureBefore) {
559 getActivity().finish();
561 if (!wasSecureBefore) {
562 startActivity(getRedactionInterstitialIntent(getActivity()));
566 protected void setNextEnabled(boolean enabled) {
567 mNextButton.setEnabled(enabled);
570 protected void setNextText(int text) {
571 mNextButton.setText(text);
574 public void onClick(View v) {
576 case R.id.next_button:
580 case R.id.cancel_button:
581 getActivity().finish();
586 private void showError(String msg, final Stage next) {
587 mHeaderText.setText(msg);
588 mHeaderText.announceForAccessibility(mHeaderText.getText());
589 Message mesg = mHandler.obtainMessage(MSG_SHOW_ERROR, next);
590 mHandler.removeMessages(MSG_SHOW_ERROR);
591 mHandler.sendMessageDelayed(mesg, ERROR_MESSAGE_TIMEOUT);
594 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
595 // Check if this was the result of hitting the enter or "done" key
596 if (actionId == EditorInfo.IME_NULL
597 || actionId == EditorInfo.IME_ACTION_DONE
598 || actionId == EditorInfo.IME_ACTION_NEXT) {
606 * Update the hint based on current Stage and length of password entry
608 private void updateUi() {
609 String password = mPasswordEntry.getText().toString();
610 final int length = password.length();
611 if (mUiStage == Stage.Introduction) {
612 if (length < mPasswordMinLength) {
613 String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short
614 : R.string.lockpassword_pin_too_short, mPasswordMinLength);
615 mHeaderText.setText(msg);
616 setNextEnabled(false);
618 String error = validatePassword(password);
620 mHeaderText.setText(error);
621 setNextEnabled(false);
623 mHeaderText.setText(R.string.lockpassword_press_continue);
624 setNextEnabled(true);
628 mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint);
629 setNextEnabled(length > 0);
631 setNextText(mUiStage.buttonText);
634 public void afterTextChanged(Editable s) {
635 // Changing the text while error displayed resets to NeedToConfirm state
636 if (mUiStage == Stage.ConfirmWrong) {
637 mUiStage = Stage.NeedToConfirm;
642 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
646 public void onTextChanged(CharSequence s, int start, int before, int count) {