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.LockPatternUtils; 21 import com.android.internal.widget.PasswordEntryKeyboardHelper; 22 import com.android.internal.widget.PasswordEntryKeyboardView; 23 import com.android.internal.widget.TextViewInputDisabler; 24 import com.android.internal.widget.LockPatternUtils.RequestThrottledException; 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.Bundle; 34 import android.os.Handler; 35 import android.os.Message; 36 import android.os.UserHandle; 37 import android.text.Editable; 38 import android.text.InputType; 39 import android.text.Selection; 40 import android.text.Spannable; 41 import android.text.TextUtils; 42 import android.text.TextWatcher; 43 import android.util.Log; 44 import android.view.KeyEvent; 45 import android.view.LayoutInflater; 46 import android.view.View; 47 import android.view.ViewGroup; 48 import android.view.View.OnClickListener; 49 import android.view.inputmethod.EditorInfo; 50 import android.widget.Button; 51 import android.widget.TextView; 52 import android.widget.TextView.OnEditorActionListener; 53 54 public 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 getIntent()67 public Intent getIntent() { 68 Intent modIntent = new Intent(super.getIntent()); 69 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName()); 70 return modIntent; 71 } 72 createIntent(Context context, int quality, int minLength, final int maxLength, boolean requirePasswordToDecrypt, boolean confirmCredentials)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 createIntent(Context context, int quality, int minLength, final int maxLength, boolean requirePasswordToDecrypt, String password)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 createIntent(Context context, int quality, int minLength, final int maxLength, boolean requirePasswordToDecrypt, long challenge)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 isValidFragment(String fragmentName)103 protected boolean isValidFragment(String fragmentName) { 104 if (ChooseLockPasswordFragment.class.getName().equals(fragmentName)) return true; 105 return false; 106 } 107 getFragmentClass()108 /* package */ Class<? extends Fragment> getFragmentClass() { 109 return ChooseLockPasswordFragment.class; 110 } 111 112 @Override onCreate(Bundle savedInstanceState)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 Stage(int hintInAlpha, int hintInNumeric, int nextButtonText)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 ChooseLockPasswordFragment()201 public ChooseLockPasswordFragment() { 202 203 } 204 205 @Override onCreate(Bundle savedInstanceState)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 onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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 onViewCreated(View view, Bundle savedInstanceState)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 getMetricsCategory()321 protected int getMetricsCategory() { 322 return MetricsLogger.CHOOSE_LOCK_PASSWORD; 323 } 324 325 @Override onResume()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 onPause()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 onSaveInstanceState(Bundle outState)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 onActivityResult(int requestCode, int resultCode, Intent data)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 getRedactionInterstitialIntent(Context context)371 protected Intent getRedactionInterstitialIntent(Context context) { 372 return RedactionInterstitial.createStartIntent(context); 373 } 374 updateStage(Stage stage)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 */ validatePassword(String password)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 handleNext()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 setNextEnabled(boolean enabled)518 protected void setNextEnabled(boolean enabled) { 519 mNextButton.setEnabled(enabled); 520 } 521 setNextText(int text)522 protected void setNextText(int text) { 523 mNextButton.setText(text); 524 } 525 onClick(View v)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 showError(String msg, final Stage next)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 onEditorAction(TextView v, int actionId, KeyEvent event)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 */ updateUi()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 afterTextChanged(Editable s)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 beforeTextChanged(CharSequence s, int start, int count, int after)596 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 597 598 } 599 onTextChanged(CharSequence s, int start, int before, int count)600 public void onTextChanged(CharSequence s, int start, int before, int count) { 601 602 } 603 startSaveAndFinish()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 onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData)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 start(LockPatternUtils utils, boolean required, boolean hasChallenge, long challenge, String chosenPassword, String currentPassword, int requestedQuality)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 saveAndVerifyInBackground()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