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