ChooseLockPassword.java revision f2c2c19339b5d821cb6196911cee04dc9ac647ad
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 17package com.android.settings; 18 19import com.android.internal.logging.MetricsLogger; 20import com.android.internal.widget.LockPatternChecker; 21import com.android.internal.widget.LockPatternUtils; 22import com.android.internal.widget.PasswordEntryKeyboardHelper; 23import com.android.internal.widget.PasswordEntryKeyboardView; 24import com.android.internal.widget.TextViewInputDisabler; 25import com.android.settings.notification.RedactionInterstitial; 26 27import android.app.Activity; 28import android.app.Fragment; 29import android.app.admin.DevicePolicyManager; 30import android.content.Context; 31import android.content.Intent; 32import android.inputmethodservice.KeyboardView; 33import android.os.AsyncTask; 34import android.os.Bundle; 35import android.os.Handler; 36import android.os.Message; 37import android.os.UserHandle; 38import android.text.Editable; 39import android.text.InputType; 40import android.text.Selection; 41import android.text.Spannable; 42import android.text.TextUtils; 43import android.text.TextWatcher; 44import android.view.KeyEvent; 45import android.view.LayoutInflater; 46import android.view.View; 47import android.view.ViewGroup; 48import android.view.View.OnClickListener; 49import android.view.inputmethod.EditorInfo; 50import android.widget.Button; 51import android.widget.TextView; 52import android.widget.TextView.OnEditorActionListener; 53 54public class ChooseLockPassword extends SettingsActivity { 55 public static final String PASSWORD_MIN_KEY = "lockscreen.password_min"; 56 public static final String PASSWORD_MAX_KEY = "lockscreen.password_max"; 57 public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters"; 58 public static final String PASSWORD_MIN_LOWERCASE_KEY = "lockscreen.password_min_lowercase"; 59 public static final String PASSWORD_MIN_UPPERCASE_KEY = "lockscreen.password_min_uppercase"; 60 public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric"; 61 public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols"; 62 public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter"; 63 64 @Override 65 public Intent getIntent() { 66 Intent modIntent = new Intent(super.getIntent()); 67 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName()); 68 return modIntent; 69 } 70 71 public static Intent createIntent(Context context, int quality, 72 int minLength, final int maxLength, boolean requirePasswordToDecrypt, 73 boolean confirmCredentials) { 74 Intent intent = new Intent().setClass(context, ChooseLockPassword.class); 75 intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality); 76 intent.putExtra(PASSWORD_MIN_KEY, minLength); 77 intent.putExtra(PASSWORD_MAX_KEY, maxLength); 78 intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, confirmCredentials); 79 intent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, requirePasswordToDecrypt); 80 return intent; 81 } 82 83 public static Intent createIntent(Context context, int quality, 84 int minLength, final int maxLength, boolean requirePasswordToDecrypt, String password) { 85 Intent intent = createIntent(context, quality, minLength, maxLength, 86 requirePasswordToDecrypt, false); 87 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, password); 88 return intent; 89 } 90 91 public static Intent createIntent(Context context, int quality, 92 int minLength, final int maxLength, boolean requirePasswordToDecrypt, long challenge) { 93 Intent intent = createIntent(context, quality, minLength, maxLength, 94 requirePasswordToDecrypt, false); 95 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true); 96 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge); 97 return intent; 98 } 99 100 @Override 101 protected boolean isValidFragment(String fragmentName) { 102 if (ChooseLockPasswordFragment.class.getName().equals(fragmentName)) return true; 103 return false; 104 } 105 106 /* package */ Class<? extends Fragment> getFragmentClass() { 107 return ChooseLockPasswordFragment.class; 108 } 109 110 @Override 111 public void onCreate(Bundle savedInstanceState) { 112 // TODO: Fix on phones 113 // Disable IME on our window since we provide our own keyboard 114 //getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 115 //WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); 116 super.onCreate(savedInstanceState); 117 CharSequence msg = getText(R.string.lockpassword_choose_your_password_header); 118 setTitle(msg); 119 } 120 121 public static class ChooseLockPasswordFragment extends InstrumentedFragment 122 implements OnClickListener, OnEditorActionListener, TextWatcher { 123 private static final String KEY_FIRST_PIN = "first_pin"; 124 private static final String KEY_UI_STAGE = "ui_stage"; 125 private static final String KEY_CURRENT_PASSWORD = "current_password"; 126 127 private String mCurrentPassword; 128 private boolean mHasChallenge; 129 private long mChallenge; 130 private TextView mPasswordEntry; 131 private TextViewInputDisabler mPasswordEntryInputDisabler; 132 private int mPasswordMinLength = LockPatternUtils.MIN_LOCK_PASSWORD_SIZE; 133 private int mPasswordMaxLength = 16; 134 private int mPasswordMinLetters = 0; 135 private int mPasswordMinUpperCase = 0; 136 private int mPasswordMinLowerCase = 0; 137 private int mPasswordMinSymbols = 0; 138 private int mPasswordMinNumeric = 0; 139 private int mPasswordMinNonLetter = 0; 140 private LockPatternUtils mLockPatternUtils; 141 private AsyncTask<?, ?, ?> mPendingLockCheck; 142 private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 143 private ChooseLockSettingsHelper mChooseLockSettingsHelper; 144 private Stage mUiStage = Stage.Introduction; 145 private boolean mDone = false; 146 private TextView mHeaderText; 147 private String mFirstPin; 148 private KeyboardView mKeyboardView; 149 private PasswordEntryKeyboardHelper mKeyboardHelper; 150 private boolean mIsAlphaMode; 151 private Button mCancelButton; 152 private Button mNextButton; 153 private static final int CONFIRM_EXISTING_REQUEST = 58; 154 static final int RESULT_FINISHED = RESULT_FIRST_USER; 155 private static final long ERROR_MESSAGE_TIMEOUT = 3000; 156 private static final int MSG_SHOW_ERROR = 1; 157 158 private Handler mHandler = new Handler() { 159 @Override 160 public void handleMessage(Message msg) { 161 if (msg.what == MSG_SHOW_ERROR) { 162 updateStage((Stage) msg.obj); 163 } 164 } 165 }; 166 167 /** 168 * Keep track internally of where the user is in choosing a pattern. 169 */ 170 protected enum Stage { 171 172 Introduction(R.string.lockpassword_choose_your_password_header, 173 R.string.lockpassword_choose_your_pin_header, 174 R.string.lockpassword_continue_label), 175 176 NeedToConfirm(R.string.lockpassword_confirm_your_password_header, 177 R.string.lockpassword_confirm_your_pin_header, 178 R.string.lockpassword_ok_label), 179 180 ConfirmWrong(R.string.lockpassword_confirm_passwords_dont_match, 181 R.string.lockpassword_confirm_pins_dont_match, 182 R.string.lockpassword_continue_label); 183 184 Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) { 185 this.alphaHint = hintInAlpha; 186 this.numericHint = hintInNumeric; 187 this.buttonText = nextButtonText; 188 } 189 190 public final int alphaHint; 191 public final int numericHint; 192 public final int buttonText; 193 } 194 195 // required constructor for fragments 196 public ChooseLockPasswordFragment() { 197 198 } 199 200 @Override 201 public void onCreate(Bundle savedInstanceState) { 202 super.onCreate(savedInstanceState); 203 mLockPatternUtils = new LockPatternUtils(getActivity()); 204 Intent intent = getActivity().getIntent(); 205 if (!(getActivity() instanceof ChooseLockPassword)) { 206 throw new SecurityException("Fragment contained in wrong activity"); 207 } 208 mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, 209 mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality( 210 UserHandle.myUserId())); 211 mPasswordMinLength = Math.max(Math.max( 212 LockPatternUtils.MIN_LOCK_PASSWORD_SIZE, 213 intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength)), 214 mLockPatternUtils.getRequestedMinimumPasswordLength(UserHandle.myUserId())); 215 mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength); 216 mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY, 217 mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters( 218 UserHandle.myUserId())); 219 mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY, 220 mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase( 221 UserHandle.myUserId())); 222 mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY, 223 mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase( 224 UserHandle.myUserId())); 225 mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY, 226 mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric( 227 UserHandle.myUserId())); 228 mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY, 229 mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols( 230 UserHandle.myUserId())); 231 mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY, 232 mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter( 233 UserHandle.myUserId())); 234 235 mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity()); 236 } 237 238 @Override 239 public View onCreateView(LayoutInflater inflater, ViewGroup container, 240 Bundle savedInstanceState) { 241 return inflater.inflate(R.layout.choose_lock_password, container, false); 242 } 243 244 @Override 245 public void onViewCreated(View view, Bundle savedInstanceState) { 246 super.onViewCreated(view, savedInstanceState); 247 248 mCancelButton = (Button) view.findViewById(R.id.cancel_button); 249 mCancelButton.setOnClickListener(this); 250 mNextButton = (Button) view.findViewById(R.id.next_button); 251 mNextButton.setOnClickListener(this); 252 253 mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality 254 || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality 255 || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality; 256 mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard); 257 mPasswordEntry = (TextView) view.findViewById(R.id.password_entry); 258 mPasswordEntry.setOnEditorActionListener(this); 259 mPasswordEntry.addTextChangedListener(this); 260 mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry); 261 262 final Activity activity = getActivity(); 263 mKeyboardHelper = new PasswordEntryKeyboardHelper(activity, 264 mKeyboardView, mPasswordEntry); 265 mKeyboardHelper.setKeyboardMode(mIsAlphaMode ? 266 PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA 267 : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC); 268 269 mHeaderText = (TextView) view.findViewById(R.id.headerText); 270 mKeyboardView.requestFocus(); 271 272 int currentType = mPasswordEntry.getInputType(); 273 mPasswordEntry.setInputType(mIsAlphaMode ? currentType 274 : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD)); 275 276 Intent intent = getActivity().getIntent(); 277 final boolean confirmCredentials = intent.getBooleanExtra( 278 ChooseLockGeneric.CONFIRM_CREDENTIALS, true); 279 mCurrentPassword = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 280 mHasChallenge = intent.getBooleanExtra( 281 ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false); 282 mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0); 283 if (savedInstanceState == null) { 284 updateStage(Stage.Introduction); 285 if (confirmCredentials) { 286 mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST, 287 getString(R.string.unlock_set_unlock_launch_picker_title), true); 288 } 289 } else { 290 // restore from previous state 291 mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN); 292 final String state = savedInstanceState.getString(KEY_UI_STAGE); 293 if (state != null) { 294 mUiStage = Stage.valueOf(state); 295 updateStage(mUiStage); 296 } 297 298 if (mCurrentPassword == null) { 299 mCurrentPassword = savedInstanceState.getString(KEY_CURRENT_PASSWORD); 300 } 301 } 302 mDone = false; 303 if (activity instanceof SettingsActivity) { 304 final SettingsActivity sa = (SettingsActivity) activity; 305 int id = mIsAlphaMode ? R.string.lockpassword_choose_your_password_header 306 : R.string.lockpassword_choose_your_pin_header; 307 CharSequence title = getText(id); 308 sa.setTitle(title); 309 } 310 } 311 312 @Override 313 protected int getMetricsCategory() { 314 return MetricsLogger.CHOOSE_LOCK_PASSWORD; 315 } 316 317 @Override 318 public void onResume() { 319 super.onResume(); 320 updateStage(mUiStage); 321 mPasswordEntryInputDisabler.setInputEnabled(true); 322 mKeyboardView.requestFocus(); 323 } 324 325 @Override 326 public void onPause() { 327 mHandler.removeMessages(MSG_SHOW_ERROR); 328 if (mPendingLockCheck != null) { 329 mPendingLockCheck.cancel(false); 330 mPendingLockCheck = null; 331 } 332 333 super.onPause(); 334 } 335 336 @Override 337 public void onSaveInstanceState(Bundle outState) { 338 super.onSaveInstanceState(outState); 339 outState.putString(KEY_UI_STAGE, mUiStage.name()); 340 outState.putString(KEY_FIRST_PIN, mFirstPin); 341 outState.putString(KEY_CURRENT_PASSWORD, mCurrentPassword); 342 } 343 344 @Override 345 public void onActivityResult(int requestCode, int resultCode, 346 Intent data) { 347 super.onActivityResult(requestCode, resultCode, data); 348 switch (requestCode) { 349 case CONFIRM_EXISTING_REQUEST: 350 if (resultCode != Activity.RESULT_OK) { 351 getActivity().setResult(RESULT_FINISHED); 352 getActivity().finish(); 353 } else { 354 mCurrentPassword = data.getStringExtra( 355 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 356 } 357 break; 358 } 359 } 360 361 protected Intent getRedactionInterstitialIntent(Context context) { 362 return RedactionInterstitial.createStartIntent(context); 363 } 364 365 protected void updateStage(Stage stage) { 366 final Stage previousStage = mUiStage; 367 mUiStage = stage; 368 updateUi(); 369 370 // If the stage changed, announce the header for accessibility. This 371 // is a no-op when accessibility is disabled. 372 if (previousStage != stage) { 373 mHeaderText.announceForAccessibility(mHeaderText.getText()); 374 } 375 } 376 377 /** 378 * Validates PIN and returns a message to display if PIN fails test. 379 * @param password the raw password the user typed in 380 * @return error message to show to user or null if password is OK 381 */ 382 private String validatePassword(String password) { 383 if (password.length() < mPasswordMinLength) { 384 return getString(mIsAlphaMode ? 385 R.string.lockpassword_password_too_short 386 : R.string.lockpassword_pin_too_short, mPasswordMinLength); 387 } 388 if (password.length() > mPasswordMaxLength) { 389 return getString(mIsAlphaMode ? 390 R.string.lockpassword_password_too_long 391 : R.string.lockpassword_pin_too_long, mPasswordMaxLength + 1); 392 } 393 int letters = 0; 394 int numbers = 0; 395 int lowercase = 0; 396 int symbols = 0; 397 int uppercase = 0; 398 int nonletter = 0; 399 for (int i = 0; i < password.length(); i++) { 400 char c = password.charAt(i); 401 // allow non control Latin-1 characters only 402 if (c < 32 || c > 127) { 403 return getString(R.string.lockpassword_illegal_character); 404 } 405 if (c >= '0' && c <= '9') { 406 numbers++; 407 nonletter++; 408 } else if (c >= 'A' && c <= 'Z') { 409 letters++; 410 uppercase++; 411 } else if (c >= 'a' && c <= 'z') { 412 letters++; 413 lowercase++; 414 } else { 415 symbols++; 416 nonletter++; 417 } 418 } 419 if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality 420 || DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX == mRequestedQuality) { 421 if (letters > 0 || symbols > 0) { 422 // This shouldn't be possible unless user finds some way to bring up 423 // soft keyboard 424 return getString(R.string.lockpassword_pin_contains_non_digits); 425 } 426 // Check for repeated characters or sequences (e.g. '1234', '0000', '2468') 427 final int sequence = LockPatternUtils.maxLengthSequence(password); 428 if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX == mRequestedQuality 429 && sequence > LockPatternUtils.MAX_ALLOWED_SEQUENCE) { 430 return getString(R.string.lockpassword_pin_no_sequential_digits); 431 } 432 } else if (DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality) { 433 if (letters < mPasswordMinLetters) { 434 return String.format(getResources().getQuantityString( 435 R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters), 436 mPasswordMinLetters); 437 } else if (numbers < mPasswordMinNumeric) { 438 return String.format(getResources().getQuantityString( 439 R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric), 440 mPasswordMinNumeric); 441 } else if (lowercase < mPasswordMinLowerCase) { 442 return String.format(getResources().getQuantityString( 443 R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase), 444 mPasswordMinLowerCase); 445 } else if (uppercase < mPasswordMinUpperCase) { 446 return String.format(getResources().getQuantityString( 447 R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase), 448 mPasswordMinUpperCase); 449 } else if (symbols < mPasswordMinSymbols) { 450 return String.format(getResources().getQuantityString( 451 R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols), 452 mPasswordMinSymbols); 453 } else if (nonletter < mPasswordMinNonLetter) { 454 return String.format(getResources().getQuantityString( 455 R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter), 456 mPasswordMinNonLetter); 457 } 458 } else { 459 final boolean alphabetic = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC 460 == mRequestedQuality; 461 final boolean alphanumeric = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC 462 == mRequestedQuality; 463 if ((alphabetic || alphanumeric) && letters == 0) { 464 return getString(R.string.lockpassword_password_requires_alpha); 465 } 466 if (alphanumeric && numbers == 0) { 467 return getString(R.string.lockpassword_password_requires_digit); 468 } 469 } 470 if(mLockPatternUtils.checkPasswordHistory(password, UserHandle.myUserId())) { 471 return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used 472 : R.string.lockpassword_pin_recently_used); 473 } 474 475 return null; 476 } 477 478 public void handleNext() { 479 if (mDone) return; 480 481 final String pin = mPasswordEntry.getText().toString(); 482 if (TextUtils.isEmpty(pin)) { 483 return; 484 } 485 String errorMsg = null; 486 if (mUiStage == Stage.Introduction) { 487 errorMsg = validatePassword(pin); 488 if (errorMsg == null) { 489 mFirstPin = pin; 490 mPasswordEntry.setText(""); 491 updateStage(Stage.NeedToConfirm); 492 } 493 } else if (mUiStage == Stage.NeedToConfirm) { 494 if (mFirstPin.equals(pin)) { 495 boolean wasSecureBefore = mLockPatternUtils.isSecure(UserHandle.myUserId()); 496 final boolean required = getActivity().getIntent().getBooleanExtra( 497 EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true); 498 mLockPatternUtils.setCredentialRequiredToDecrypt(required); 499 mLockPatternUtils.saveLockPassword(pin, mCurrentPassword, mRequestedQuality, 500 UserHandle.myUserId()); 501 502 if (mHasChallenge) { 503 startVerifyPassword(pin, wasSecureBefore); 504 return; 505 } else { 506 getActivity().setResult(RESULT_FINISHED); 507 } 508 finishConfirmStage(wasSecureBefore); 509 } else { 510 CharSequence tmp = mPasswordEntry.getText(); 511 if (tmp != null) { 512 Selection.setSelection((Spannable) tmp, 0, tmp.length()); 513 } 514 updateStage(Stage.ConfirmWrong); 515 } 516 } 517 if (errorMsg != null) { 518 showError(errorMsg, mUiStage); 519 } 520 } 521 522 private void startVerifyPassword(final String pin, final boolean wasSecureBefore) { 523 mPasswordEntryInputDisabler.setInputEnabled(false); 524 setNextEnabled(false); 525 if (mPendingLockCheck != null) { 526 mPendingLockCheck.cancel(false); 527 } 528 529 mPendingLockCheck = LockPatternChecker.verifyPassword( 530 mLockPatternUtils, 531 pin, 532 mChallenge, 533 UserHandle.myUserId(), 534 new LockPatternChecker.OnVerifyCallback() { 535 @Override 536 public void onVerified(byte[] token) { 537 mPasswordEntryInputDisabler.setInputEnabled(true); 538 setNextEnabled(true); 539 mPendingLockCheck = null; 540 541 Intent intent = new Intent(); 542 intent.putExtra( 543 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, 544 token); 545 getActivity().setResult(RESULT_FINISHED, intent); 546 finishConfirmStage(wasSecureBefore); 547 } 548 }); 549 } 550 551 private void finishConfirmStage(boolean wasSecureBefore) { 552 getActivity().finish(); 553 mDone = true; 554 if (!wasSecureBefore) { 555 startActivity(getRedactionInterstitialIntent(getActivity())); 556 } 557 } 558 559 protected void setNextEnabled(boolean enabled) { 560 mNextButton.setEnabled(enabled); 561 } 562 563 protected void setNextText(int text) { 564 mNextButton.setText(text); 565 } 566 567 public void onClick(View v) { 568 switch (v.getId()) { 569 case R.id.next_button: 570 handleNext(); 571 break; 572 573 case R.id.cancel_button: 574 getActivity().finish(); 575 break; 576 } 577 } 578 579 private void showError(String msg, final Stage next) { 580 mHeaderText.setText(msg); 581 mHeaderText.announceForAccessibility(mHeaderText.getText()); 582 Message mesg = mHandler.obtainMessage(MSG_SHOW_ERROR, next); 583 mHandler.removeMessages(MSG_SHOW_ERROR); 584 mHandler.sendMessageDelayed(mesg, ERROR_MESSAGE_TIMEOUT); 585 } 586 587 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 588 // Check if this was the result of hitting the enter or "done" key 589 if (actionId == EditorInfo.IME_NULL 590 || actionId == EditorInfo.IME_ACTION_DONE 591 || actionId == EditorInfo.IME_ACTION_NEXT) { 592 handleNext(); 593 return true; 594 } 595 return false; 596 } 597 598 /** 599 * Update the hint based on current Stage and length of password entry 600 */ 601 private void updateUi() { 602 String password = mPasswordEntry.getText().toString(); 603 final int length = password.length(); 604 if (mUiStage == Stage.Introduction && length > 0) { 605 if (length < mPasswordMinLength) { 606 String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short 607 : R.string.lockpassword_pin_too_short, mPasswordMinLength); 608 mHeaderText.setText(msg); 609 setNextEnabled(false); 610 } else { 611 String error = validatePassword(password); 612 if (error != null) { 613 mHeaderText.setText(error); 614 setNextEnabled(false); 615 } else { 616 mHeaderText.setText(R.string.lockpassword_press_continue); 617 setNextEnabled(true); 618 } 619 } 620 } else { 621 mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint); 622 setNextEnabled(length > 0); 623 } 624 setNextText(mUiStage.buttonText); 625 } 626 627 public void afterTextChanged(Editable s) { 628 // Changing the text while error displayed resets to NeedToConfirm state 629 if (mUiStage == Stage.ConfirmWrong) { 630 mUiStage = Stage.NeedToConfirm; 631 } 632 updateUi(); 633 } 634 635 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 636 637 } 638 639 public void onTextChanged(CharSequence s, int start, int before, int count) { 640 641 } 642 } 643} 644