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 Intent intent = getRedactionInterstitialIntent(getActivity()); 563 if (intent != null) { 564 startActivity(intent); 565 } 566 } 567 } 568 569 protected void setNextEnabled(boolean enabled) { 570 mNextButton.setEnabled(enabled); 571 } 572 573 protected void setNextText(int text) { 574 mNextButton.setText(text); 575 } 576 577 public void onClick(View v) { 578 switch (v.getId()) { 579 case R.id.next_button: 580 handleNext(); 581 break; 582 583 case R.id.cancel_button: 584 getActivity().finish(); 585 break; 586 } 587 } 588 589 private void showError(String msg, final Stage next) { 590 mHeaderText.setText(msg); 591 mHeaderText.announceForAccessibility(mHeaderText.getText()); 592 Message mesg = mHandler.obtainMessage(MSG_SHOW_ERROR, next); 593 mHandler.removeMessages(MSG_SHOW_ERROR); 594 mHandler.sendMessageDelayed(mesg, ERROR_MESSAGE_TIMEOUT); 595 } 596 597 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 598 // Check if this was the result of hitting the enter or "done" key 599 if (actionId == EditorInfo.IME_NULL 600 || actionId == EditorInfo.IME_ACTION_DONE 601 || actionId == EditorInfo.IME_ACTION_NEXT) { 602 handleNext(); 603 return true; 604 } 605 return false; 606 } 607 608 /** 609 * Update the hint based on current Stage and length of password entry 610 */ 611 private void updateUi() { 612 String password = mPasswordEntry.getText().toString(); 613 final int length = password.length(); 614 if (mUiStage == Stage.Introduction) { 615 if (length < mPasswordMinLength) { 616 String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short 617 : R.string.lockpassword_pin_too_short, mPasswordMinLength); 618 mHeaderText.setText(msg); 619 setNextEnabled(false); 620 } else { 621 String error = validatePassword(password); 622 if (error != null) { 623 mHeaderText.setText(error); 624 setNextEnabled(false); 625 } else { 626 mHeaderText.setText(R.string.lockpassword_press_continue); 627 setNextEnabled(true); 628 } 629 } 630 } else { 631 mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint); 632 setNextEnabled(length > 0); 633 } 634 setNextText(mUiStage.buttonText); 635 } 636 637 public void afterTextChanged(Editable s) { 638 // Changing the text while error displayed resets to NeedToConfirm state 639 if (mUiStage == Stage.ConfirmWrong) { 640 mUiStage = Stage.NeedToConfirm; 641 } 642 updateUi(); 643 } 644 645 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 646 647 } 648 649 public void onTextChanged(CharSequence s, int start, int before, int count) { 650 651 } 652 } 653 } 654