blob: 44baccc65045c9877887b2f4d531443a7c982ea3 [file] [log] [blame]
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2008 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.widget.LockPatternUtils;
20import com.android.internal.widget.LockPatternView;
21import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
22
23import android.app.Activity;
24import android.content.Intent;
25import android.os.CountDownTimer;
26import android.os.SystemClock;
27import android.os.Bundle;
28import android.widget.TextView;
29import android.view.Window;
30
31import java.util.List;
32
33/**
34 * Launch this when you want the user to confirm their lock pattern.
35 *
36 * Sets an activity result of {@link Activity#RESULT_OK} when the user
37 * successfully confirmed their pattern.
38 */
39public class ConfirmLockPattern extends Activity {
40
41 /**
42 * Names of {@link CharSequence} fields within the originating {@link Intent}
43 * that are used to configure the keyguard confirmation view's labeling.
44 * The view will use the system-defined resource strings for any labels that
45 * the caller does not supply.
46 */
47 public static final String HEADER_TEXT = "com.android.settings.ConfirmLockPattern.header";
48 public static final String FOOTER_TEXT = "com.android.settings.ConfirmLockPattern.footer";
49 public static final String HEADER_WRONG_TEXT = "com.android.settings.ConfirmLockPattern.header_wrong";
50 public static final String FOOTER_WRONG_TEXT = "com.android.settings.ConfirmLockPattern.footer_wrong";
51
52 // how long we wait to clear a wrong pattern
53 private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
54
55 private static final String KEY_NUM_WRONG_ATTEMPTS = "num_wrong_attempts";
56
57 private LockPatternView mLockPatternView;
58 private LockPatternUtils mLockPatternUtils;
59 private int mNumWrongConfirmAttempts;
60 private CountDownTimer mCountdownTimer;
61
62 private TextView mHeaderTextView;
63 private TextView mFooterTextView;
64
65 // caller-supplied text for various prompts
66 private CharSequence mHeaderText;
67 private CharSequence mFooterText;
68 private CharSequence mHeaderWrongText;
69 private CharSequence mFooterWrongText;
70
71
72 private enum Stage {
73 NeedToUnlock,
74 NeedToUnlockWrong,
75 LockedOut
76 }
77
78 @Override
79 protected void onCreate(Bundle savedInstanceState) {
80 super.onCreate(savedInstanceState);
81
82 mLockPatternUtils = new LockPatternUtils(getContentResolver());
83
84 requestWindowFeature(Window.FEATURE_NO_TITLE);
85 setContentView(R.layout.confirm_lock_pattern);
86
87 mHeaderTextView = (TextView) findViewById(R.id.headerText);
88 mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
89 mFooterTextView = (TextView) findViewById(R.id.footerText);
90
91 // make it so unhandled touch events within the unlock screen go to the
92 // lock pattern view.
93 final LinearLayoutWithDefaultTouchRecepient topLayout
94 = (LinearLayoutWithDefaultTouchRecepient) findViewById(
95 R.id.topLayout);
96 topLayout.setDefaultTouchRecepient(mLockPatternView);
97
98 Intent intent = getIntent();
99 if (intent != null) {
100 mHeaderText = intent.getCharSequenceExtra(HEADER_TEXT);
101 mFooterText = intent.getCharSequenceExtra(FOOTER_TEXT);
102 mHeaderWrongText = intent.getCharSequenceExtra(HEADER_WRONG_TEXT);
103 mFooterWrongText = intent.getCharSequenceExtra(FOOTER_WRONG_TEXT);
104 }
105
The Android Open Source Project47729682009-02-19 10:57:36 -0800106 mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700107 mLockPatternView.setOnPatternListener(mConfirmExistingLockPatternListener);
108 updateStage(Stage.NeedToUnlock);
109
110 if (savedInstanceState != null) {
111 mNumWrongConfirmAttempts = savedInstanceState.getInt(KEY_NUM_WRONG_ATTEMPTS);
112 } else {
113 // on first launch, if no lock pattern is set, then finish with
114 // success (don't want user to get stuck confirming something that
115 // doesn't exist).
116 if (!mLockPatternUtils.savedPatternExists()) {
117 setResult(RESULT_OK);
118 finish();
119 }
120 }
121 }
122
123 @Override
124 protected void onSaveInstanceState(Bundle outState) {
125 // deliberately not calling super since we are managing this in full
126 outState.putInt(KEY_NUM_WRONG_ATTEMPTS, mNumWrongConfirmAttempts);
127 }
128
129 @Override
130 protected void onPause() {
131 super.onPause();
132
133 if (mCountdownTimer != null) {
134 mCountdownTimer.cancel();
135 }
136 }
137
138 @Override
139 protected void onResume() {
140 super.onResume();
141
142 // if the user is currently locked out, enforce it.
143 long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
144 if (deadline != 0) {
145 handleAttemptLockout(deadline);
146 }
147 }
148
149 private void updateStage(Stage stage) {
150
151 switch (stage) {
152 case NeedToUnlock:
153 if (mHeaderText != null) {
154 mHeaderTextView.setText(mHeaderText);
155 } else {
156 mHeaderTextView.setText(R.string.lockpattern_need_to_unlock);
157 }
158 if (mFooterText != null) {
159 mFooterTextView.setText(mFooterText);
160 } else {
161 mFooterTextView.setText(R.string.lockpattern_need_to_unlock_footer);
162 }
163
164 mLockPatternView.setEnabled(true);
165 mLockPatternView.enableInput();
166 break;
167 case NeedToUnlockWrong:
168 if (mHeaderWrongText != null) {
169 mHeaderTextView.setText(mHeaderWrongText);
170 } else {
171 mHeaderTextView.setText(R.string.lockpattern_need_to_unlock_wrong);
172 }
173 if (mFooterWrongText != null) {
174 mFooterTextView.setText(mFooterWrongText);
175 } else {
176 mFooterTextView.setText(R.string.lockpattern_need_to_unlock_wrong_footer);
177 }
178
179 mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
180 mLockPatternView.setEnabled(true);
181 mLockPatternView.enableInput();
182 break;
183 case LockedOut:
184 mLockPatternView.clearPattern();
185 // enabled = false means: disable input, and have the
186 // appearance of being disabled.
187 mLockPatternView.setEnabled(false); // appearance of being disabled
188 break;
189 }
190 }
191
192 private Runnable mClearPatternRunnable = new Runnable() {
193 public void run() {
194 mLockPatternView.clearPattern();
195 }
196 };
197
198 // clear the wrong pattern unless they have started a new one
199 // already
200 private void postClearPatternRunnable() {
201 mLockPatternView.removeCallbacks(mClearPatternRunnable);
202 mLockPatternView.postDelayed(mClearPatternRunnable, WRONG_PATTERN_CLEAR_TIMEOUT_MS);
203 }
204
205 /**
206 * The pattern listener that responds according to a user confirming
207 * an existing lock pattern.
208 */
209 private LockPatternView.OnPatternListener mConfirmExistingLockPatternListener = new LockPatternView.OnPatternListener() {
210
211 public void onPatternStart() {
212 mLockPatternView.removeCallbacks(mClearPatternRunnable);
213 }
214
215 public void onPatternCleared() {
216 mLockPatternView.removeCallbacks(mClearPatternRunnable);
217 }
218
219 public void onPatternDetected(List<LockPatternView.Cell> pattern) {
220 if (mLockPatternUtils.checkPattern(pattern)) {
221 setResult(RESULT_OK);
222 finish();
223 } else {
The Android Open Source Project47729682009-02-19 10:57:36 -0800224 if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL &&
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700225 ++mNumWrongConfirmAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
The Android Open Source Project47729682009-02-19 10:57:36 -0800226 long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
The Android Open Source Projectde2d9f52008-10-21 07:00:00 -0700227 handleAttemptLockout(deadline);
228 } else {
229 updateStage(Stage.NeedToUnlockWrong);
230 postClearPatternRunnable();
231 }
232 }
233 }
234 };
235
236
237 private void handleAttemptLockout(long elapsedRealtimeDeadline) {
238 updateStage(Stage.LockedOut);
239 long elapsedRealtime = SystemClock.elapsedRealtime();
240 mCountdownTimer = new CountDownTimer(
241 elapsedRealtimeDeadline - elapsedRealtime,
242 LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
243
244 @Override
245 public void onTick(long millisUntilFinished) {
246 mHeaderTextView.setText(R.string.lockpattern_too_many_failed_confirmation_attempts_header);
247 final int secondsCountdown = (int) (millisUntilFinished / 1000);
248 mFooterTextView.setText(getString(
249 R.string.lockpattern_too_many_failed_confirmation_attempts_footer,
250 secondsCountdown));
251 }
252
253 @Override
254 public void onFinish() {
255 mNumWrongConfirmAttempts = 0;
256 updateStage(Stage.NeedToUnlock);
257 }
258 }.start();
259 }
260}