Home | History | Annotate | Download | only in settings
      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.LockPatternChecker;
     23 import com.android.internal.widget.LockPatternUtils;
     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.AsyncTask;
     35 import android.os.Bundle;
     36 import android.os.UserHandle;
     37 import android.util.Log;
     38 import android.view.KeyEvent;
     39 import android.view.LayoutInflater;
     40 import android.view.View;
     41 import android.view.ViewGroup;
     42 import android.widget.TextView;
     43 
     44 import java.util.ArrayList;
     45 import java.util.Collections;
     46 import java.util.List;
     47 
     48 /**
     49  * If the user has a lock pattern set already, makes them confirm the existing one.
     50  *
     51  * Then, prompts the user to choose a lock pattern:
     52  * - prompts for initial pattern
     53  * - asks for confirmation / restart
     54  * - saves chosen password when confirmed
     55  */
     56 public class ChooseLockPattern extends SettingsActivity {
     57     /**
     58      * Used by the choose lock pattern wizard to indicate the wizard is
     59      * finished, and each activity in the wizard should finish.
     60      * <p>
     61      * Previously, each activity in the wizard would finish itself after
     62      * starting the next activity. However, this leads to broken 'Back'
     63      * behavior. So, now an activity does not finish itself until it gets this
     64      * result.
     65      */
     66     static final int RESULT_FINISHED = RESULT_FIRST_USER;
     67 
     68     private static final String TAG = "ChooseLockPattern";
     69 
     70     @Override
     71     public Intent getIntent() {
     72         Intent modIntent = new Intent(super.getIntent());
     73         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, getFragmentClass().getName());
     74         return modIntent;
     75     }
     76 
     77     public static Intent createIntent(Context context,
     78             boolean requirePassword, boolean confirmCredentials) {
     79         Intent intent = new Intent(context, ChooseLockPattern.class);
     80         intent.putExtra("key_lock_method", "pattern");
     81         intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, confirmCredentials);
     82         intent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, requirePassword);
     83         return intent;
     84     }
     85 
     86     public static Intent createIntent(Context context,
     87             boolean requirePassword, String pattern) {
     88         Intent intent = createIntent(context, requirePassword, false);
     89         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pattern);
     90         return intent;
     91     }
     92 
     93 
     94     public static Intent createIntent(Context context,
     95             boolean requirePassword, long challenge) {
     96         Intent intent = createIntent(context, requirePassword, 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 (ChooseLockPatternFragment.class.getName().equals(fragmentName)) return true;
    105         return false;
    106     }
    107 
    108     /* package */ Class<? extends Fragment> getFragmentClass() {
    109         return ChooseLockPatternFragment.class;
    110     }
    111 
    112     @Override
    113     public void onCreate(Bundle savedInstanceState) {
    114         // requestWindowFeature(Window.FEATURE_NO_TITLE);
    115         super.onCreate(savedInstanceState);
    116         CharSequence msg = getText(R.string.lockpassword_choose_your_pattern_header);
    117         setTitle(msg);
    118     }
    119 
    120     @Override
    121     public boolean onKeyDown(int keyCode, KeyEvent event) {
    122         // *** TODO ***
    123         // chooseLockPatternFragment.onKeyDown(keyCode, event);
    124         return super.onKeyDown(keyCode, event);
    125     }
    126 
    127     public static class ChooseLockPatternFragment extends InstrumentedFragment
    128             implements View.OnClickListener {
    129 
    130         public static final int CONFIRM_EXISTING_REQUEST = 55;
    131 
    132         // how long after a confirmation message is shown before moving on
    133         static final int INFORMATION_MSG_TIMEOUT_MS = 3000;
    134 
    135         // how long we wait to clear a wrong pattern
    136         private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
    137 
    138         private static final int ID_EMPTY_MESSAGE = -1;
    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         private boolean mDone = false;
    350 
    351         private Runnable mClearPatternRunnable = new Runnable() {
    352             public void run() {
    353                 mLockPatternView.clearPattern();
    354             }
    355         };
    356 
    357         private ChooseLockSettingsHelper mChooseLockSettingsHelper;
    358         private AsyncTask<?, ?, ?> mPendingLockCheck;
    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         }
    372 
    373         @Override
    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
    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             mDone = false;
    439         }
    440 
    441         @Override
    442         public void onResume() {
    443             super.onResume();
    444             mLockPatternView.enableInput();
    445         }
    446 
    447         @Override
    448         public void onPause() {
    449             super.onPause();
    450             if (mPendingLockCheck != null) {
    451                 mPendingLockCheck.cancel(false);
    452                 mPendingLockCheck = null;
    453             }
    454         }
    455 
    456         protected Intent getRedactionInterstitialIntent(Context context) {
    457             return RedactionInterstitial.createStartIntent(context);
    458         }
    459 
    460         public void handleLeftButton() {
    461             if (mUiStage.leftMode == LeftButtonMode.Retry) {
    462                 mChosenPattern = null;
    463                 mLockPatternView.clearPattern();
    464                 updateStage(Stage.Introduction);
    465             } else if (mUiStage.leftMode == LeftButtonMode.Cancel) {
    466                 getActivity().finish();
    467             } else {
    468                 throw new IllegalStateException("left footer button pressed, but stage of " +
    469                         mUiStage + " doesn't make sense");
    470             }
    471         }
    472 
    473         public void handleRightButton() {
    474             if (mUiStage.rightMode == RightButtonMode.Continue) {
    475                 if (mUiStage != Stage.FirstChoiceValid) {
    476                     throw new IllegalStateException("expected ui stage "
    477                             + Stage.FirstChoiceValid + " when button is "
    478                             + RightButtonMode.Continue);
    479                 }
    480                 updateStage(Stage.NeedToConfirm);
    481             } else if (mUiStage.rightMode == RightButtonMode.Confirm) {
    482                 if (mUiStage != Stage.ChoiceConfirmed) {
    483                     throw new IllegalStateException("expected ui stage " + Stage.ChoiceConfirmed
    484                             + " when button is " + RightButtonMode.Confirm);
    485                 }
    486                 saveChosenPatternAndFinish();
    487             } else if (mUiStage.rightMode == RightButtonMode.Ok) {
    488                 if (mUiStage != Stage.HelpScreen) {
    489                     throw new IllegalStateException("Help screen is only mode with ok button, "
    490                             + "but stage is " + mUiStage);
    491                 }
    492                 mLockPatternView.clearPattern();
    493                 mLockPatternView.setDisplayMode(DisplayMode.Correct);
    494                 updateStage(Stage.Introduction);
    495             }
    496         }
    497 
    498         public void onClick(View v) {
    499             if (v == mFooterLeftButton) {
    500                 handleLeftButton();
    501             } else if (v == mFooterRightButton) {
    502                 handleRightButton();
    503             }
    504         }
    505 
    506         public boolean onKeyDown(int keyCode, KeyEvent event) {
    507             if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
    508                 if (mUiStage == Stage.HelpScreen) {
    509                     updateStage(Stage.Introduction);
    510                     return true;
    511                 }
    512             }
    513             if (keyCode == KeyEvent.KEYCODE_MENU && mUiStage == Stage.Introduction) {
    514                 updateStage(Stage.HelpScreen);
    515                 return true;
    516             }
    517             return false;
    518         }
    519 
    520         public void onSaveInstanceState(Bundle outState) {
    521             super.onSaveInstanceState(outState);
    522 
    523             outState.putInt(KEY_UI_STAGE, mUiStage.ordinal());
    524             if (mChosenPattern != null) {
    525                 outState.putString(KEY_PATTERN_CHOICE,
    526                         LockPatternUtils.patternToString(mChosenPattern));
    527             }
    528 
    529             if (mCurrentPattern != null) {
    530                 outState.putString(KEY_CURRENT_PATTERN,
    531                         mCurrentPattern);
    532             }
    533         }
    534 
    535         /**
    536          * Updates the messages and buttons appropriate to what stage the user
    537          * is at in choosing a view.  This doesn't handle clearing out the pattern;
    538          * the pattern is expected to be in the right state.
    539          * @param stage
    540          */
    541         protected void updateStage(Stage stage) {
    542             final Stage previousStage = mUiStage;
    543 
    544             mUiStage = stage;
    545 
    546             // header text, footer text, visibility and
    547             // enabled state all known from the stage
    548             if (stage == Stage.ChoiceTooShort) {
    549                 mHeaderText.setText(
    550                         getResources().getString(
    551                                 stage.headerMessage,
    552                                 LockPatternUtils.MIN_LOCK_PATTERN_SIZE));
    553             } else {
    554                 mHeaderText.setText(stage.headerMessage);
    555             }
    556             if (stage.footerMessage == ID_EMPTY_MESSAGE) {
    557                 mFooterText.setText("");
    558             } else {
    559                 mFooterText.setText(stage.footerMessage);
    560             }
    561 
    562             if (stage.leftMode == LeftButtonMode.Gone) {
    563                 mFooterLeftButton.setVisibility(View.GONE);
    564             } else {
    565                 mFooterLeftButton.setVisibility(View.VISIBLE);
    566                 mFooterLeftButton.setText(stage.leftMode.text);
    567                 mFooterLeftButton.setEnabled(stage.leftMode.enabled);
    568             }
    569 
    570             setRightButtonText(stage.rightMode.text);
    571             setRightButtonEnabled(stage.rightMode.enabled);
    572 
    573             // same for whether the patten is enabled
    574             if (stage.patternEnabled) {
    575                 mLockPatternView.enableInput();
    576             } else {
    577                 mLockPatternView.disableInput();
    578             }
    579 
    580             // the rest of the stuff varies enough that it is easier just to handle
    581             // on a case by case basis.
    582             mLockPatternView.setDisplayMode(DisplayMode.Correct);
    583             boolean announceAlways = false;
    584 
    585             switch (mUiStage) {
    586                 case Introduction:
    587                     mLockPatternView.clearPattern();
    588                     break;
    589                 case HelpScreen:
    590                     mLockPatternView.setPattern(DisplayMode.Animate, mAnimatePattern);
    591                     break;
    592                 case ChoiceTooShort:
    593                     mLockPatternView.setDisplayMode(DisplayMode.Wrong);
    594                     postClearPatternRunnable();
    595                     announceAlways = true;
    596                     break;
    597                 case FirstChoiceValid:
    598                     break;
    599                 case NeedToConfirm:
    600                     mLockPatternView.clearPattern();
    601                     break;
    602                 case ConfirmWrong:
    603                     mLockPatternView.setDisplayMode(DisplayMode.Wrong);
    604                     postClearPatternRunnable();
    605                     announceAlways = true;
    606                     break;
    607                 case ChoiceConfirmed:
    608                     break;
    609             }
    610 
    611             // If the stage changed, announce the header for accessibility. This
    612             // is a no-op when accessibility is disabled.
    613             if (previousStage != stage || announceAlways) {
    614                 mHeaderText.announceForAccessibility(mHeaderText.getText());
    615             }
    616         }
    617 
    618 
    619         // clear the wrong pattern unless they have started a new one
    620         // already
    621         private void postClearPatternRunnable() {
    622             mLockPatternView.removeCallbacks(mClearPatternRunnable);
    623             mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
    624         }
    625 
    626         private void saveChosenPatternAndFinish() {
    627             if (mDone) return;
    628             LockPatternUtils utils = mChooseLockSettingsHelper.utils();
    629             final boolean lockVirgin = !utils.isPatternEverChosen(UserHandle.myUserId());
    630 
    631             boolean wasSecureBefore = utils.isSecure(UserHandle.myUserId());
    632 
    633             final boolean required = getActivity().getIntent().getBooleanExtra(
    634                     EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
    635 
    636             utils.setCredentialRequiredToDecrypt(required);
    637             utils.saveLockPattern(mChosenPattern, mCurrentPattern, UserHandle.myUserId());
    638 
    639             if (lockVirgin) {
    640                 utils.setVisiblePatternEnabled(true, UserHandle.myUserId());
    641             }
    642 
    643             if (mHasChallenge) {
    644                 startVerifyPattern(utils, wasSecureBefore);
    645             } else {
    646                 if (!wasSecureBefore) {
    647                     Intent intent = getRedactionInterstitialIntent(getActivity());
    648                     if (intent != null) {
    649                         startActivity(intent);
    650                     }
    651                 }
    652                 getActivity().setResult(RESULT_FINISHED);
    653                 doFinish();
    654             }
    655         }
    656 
    657         private void startVerifyPattern(LockPatternUtils utils, final boolean wasSecureBefore) {
    658             mLockPatternView.disableInput();
    659             if (mPendingLockCheck != null) {
    660                 mPendingLockCheck.cancel(false);
    661             }
    662 
    663             mPendingLockCheck = LockPatternChecker.verifyPattern(
    664                     utils,
    665                     mChosenPattern,
    666                     mChallenge,
    667                     UserHandle.myUserId(),
    668                     new LockPatternChecker.OnVerifyCallback() {
    669                         @Override
    670                         public void onVerified(byte[] token, int timeoutMs) {
    671                             if (token == null) {
    672                                 Log.e(TAG, "critical: no token returned for known good pattern");
    673                             }
    674 
    675                             mLockPatternView.enableInput();
    676                             mPendingLockCheck = null;
    677 
    678                             if (!wasSecureBefore) {
    679                                 Intent intent = getRedactionInterstitialIntent(getActivity());
    680                                 if (intent != null) {
    681                                     startActivity(intent);
    682                                 }
    683                             }
    684 
    685                             Intent intent = new Intent();
    686                             intent.putExtra(
    687                                     ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
    688                             getActivity().setResult(RESULT_FINISHED, intent);
    689                             doFinish();
    690                         }
    691                     });
    692         }
    693 
    694         private void doFinish() {
    695             getActivity().finish();
    696             mDone = true;
    697         }
    698     }
    699 }
    700