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