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