OSDN Git Service

Merge "Clear only keystore credential entires" into mnc-dev
[android-x86/packages-apps-Settings.git] / src / com / android / settings / ChooseLockPassword.java
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.settings;
18
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;
26
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;
54
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";
64
65     private static final String TAG = "ChooseLockPassword";
66
67     @Override
68     public Intent getIntent() {
69         Intent modIntent = new Intent(super.getIntent());
70         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
71         return modIntent;
72     }
73
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);
83         return intent;
84     }
85
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);
91         return intent;
92     }
93
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);
100         return intent;
101     }
102
103     @Override
104     protected boolean isValidFragment(String fragmentName) {
105         if (ChooseLockPasswordFragment.class.getName().equals(fragmentName)) return true;
106         return false;
107     }
108
109     /* package */ Class<? extends Fragment> getFragmentClass() {
110         return ChooseLockPasswordFragment.class;
111     }
112
113     @Override
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);
121         setTitle(msg);
122     }
123
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";
129
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;
160
161         private Handler mHandler = new Handler() {
162             @Override
163             public void handleMessage(Message msg) {
164                 if (msg.what == MSG_SHOW_ERROR) {
165                     updateStage((Stage) msg.obj);
166                 }
167             }
168         };
169
170         /**
171          * Keep track internally of where the user is in choosing a pattern.
172          */
173         protected enum Stage {
174
175             Introduction(R.string.lockpassword_choose_your_password_header,
176                     R.string.lockpassword_choose_your_pin_header,
177                     R.string.lockpassword_continue_label),
178
179             NeedToConfirm(R.string.lockpassword_confirm_your_password_header,
180                     R.string.lockpassword_confirm_your_pin_header,
181                     R.string.lockpassword_ok_label),
182
183             ConfirmWrong(R.string.lockpassword_confirm_passwords_dont_match,
184                     R.string.lockpassword_confirm_pins_dont_match,
185                     R.string.lockpassword_continue_label);
186
187             Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) {
188                 this.alphaHint = hintInAlpha;
189                 this.numericHint = hintInNumeric;
190                 this.buttonText = nextButtonText;
191             }
192
193             public final int alphaHint;
194             public final int numericHint;
195             public final int buttonText;
196         }
197
198         // required constructor for fragments
199         public ChooseLockPasswordFragment() {
200
201         }
202
203         @Override
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");
210             }
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()));
237
238             mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
239         }
240
241         @Override
242         public View onCreateView(LayoutInflater inflater, ViewGroup container,
243                 Bundle savedInstanceState) {
244             return inflater.inflate(R.layout.choose_lock_password, container, false);
245         }
246
247         @Override
248         public void onViewCreated(View view, Bundle savedInstanceState) {
249             super.onViewCreated(view, savedInstanceState);
250
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);
255
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);
264
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);
271
272             mHeaderText = (TextView) view.findViewById(R.id.headerText);
273             mKeyboardView.requestFocus();
274
275             int currentType = mPasswordEntry.getInputType();
276             mPasswordEntry.setInputType(mIsAlphaMode ? currentType
277                     : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
278
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);
291                 }
292             } else {
293                 // restore from previous state
294                 mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
295                 final String state = savedInstanceState.getString(KEY_UI_STAGE);
296                 if (state != null) {
297                     mUiStage = Stage.valueOf(state);
298                     updateStage(mUiStage);
299                 }
300
301                 if (mCurrentPassword == null) {
302                     mCurrentPassword = savedInstanceState.getString(KEY_CURRENT_PASSWORD);
303                 }
304             }
305             mDone = false;
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);
311                 sa.setTitle(title);
312             }
313         }
314
315         @Override
316         protected int getMetricsCategory() {
317             return MetricsLogger.CHOOSE_LOCK_PASSWORD;
318         }
319
320         @Override
321         public void onResume() {
322             super.onResume();
323             updateStage(mUiStage);
324             mPasswordEntryInputDisabler.setInputEnabled(true);
325             mKeyboardView.requestFocus();
326         }
327
328         @Override
329         public void onPause() {
330             mHandler.removeMessages(MSG_SHOW_ERROR);
331             if (mPendingLockCheck != null) {
332                 mPendingLockCheck.cancel(false);
333                 mPendingLockCheck = null;
334             }
335
336             super.onPause();
337         }
338
339         @Override
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);
345         }
346
347         @Override
348         public void onActivityResult(int requestCode, int resultCode,
349                 Intent data) {
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();
356                     } else {
357                         mCurrentPassword = data.getStringExtra(
358                                 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
359                     }
360                     break;
361             }
362         }
363
364         protected Intent getRedactionInterstitialIntent(Context context) {
365             return RedactionInterstitial.createStartIntent(context);
366         }
367
368         protected void updateStage(Stage stage) {
369             final Stage previousStage = mUiStage;
370             mUiStage = stage;
371             updateUi();
372
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());
377             }
378         }
379
380         /**
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
384          */
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);
390             }
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);
395             }
396             int letters = 0;
397             int numbers = 0;
398             int lowercase = 0;
399             int symbols = 0;
400             int uppercase = 0;
401             int nonletter = 0;
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);
407                 }
408                 if (c >= '0' && c <= '9') {
409                     numbers++;
410                     nonletter++;
411                 } else if (c >= 'A' && c <= 'Z') {
412                     letters++;
413                     uppercase++;
414                 } else if (c >= 'a' && c <= 'z') {
415                     letters++;
416                     lowercase++;
417                 } else {
418                     symbols++;
419                     nonletter++;
420                 }
421             }
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
426                     // soft keyboard
427                     return getString(R.string.lockpassword_pin_contains_non_digits);
428                 }
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);
434                 }
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);
460                 }
461             } else {
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);
468                 }
469                 if (alphanumeric && numbers == 0) {
470                     return getString(R.string.lockpassword_password_requires_digit);
471                 }
472             }
473             if(mLockPatternUtils.checkPasswordHistory(password, UserHandle.myUserId())) {
474                 return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used
475                         : R.string.lockpassword_pin_recently_used);
476             }
477
478             return null;
479         }
480
481         public void handleNext() {
482             if (mDone) return;
483
484             final String pin = mPasswordEntry.getText().toString();
485             if (TextUtils.isEmpty(pin)) {
486                 return;
487             }
488             String errorMsg = null;
489             if (mUiStage == Stage.Introduction) {
490                 errorMsg = validatePassword(pin);
491                 if (errorMsg == null) {
492                     mFirstPin = pin;
493                     mPasswordEntry.setText("");
494                     updateStage(Stage.NeedToConfirm);
495                 }
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());
504
505                     if (mHasChallenge) {
506                         startVerifyPassword(pin, wasSecureBefore);
507                         return;
508                     } else {
509                         getActivity().setResult(RESULT_FINISHED);
510                     }
511                     finishConfirmStage(wasSecureBefore);
512                 } else {
513                     CharSequence tmp = mPasswordEntry.getText();
514                     if (tmp != null) {
515                         Selection.setSelection((Spannable) tmp, 0, tmp.length());
516                     }
517                     updateStage(Stage.ConfirmWrong);
518                 }
519             }
520             if (errorMsg != null) {
521                 showError(errorMsg, mUiStage);
522             }
523         }
524
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);
530             }
531
532             mPendingLockCheck = LockPatternChecker.verifyPassword(
533                     mLockPatternUtils,
534                     pin,
535                     mChallenge,
536                     UserHandle.myUserId(),
537                     new LockPatternChecker.OnVerifyCallback() {
538                         @Override
539                         public void onVerified(byte[] token, int timeoutMs) {
540                             if (token == null) {
541                                 Log.e(TAG, "critical: no token returned from known good password");
542                             }
543
544                             mPasswordEntryInputDisabler.setInputEnabled(true);
545                             setNextEnabled(true);
546                             mPendingLockCheck = null;
547
548                             Intent intent = new Intent();
549                             intent.putExtra(
550                                     ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
551                                     token);
552                             getActivity().setResult(RESULT_FINISHED, intent);
553                             finishConfirmStage(wasSecureBefore);
554                         }
555                     });
556         }
557
558         private void finishConfirmStage(boolean wasSecureBefore) {
559             getActivity().finish();
560             mDone = true;
561             if (!wasSecureBefore) {
562                 startActivity(getRedactionInterstitialIntent(getActivity()));
563             }
564         }
565
566         protected void setNextEnabled(boolean enabled) {
567             mNextButton.setEnabled(enabled);
568         }
569
570         protected void setNextText(int text) {
571             mNextButton.setText(text);
572         }
573
574         public void onClick(View v) {
575             switch (v.getId()) {
576                 case R.id.next_button:
577                     handleNext();
578                     break;
579
580                 case R.id.cancel_button:
581                     getActivity().finish();
582                     break;
583             }
584         }
585
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);
592         }
593
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) {
599                 handleNext();
600                 return true;
601             }
602             return false;
603         }
604
605         /**
606          * Update the hint based on current Stage and length of password entry
607          */
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);
617                 } else {
618                     String error = validatePassword(password);
619                     if (error != null) {
620                         mHeaderText.setText(error);
621                         setNextEnabled(false);
622                     } else {
623                         mHeaderText.setText(R.string.lockpassword_press_continue);
624                         setNextEnabled(true);
625                     }
626                 }
627             } else {
628                 mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint);
629                 setNextEnabled(length > 0);
630             }
631             setNextText(mUiStage.buttonText);
632         }
633
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;
638             }
639             updateUi();
640         }
641
642         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
643
644         }
645
646         public void onTextChanged(CharSequence s, int start, int before, int count) {
647
648         }
649     }
650 }