ChooseLockPattern.java revision 00da4f6d3b7cd2aa347b88e3f15b3b6ddd3e5803
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 com.android.internal.logging.MetricsLogger;
20import com.google.android.collect.Lists;
21import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
22import com.android.internal.widget.LockPatternChecker;
23import com.android.internal.widget.LockPatternUtils;
24import com.android.internal.widget.LockPatternView;
25import com.android.internal.widget.LockPatternView.Cell;
26import com.android.settings.notification.RedactionInterstitial;
27
28import static com.android.internal.widget.LockPatternView.DisplayMode;
29
30import android.app.Activity;
31import android.app.Fragment;
32import android.content.Context;
33import android.content.Intent;
34import android.os.AsyncTask;
35import android.os.Bundle;
36import android.os.UserHandle;
37import android.util.Log;
38import android.view.KeyEvent;
39import android.view.LayoutInflater;
40import android.view.View;
41import android.view.ViewGroup;
42import android.widget.TextView;
43
44import java.util.ArrayList;
45import java.util.Collections;
46import 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 */
56public 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