2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings;
19 import android.os.UserHandle;
20 import android.text.TextUtils;
21 import com.android.internal.logging.MetricsLogger;
22 import com.android.internal.widget.LockPatternChecker;
23 import com.android.internal.widget.LockPatternUtils;
24 import com.android.internal.widget.TextViewInputDisabler;
26 import android.app.Fragment;
27 import android.app.admin.DevicePolicyManager;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.os.AsyncTask;
31 import android.os.Bundle;
32 import android.os.CountDownTimer;
33 import android.os.Handler;
34 import android.os.SystemClock;
35 import android.os.storage.StorageManager;
36 import android.text.InputType;
37 import android.view.KeyEvent;
38 import android.view.LayoutInflater;
39 import android.view.View;
40 import android.view.View.OnClickListener;
41 import android.view.ViewGroup;
42 import android.view.inputmethod.EditorInfo;
43 import android.view.inputmethod.InputMethodManager;
44 import android.widget.TextView;
45 import android.widget.TextView.OnEditorActionListener;
47 public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
49 public static class InternalActivity extends ConfirmLockPassword {
53 public Intent getIntent() {
54 Intent modIntent = new Intent(super.getIntent());
55 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPasswordFragment.class.getName());
60 protected boolean isValidFragment(String fragmentName) {
61 if (ConfirmLockPasswordFragment.class.getName().equals(fragmentName)) return true;
66 public void onWindowFocusChanged(boolean hasFocus) {
67 super.onWindowFocusChanged(hasFocus);
68 Fragment fragment = getFragmentManager().findFragmentById(R.id.main_content);
69 if (fragment != null && fragment instanceof ConfirmLockPasswordFragment) {
70 ((ConfirmLockPasswordFragment)fragment).onWindowFocusChanged(hasFocus);
74 public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
75 implements OnClickListener, OnEditorActionListener {
76 private static final String KEY_NUM_WRONG_CONFIRM_ATTEMPTS
77 = "confirm_lock_password_fragment.key_num_wrong_confirm_attempts";
78 private static final long ERROR_MESSAGE_TIMEOUT = 3000;
79 private TextView mPasswordEntry;
80 private TextViewInputDisabler mPasswordEntryInputDisabler;
81 private LockPatternUtils mLockPatternUtils;
82 private AsyncTask<?, ?, ?> mPendingLockCheck;
83 private TextView mHeaderTextView;
84 private TextView mDetailsTextView;
85 private TextView mErrorTextView;
86 private Handler mHandler = new Handler();
87 private int mNumWrongConfirmAttempts;
88 private CountDownTimer mCountdownTimer;
89 private boolean mIsAlpha;
90 private InputMethodManager mImm;
92 // required constructor for fragments
93 public ConfirmLockPasswordFragment() {
98 public void onCreate(Bundle savedInstanceState) {
99 super.onCreate(savedInstanceState);
100 mLockPatternUtils = new LockPatternUtils(getActivity());
101 if (savedInstanceState != null) {
102 mNumWrongConfirmAttempts = savedInstanceState.getInt(
103 KEY_NUM_WRONG_CONFIRM_ATTEMPTS, 0);
108 public View onCreateView(LayoutInflater inflater, ViewGroup container,
109 Bundle savedInstanceState) {
110 final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
111 UserHandle.myUserId());
112 View view = inflater.inflate(R.layout.confirm_lock_password, null);
114 mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
115 mPasswordEntry.setOnEditorActionListener(this);
116 mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
118 mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
119 mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
120 mErrorTextView = (TextView) view.findViewById(R.id.errorText);
121 mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
122 || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
123 || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality;
125 mImm = (InputMethodManager) getActivity().getSystemService(
126 Context.INPUT_METHOD_SERVICE);
128 Intent intent = getActivity().getIntent();
129 if (intent != null) {
130 CharSequence headerMessage = intent.getCharSequenceExtra(
131 ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
132 CharSequence detailsMessage = intent.getCharSequenceExtra(
133 ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
134 if (TextUtils.isEmpty(headerMessage)) {
135 headerMessage = getString(getDefaultHeader());
137 if (TextUtils.isEmpty(detailsMessage)) {
138 detailsMessage = getString(getDefaultDetails());
140 mHeaderTextView.setText(headerMessage);
141 mDetailsTextView.setText(detailsMessage);
143 int currentType = mPasswordEntry.getInputType();
144 mPasswordEntry.setInputType(mIsAlpha ? currentType
145 : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
149 private int getDefaultHeader() {
150 return mIsAlpha ? R.string.lockpassword_confirm_your_password_header
151 : R.string.lockpassword_confirm_your_pin_header;
154 private int getDefaultDetails() {
155 return mIsAlpha ? R.string.lockpassword_confirm_your_password_generic
156 : R.string.lockpassword_confirm_your_pin_generic;
159 private int getErrorMessage() {
160 return mIsAlpha ? R.string.lockpassword_invalid_password
161 : R.string.lockpassword_invalid_pin;
165 public void onPause() {
167 if (mCountdownTimer != null) {
168 mCountdownTimer.cancel();
169 mCountdownTimer = null;
171 if (mPendingLockCheck != null) {
172 mPendingLockCheck.cancel(false);
173 mPendingLockCheck = null;
178 protected int getMetricsCategory() {
179 return MetricsLogger.CONFIRM_LOCK_PASSWORD;
183 public void onResume() {
185 long deadline = mLockPatternUtils.getLockoutAttemptDeadline(UserHandle.myUserId());
187 handleAttemptLockout(deadline);
194 public void onSaveInstanceState(Bundle outState) {
195 super.onSaveInstanceState(outState);
196 outState.putInt(KEY_NUM_WRONG_CONFIRM_ATTEMPTS, mNumWrongConfirmAttempts);
200 protected void authenticationSucceeded() {
201 Intent intent = new Intent();
202 getActivity().setResult(RESULT_OK, intent);
203 getActivity().finish();
206 private void resetState() {
207 mPasswordEntry.setEnabled(true);
208 mPasswordEntryInputDisabler.setInputEnabled(true);
209 mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
212 public void onWindowFocusChanged(boolean hasFocus) {
216 // Post to let window focus logic to finish to allow soft input show/hide properly.
217 mPasswordEntry.post(new Runnable() {
220 if (mPasswordEntry.isEnabled()) {
225 mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(),
226 InputMethodManager.HIDE_IMPLICIT_ONLY);
231 private void handleNext() {
232 mPasswordEntryInputDisabler.setInputEnabled(false);
233 if (mPendingLockCheck != null) {
234 mPendingLockCheck.cancel(false);
237 final String pin = mPasswordEntry.getText().toString();
238 final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
239 ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
240 Intent intent = new Intent();
241 if (verifyChallenge) {
242 if (isInternalActivity()) {
243 startVerifyPassword(pin, intent);
247 startCheckPassword(pin, intent);
251 onPasswordChecked(false, intent);
254 private boolean isInternalActivity() {
255 return getActivity() instanceof ConfirmLockPassword.InternalActivity;
258 private void startVerifyPassword(final String pin, final Intent intent) {
259 long challenge = getActivity().getIntent().getLongExtra(
260 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
261 mPendingLockCheck = LockPatternChecker.verifyPassword(
265 UserHandle.myUserId(),
266 new LockPatternChecker.OnVerifyCallback() {
268 public void onVerified(byte[] token) {
269 mPendingLockCheck = null;
270 boolean matched = false;
274 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
277 onPasswordChecked(matched, intent);
282 private void startCheckPassword(final String pin, final Intent intent) {
283 mPendingLockCheck = LockPatternChecker.checkPassword(
286 UserHandle.myUserId(),
287 new LockPatternChecker.OnCheckCallback() {
289 public void onChecked(boolean matched) {
290 mPendingLockCheck = null;
291 if (matched && isInternalActivity()) {
292 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
293 mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
294 : StorageManager.CRYPT_TYPE_PIN);
296 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
298 onPasswordChecked(matched, intent);
303 private void onPasswordChecked(boolean matched, Intent intent) {
304 mPasswordEntryInputDisabler.setInputEnabled(true);
306 getActivity().setResult(RESULT_OK, intent);
307 getActivity().finish();
309 if (++mNumWrongConfirmAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
310 long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
311 UserHandle.myUserId());
312 handleAttemptLockout(deadline);
314 showError(getErrorMessage());
319 private void handleAttemptLockout(long elapsedRealtimeDeadline) {
320 long elapsedRealtime = SystemClock.elapsedRealtime();
321 mPasswordEntry.setEnabled(false);
322 mCountdownTimer = new CountDownTimer(
323 elapsedRealtimeDeadline - elapsedRealtime,
324 LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
327 public void onTick(long millisUntilFinished) {
328 final int secondsCountdown = (int) (millisUntilFinished / 1000);
330 R.string.lockpattern_too_many_failed_confirmation_attempts,
331 secondsCountdown), 0);
335 public void onFinish() {
337 mErrorTextView.setText("");
338 mNumWrongConfirmAttempts = 0;
343 public void onClick(View v) {
345 case R.id.next_button:
349 case R.id.cancel_button:
350 getActivity().setResult(RESULT_CANCELED);
351 getActivity().finish();
356 private void showError(int msg) {
357 showError(msg, ERROR_MESSAGE_TIMEOUT);
360 private final Runnable mResetErrorRunnable = new Runnable() {
362 mErrorTextView.setText("");
366 private void showError(CharSequence msg, long timeout) {
367 mErrorTextView.setText(msg);
368 mErrorTextView.announceForAccessibility(mErrorTextView.getText());
369 mPasswordEntry.setText(null);
370 mHandler.removeCallbacks(mResetErrorRunnable);
372 mHandler.postDelayed(mResetErrorRunnable, timeout);
376 private void showError(int msg, long timeout) {
377 showError(getText(msg), timeout);
380 // {@link OnEditorActionListener} methods.
381 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
382 // Check if this was the result of hitting the enter or "done" key
383 if (actionId == EditorInfo.IME_NULL
384 || actionId == EditorInfo.IME_ACTION_DONE
385 || actionId == EditorInfo.IME_ACTION_NEXT) {