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