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