1 /* 2 * Copyright (C) 2007 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.google.android.collect.Lists; 21 import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient; 22 import com.android.internal.widget.LockPatternUtils; 23 import com.android.internal.widget.LockPatternUtils.RequestThrottledException; 24 import com.android.internal.widget.LockPatternView; 25 import com.android.internal.widget.LockPatternView.Cell; 26 import com.android.settings.notification.RedactionInterstitial; 27 28 import static com.android.internal.widget.LockPatternView.DisplayMode; 29 30 import android.app.Activity; 31 import android.app.Fragment; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.os.Bundle; 35 import android.os.UserHandle; 36 import android.util.Log; 37 import android.view.KeyEvent; 38 import android.view.LayoutInflater; 39 import android.view.View; 40 import android.view.ViewGroup; 41 import android.widget.TextView; 42 43 import java.util.ArrayList; 44 import java.util.Collections; 45 import java.util.List; 46 47 /** 48 * If the user has a lock pattern set already, makes them confirm the existing one. 49 * 50 * Then, prompts the user to choose a lock pattern: 51 * - prompts for initial pattern 52 * - asks for confirmation / restart 53 * - saves chosen password when confirmed 54 */ 55 public class ChooseLockPattern extends SettingsActivity { 56 /** 57 * Used by the choose lock pattern wizard to indicate the wizard is 58 * finished, and each activity in the wizard should finish. 59 * <p> 60 * Previously, each activity in the wizard would finish itself after 61 * starting the next activity. However, this leads to broken 'Back' 62 * behavior. So, now an activity does not finish itself until it gets this 63 * result. 64 */ 65 static final int RESULT_FINISHED = RESULT_FIRST_USER; 66 67 private static final String TAG = "ChooseLockPattern"; 68 69 @Override getIntent()70 public Intent getIntent() { 71 Intent modIntent = new Intent(super.getIntent()); 72 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName()); 73 return modIntent; 74 } 75 createIntent(Context context, boolean requirePassword, boolean confirmCredentials)76 public static Intent createIntent(Context context, 77 boolean requirePassword, boolean confirmCredentials) { 78 Intent intent = new Intent(context, ChooseLockPattern.class); 79 intent.putExtra("key_lock_method", "pattern"); 80 intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, confirmCredentials); 81 intent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, requirePassword); 82 return intent; 83 } 84 createIntent(Context context, boolean requirePassword, String pattern)85 public static Intent createIntent(Context context, 86 boolean requirePassword, String pattern) { 87 Intent intent = createIntent(context, requirePassword, false); 88 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pattern); 89 return intent; 90 } 91 92 createIntent(Context context, boolean requirePassword, long challenge)93 public static Intent createIntent(Context context, 94 boolean requirePassword, long challenge) { 95 Intent intent = createIntent(context, requirePassword, false); 96 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true); 97 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge); 98 return intent; 99 } 100 101 @Override isValidFragment(String fragmentName)102 protected boolean isValidFragment(String fragmentName) { 103 if (ChooseLockPatternFragment.class.getName().equals(fragmentName)) return true; 104 return false; 105 } 106 getFragmentClass()107 /* package */ Class<? extends Fragment> getFragmentClass() { 108 return ChooseLockPatternFragment.class; 109 } 110 111 @Override onCreate(Bundle savedInstanceState)112 public void onCreate(Bundle savedInstanceState) { 113 // requestWindowFeature(Window.FEATURE_NO_TITLE); 114 super.onCreate(savedInstanceState); 115 CharSequence msg = getText(R.string.lockpassword_choose_your_pattern_header); 116 setTitle(msg); 117 } 118 119 @Override onKeyDown(int keyCode, KeyEvent event)120 public boolean onKeyDown(int keyCode, KeyEvent event) { 121 // *** TODO *** 122 // chooseLockPatternFragment.onKeyDown(keyCode, event); 123 return super.onKeyDown(keyCode, event); 124 } 125 126 public static class ChooseLockPatternFragment extends InstrumentedFragment 127 implements View.OnClickListener, SaveAndFinishWorker.Listener { 128 129 public static final int CONFIRM_EXISTING_REQUEST = 55; 130 131 // how long after a confirmation message is shown before moving on 132 static final int INFORMATION_MSG_TIMEOUT_MS = 3000; 133 134 // how long we wait to clear a wrong pattern 135 private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000; 136 137 private static final int ID_EMPTY_MESSAGE = -1; 138 139 private static final String FRAGMENT_TAG_SAVE_AND_FINISH = "save_and_finish_worker"; 140 141 private String mCurrentPattern; 142 private boolean mHasChallenge; 143 private long mChallenge; 144 protected TextView mHeaderText; 145 protected LockPatternView mLockPatternView; 146 protected TextView mFooterText; 147 private TextView mFooterLeftButton; 148 private TextView mFooterRightButton; 149 protected List<LockPatternView.Cell> mChosenPattern = null; 150 151 /** 152 * The patten used during the help screen to show how to draw a pattern. 153 */ 154 private final List<LockPatternView.Cell> mAnimatePattern = 155 Collections.unmodifiableList(Lists.newArrayList( 156 LockPatternView.Cell.of(0, 0), 157 LockPatternView.Cell.of(0, 1), 158 LockPatternView.Cell.of(1, 1), 159 LockPatternView.Cell.of(2, 1) 160 )); 161 162 @Override onActivityResult(int requestCode, int resultCode, Intent data)163 public void onActivityResult(int requestCode, int resultCode, 164 Intent data) { 165 super.onActivityResult(requestCode, resultCode, data); 166 switch (requestCode) { 167 case CONFIRM_EXISTING_REQUEST: 168 if (resultCode != Activity.RESULT_OK) { 169 getActivity().setResult(RESULT_FINISHED); 170 getActivity().finish(); 171 } else { 172 mCurrentPattern = data.getStringExtra( 173 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 174 } 175 176 updateStage(Stage.Introduction); 177 break; 178 } 179 } 180 setRightButtonEnabled(boolean enabled)181 protected void setRightButtonEnabled(boolean enabled) { 182 mFooterRightButton.setEnabled(enabled); 183 } 184 setRightButtonText(int text)185 protected void setRightButtonText(int text) { 186 mFooterRightButton.setText(text); 187 } 188 189 /** 190 * The pattern listener that responds according to a user choosing a new 191 * lock pattern. 192 */ 193 protected LockPatternView.OnPatternListener mChooseNewLockPatternListener = 194 new LockPatternView.OnPatternListener() { 195 196 public void onPatternStart() { 197 mLockPatternView.removeCallbacks(mClearPatternRunnable); 198 patternInProgress(); 199 } 200 201 public void onPatternCleared() { 202 mLockPatternView.removeCallbacks(mClearPatternRunnable); 203 } 204 205 public void onPatternDetected(List<LockPatternView.Cell> pattern) { 206 if (mUiStage == Stage.NeedToConfirm || mUiStage == Stage.ConfirmWrong) { 207 if (mChosenPattern == null) throw new IllegalStateException( 208 "null chosen pattern in stage 'need to confirm"); 209 if (mChosenPattern.equals(pattern)) { 210 updateStage(Stage.ChoiceConfirmed); 211 } else { 212 updateStage(Stage.ConfirmWrong); 213 } 214 } else if (mUiStage == Stage.Introduction || mUiStage == Stage.ChoiceTooShort){ 215 if (pattern.size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) { 216 updateStage(Stage.ChoiceTooShort); 217 } else { 218 mChosenPattern = new ArrayList<LockPatternView.Cell>(pattern); 219 updateStage(Stage.FirstChoiceValid); 220 } 221 } else { 222 throw new IllegalStateException("Unexpected stage " + mUiStage + " when " 223 + "entering the pattern."); 224 } 225 } 226 227 public void onPatternCellAdded(List<Cell> pattern) { 228 229 } 230 231 private void patternInProgress() { 232 mHeaderText.setText(R.string.lockpattern_recording_inprogress); 233 mFooterText.setText(""); 234 mFooterLeftButton.setEnabled(false); 235 mFooterRightButton.setEnabled(false); 236 } 237 }; 238 239 @Override getMetricsCategory()240 protected int getMetricsCategory() { 241 return MetricsLogger.CHOOSE_LOCK_PATTERN; 242 } 243 244 245 /** 246 * The states of the left footer button. 247 */ 248 enum LeftButtonMode { 249 Cancel(R.string.cancel, true), 250 CancelDisabled(R.string.cancel, false), 251 Retry(R.string.lockpattern_retry_button_text, true), 252 RetryDisabled(R.string.lockpattern_retry_button_text, false), 253 Gone(ID_EMPTY_MESSAGE, false); 254 255 256 /** 257 * @param text The displayed text for this mode. 258 * @param enabled Whether the button should be enabled. 259 */ LeftButtonMode(int text, boolean enabled)260 LeftButtonMode(int text, boolean enabled) { 261 this.text = text; 262 this.enabled = enabled; 263 } 264 265 final int text; 266 final boolean enabled; 267 } 268 269 /** 270 * The states of the right button. 271 */ 272 enum RightButtonMode { 273 Continue(R.string.lockpattern_continue_button_text, true), 274 ContinueDisabled(R.string.lockpattern_continue_button_text, false), 275 Confirm(R.string.lockpattern_confirm_button_text, true), 276 ConfirmDisabled(R.string.lockpattern_confirm_button_text, false), 277 Ok(android.R.string.ok, true); 278 279 /** 280 * @param text The displayed text for this mode. 281 * @param enabled Whether the button should be enabled. 282 */ RightButtonMode(int text, boolean enabled)283 RightButtonMode(int text, boolean enabled) { 284 this.text = text; 285 this.enabled = enabled; 286 } 287 288 final int text; 289 final boolean enabled; 290 } 291 292 /** 293 * Keep track internally of where the user is in choosing a pattern. 294 */ 295 protected enum Stage { 296 297 Introduction( 298 R.string.lockpattern_recording_intro_header, 299 LeftButtonMode.Cancel, RightButtonMode.ContinueDisabled, 300 ID_EMPTY_MESSAGE, true), 301 HelpScreen( 302 R.string.lockpattern_settings_help_how_to_record, 303 LeftButtonMode.Gone, RightButtonMode.Ok, ID_EMPTY_MESSAGE, false), 304 ChoiceTooShort( 305 R.string.lockpattern_recording_incorrect_too_short, 306 LeftButtonMode.Retry, RightButtonMode.ContinueDisabled, 307 ID_EMPTY_MESSAGE, true), 308 FirstChoiceValid( 309 R.string.lockpattern_pattern_entered_header, 310 LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false), 311 NeedToConfirm( 312 R.string.lockpattern_need_to_confirm, 313 LeftButtonMode.Cancel, RightButtonMode.ConfirmDisabled, 314 ID_EMPTY_MESSAGE, true), 315 ConfirmWrong( 316 R.string.lockpattern_need_to_unlock_wrong, 317 LeftButtonMode.Cancel, RightButtonMode.ConfirmDisabled, 318 ID_EMPTY_MESSAGE, true), 319 ChoiceConfirmed( 320 R.string.lockpattern_pattern_confirmed_header, 321 LeftButtonMode.Cancel, RightButtonMode.Confirm, ID_EMPTY_MESSAGE, false); 322 323 324 /** 325 * @param headerMessage The message displayed at the top. 326 * @param leftMode The mode of the left button. 327 * @param rightMode The mode of the right button. 328 * @param footerMessage The footer message. 329 * @param patternEnabled Whether the pattern widget is enabled. 330 */ Stage(int headerMessage, LeftButtonMode leftMode, RightButtonMode rightMode, int footerMessage, boolean patternEnabled)331 Stage(int headerMessage, 332 LeftButtonMode leftMode, 333 RightButtonMode rightMode, 334 int footerMessage, boolean patternEnabled) { 335 this.headerMessage = headerMessage; 336 this.leftMode = leftMode; 337 this.rightMode = rightMode; 338 this.footerMessage = footerMessage; 339 this.patternEnabled = patternEnabled; 340 } 341 342 final int headerMessage; 343 final LeftButtonMode leftMode; 344 final RightButtonMode rightMode; 345 final int footerMessage; 346 final boolean patternEnabled; 347 } 348 349 private Stage mUiStage = Stage.Introduction; 350 351 private Runnable mClearPatternRunnable = new Runnable() { 352 public void run() { 353 mLockPatternView.clearPattern(); 354 } 355 }; 356 357 private ChooseLockSettingsHelper mChooseLockSettingsHelper; 358 private SaveAndFinishWorker mSaveAndFinishWorker; 359 360 private static final String KEY_UI_STAGE = "uiStage"; 361 private static final String KEY_PATTERN_CHOICE = "chosenPattern"; 362 private static final String KEY_CURRENT_PATTERN = "currentPattern"; 363 364 @Override onCreate(Bundle savedInstanceState)365 public void onCreate(Bundle savedInstanceState) { 366 super.onCreate(savedInstanceState); 367 mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity()); 368 if (!(getActivity() instanceof ChooseLockPattern)) { 369 throw new SecurityException("Fragment contained in wrong activity"); 370 } 371 } 372 373 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)374 public View onCreateView(LayoutInflater inflater, ViewGroup container, 375 Bundle savedInstanceState) { 376 return inflater.inflate(R.layout.choose_lock_pattern, container, false); 377 } 378 379 @Override onViewCreated(View view, Bundle savedInstanceState)380 public void onViewCreated(View view, Bundle savedInstanceState) { 381 super.onViewCreated(view, savedInstanceState); 382 mHeaderText = (TextView) view.findViewById(R.id.headerText); 383 mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern); 384 mLockPatternView.setOnPatternListener(mChooseNewLockPatternListener); 385 mLockPatternView.setTactileFeedbackEnabled( 386 mChooseLockSettingsHelper.utils().isTactileFeedbackEnabled()); 387 388 mFooterText = (TextView) view.findViewById(R.id.footerText); 389 390 mFooterLeftButton = (TextView) view.findViewById(R.id.footerLeftButton); 391 mFooterRightButton = (TextView) view.findViewById(R.id.footerRightButton); 392 393 mFooterLeftButton.setOnClickListener(this); 394 mFooterRightButton.setOnClickListener(this); 395 396 // make it so unhandled touch events within the unlock screen go to the 397 // lock pattern view. 398 final LinearLayoutWithDefaultTouchRecepient topLayout 399 = (LinearLayoutWithDefaultTouchRecepient) view.findViewById( 400 R.id.topLayout); 401 topLayout.setDefaultTouchRecepient(mLockPatternView); 402 403 final boolean confirmCredentials = getActivity().getIntent() 404 .getBooleanExtra("confirm_credentials", true); 405 Intent intent = getActivity().getIntent(); 406 mCurrentPattern = intent.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD); 407 mHasChallenge = intent.getBooleanExtra( 408 ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false); 409 mChallenge = intent.getLongExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0); 410 411 if (savedInstanceState == null) { 412 if (confirmCredentials) { 413 // first launch. As a security measure, we're in NeedToConfirm mode until we 414 // know there isn't an existing password or the user confirms their password. 415 updateStage(Stage.NeedToConfirm); 416 boolean launchedConfirmationActivity = 417 mChooseLockSettingsHelper.launchConfirmationActivity( 418 CONFIRM_EXISTING_REQUEST, 419 getString(R.string.unlock_set_unlock_launch_picker_title), true); 420 if (!launchedConfirmationActivity) { 421 updateStage(Stage.Introduction); 422 } 423 } else { 424 updateStage(Stage.Introduction); 425 } 426 } else { 427 // restore from previous state 428 final String patternString = savedInstanceState.getString(KEY_PATTERN_CHOICE); 429 if (patternString != null) { 430 mChosenPattern = LockPatternUtils.stringToPattern(patternString); 431 } 432 433 if (mCurrentPattern == null) { 434 mCurrentPattern = savedInstanceState.getString(KEY_CURRENT_PATTERN); 435 } 436 updateStage(Stage.values()[savedInstanceState.getInt(KEY_UI_STAGE)]); 437 438 // Re-attach to the exiting worker if there is one. 439 mSaveAndFinishWorker = (SaveAndFinishWorker) getFragmentManager().findFragmentByTag( 440 FRAGMENT_TAG_SAVE_AND_FINISH); 441 } 442 } 443 444 @Override onResume()445 public void onResume() { 446 super.onResume(); 447 updateStage(mUiStage); 448 449 if (mSaveAndFinishWorker != null) { 450 setRightButtonEnabled(false); 451 mSaveAndFinishWorker.setListener(this); 452 } 453 } 454 455 @Override onPause()456 public void onPause() { 457 super.onPause(); 458 if (mSaveAndFinishWorker != null) { 459 mSaveAndFinishWorker.setListener(null); 460 } 461 } 462 getRedactionInterstitialIntent(Context context)463 protected Intent getRedactionInterstitialIntent(Context context) { 464 return RedactionInterstitial.createStartIntent(context); 465 } 466 handleLeftButton()467 public void handleLeftButton() { 468 if (mUiStage.leftMode == LeftButtonMode.Retry) { 469 mChosenPattern = null; 470 mLockPatternView.clearPattern(); 471 updateStage(Stage.Introduction); 472 } else if (mUiStage.leftMode == LeftButtonMode.Cancel) { 473 getActivity().finish(); 474 } else { 475 throw new IllegalStateException("left footer button pressed, but stage of " + 476 mUiStage + " doesn't make sense"); 477 } 478 } 479 handleRightButton()480 public void handleRightButton() { 481 if (mUiStage.rightMode == RightButtonMode.Continue) { 482 if (mUiStage != Stage.FirstChoiceValid) { 483 throw new IllegalStateException("expected ui stage " 484 + Stage.FirstChoiceValid + " when button is " 485 + RightButtonMode.Continue); 486 } 487 updateStage(Stage.NeedToConfirm); 488 } else if (mUiStage.rightMode == RightButtonMode.Confirm) { 489 if (mUiStage != Stage.ChoiceConfirmed) { 490 throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed 491 + " when button is " + RightButtonMode.Confirm); 492 } 493 startSaveAndFinish(); 494 } else if (mUiStage.rightMode == RightButtonMode.Ok) { 495 if (mUiStage != Stage.HelpScreen) { 496 throw new IllegalStateException("Help screen is only mode with ok button, " 497 + "but stage is " + mUiStage); 498 } 499 mLockPatternView.clearPattern(); 500 mLockPatternView.setDisplayMode(DisplayMode.Correct); 501 updateStage(Stage.Introduction); 502 } 503 } 504 onClick(View v)505 public void onClick(View v) { 506 if (v == mFooterLeftButton) { 507 handleLeftButton(); 508 } else if (v == mFooterRightButton) { 509 handleRightButton(); 510 } 511 } 512 onKeyDown(int keyCode, KeyEvent event)513 public boolean onKeyDown(int keyCode, KeyEvent event) { 514 if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { 515 if (mUiStage == Stage.HelpScreen) { 516 updateStage(Stage.Introduction); 517 return true; 518 } 519 } 520 if (keyCode == KeyEvent.KEYCODE_MENU && mUiStage == Stage.Introduction) { 521 updateStage(Stage.HelpScreen); 522 return true; 523 } 524 return false; 525 } 526 onSaveInstanceState(Bundle outState)527 public void onSaveInstanceState(Bundle outState) { 528 super.onSaveInstanceState(outState); 529 530 outState.putInt(KEY_UI_STAGE, mUiStage.ordinal()); 531 if (mChosenPattern != null) { 532 outState.putString(KEY_PATTERN_CHOICE, 533 LockPatternUtils.patternToString(mChosenPattern)); 534 } 535 536 if (mCurrentPattern != null) { 537 outState.putString(KEY_CURRENT_PATTERN, 538 mCurrentPattern); 539 } 540 } 541 542 /** 543 * Updates the messages and buttons appropriate to what stage the user 544 * is at in choosing a view. This doesn't handle clearing out the pattern; 545 * the pattern is expected to be in the right state. 546 * @param stage 547 */ updateStage(Stage stage)548 protected void updateStage(Stage stage) { 549 final Stage previousStage = mUiStage; 550 551 mUiStage = stage; 552 553 // header text, footer text, visibility and 554 // enabled state all known from the stage 555 if (stage == Stage.ChoiceTooShort) { 556 mHeaderText.setText( 557 getResources().getString( 558 stage.headerMessage, 559 LockPatternUtils.MIN_LOCK_PATTERN_SIZE)); 560 } else { 561 mHeaderText.setText(stage.headerMessage); 562 } 563 if (stage.footerMessage == ID_EMPTY_MESSAGE) { 564 mFooterText.setText(""); 565 } else { 566 mFooterText.setText(stage.footerMessage); 567 } 568 569 if (stage.leftMode == LeftButtonMode.Gone) { 570 mFooterLeftButton.setVisibility(View.GONE); 571 } else { 572 mFooterLeftButton.setVisibility(View.VISIBLE); 573 mFooterLeftButton.setText(stage.leftMode.text); 574 mFooterLeftButton.setEnabled(stage.leftMode.enabled); 575 } 576 577 setRightButtonText(stage.rightMode.text); 578 setRightButtonEnabled(stage.rightMode.enabled); 579 580 // same for whether the pattern is enabled 581 if (stage.patternEnabled) { 582 mLockPatternView.enableInput(); 583 } else { 584 mLockPatternView.disableInput(); 585 } 586 587 // the rest of the stuff varies enough that it is easier just to handle 588 // on a case by case basis. 589 mLockPatternView.setDisplayMode(DisplayMode.Correct); 590 boolean announceAlways = false; 591 592 switch (mUiStage) { 593 case Introduction: 594 mLockPatternView.clearPattern(); 595 break; 596 case HelpScreen: 597 mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern); 598 break; 599 case ChoiceTooShort: 600 mLockPatternView.setDisplayMode(DisplayMode.Wrong); 601 postClearPatternRunnable(); 602 announceAlways = true; 603 break; 604 case FirstChoiceValid: 605 break; 606 case NeedToConfirm: 607 mLockPatternView.clearPattern(); 608 break; 609 case ConfirmWrong: 610 mLockPatternView.setDisplayMode(DisplayMode.Wrong); 611 postClearPatternRunnable(); 612 announceAlways = true; 613 break; 614 case ChoiceConfirmed: 615 break; 616 } 617 618 // If the stage changed, announce the header for accessibility. This 619 // is a no-op when accessibility is disabled. 620 if (previousStage != stage || announceAlways) { 621 mHeaderText.announceForAccessibility(mHeaderText.getText()); 622 } 623 } 624 625 // clear the wrong pattern unless they have started a new one 626 // already postClearPatternRunnable()627 private void postClearPatternRunnable() { 628 mLockPatternView.removeCallbacks(mClearPatternRunnable); 629 mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS); 630 } 631 startSaveAndFinish()632 private void startSaveAndFinish() { 633 if (mSaveAndFinishWorker != null) { 634 Log.w(TAG, "startSaveAndFinish with an existing SaveAndFinishWorker."); 635 return; 636 } 637 638 setRightButtonEnabled(false); 639 640 mSaveAndFinishWorker = new SaveAndFinishWorker(); 641 getFragmentManager().beginTransaction().add(mSaveAndFinishWorker, 642 FRAGMENT_TAG_SAVE_AND_FINISH).commit(); 643 mSaveAndFinishWorker.setListener(this); 644 645 final boolean required = getActivity().getIntent().getBooleanExtra( 646 EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true); 647 mSaveAndFinishWorker.start(mChooseLockSettingsHelper.utils(), required, 648 mHasChallenge, mChallenge, mChosenPattern, mCurrentPattern); 649 } 650 651 @Override onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData)652 public void onChosenLockSaveFinished(boolean wasSecureBefore, Intent resultData) { 653 getActivity().setResult(RESULT_FINISHED, resultData); 654 getActivity().finish(); 655 656 if (!wasSecureBefore) { 657 Intent intent = getRedactionInterstitialIntent(getActivity()); 658 if (intent != null) { 659 startActivity(intent); 660 } 661 } 662 } 663 } 664 665 private static class SaveAndFinishWorker extends SaveChosenLockWorkerBase { 666 667 private List<LockPatternView.Cell> mChosenPattern; 668 private String mCurrentPattern; 669 private boolean mLockVirgin; 670 start(LockPatternUtils utils, boolean credentialRequired, boolean hasChallenge, long challenge, List<LockPatternView.Cell> chosenPattern, String currentPattern)671 public void start(LockPatternUtils utils, boolean credentialRequired, 672 boolean hasChallenge, long challenge, 673 List<LockPatternView.Cell> chosenPattern, String currentPattern) { 674 prepare(utils, credentialRequired, hasChallenge, challenge); 675 676 mCurrentPattern = currentPattern; 677 mChosenPattern = chosenPattern; 678 679 mLockVirgin = !mUtils.isPatternEverChosen(UserHandle.myUserId()); 680 681 start(); 682 } 683 684 @Override saveAndVerifyInBackground()685 protected Intent saveAndVerifyInBackground() { 686 Intent result = null; 687 final int userId = UserHandle.myUserId(); 688 mUtils.saveLockPattern(mChosenPattern, mCurrentPattern, userId); 689 690 if (mHasChallenge) { 691 byte[] token; 692 try { 693 token = mUtils.verifyPattern(mChosenPattern, mChallenge, userId); 694 } catch (RequestThrottledException e) { 695 token = null; 696 } 697 698 if (token == null) { 699 Log.e(TAG, "critical: no token returned for known good pattern"); 700 } 701 702 result = new Intent(); 703 result.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token); 704 } 705 706 return result; 707 } 708 709 @Override finish(Intent resultData)710 protected void finish(Intent resultData) { 711 if (mLockVirgin) { 712 mUtils.setVisiblePatternEnabled(true, UserHandle.myUserId()); 713 } 714 715 super.finish(resultData); 716 } 717 } 718 } 719