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;
91 private boolean mUsingFingerprint = false;
93 // required constructor for fragments
94 public ConfirmLockPasswordFragment() {
99 public void onCreate(Bundle savedInstanceState) {
100 super.onCreate(savedInstanceState);
101 mLockPatternUtils = new LockPatternUtils(getActivity());
102 if (savedInstanceState != null) {
103 mNumWrongConfirmAttempts = savedInstanceState.getInt(
104 KEY_NUM_WRONG_CONFIRM_ATTEMPTS, 0);
109 public View onCreateView(LayoutInflater inflater, ViewGroup container,
110 Bundle savedInstanceState) {
111 final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
112 UserHandle.myUserId());
113 View view = inflater.inflate(R.layout.confirm_lock_password, null);
115 mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
116 mPasswordEntry.setOnEditorActionListener(this);
117 mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
119 mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
120 mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
121 mErrorTextView = (TextView) view.findViewById(R.id.errorText);
122 mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
123 || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
124 || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality;
126 mImm = (InputMethodManager) getActivity().getSystemService(
127 Context.INPUT_METHOD_SERVICE);
129 Intent intent = getActivity().getIntent();
130 if (intent != null) {
131 CharSequence headerMessage = intent.getCharSequenceExtra(
132 ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
133 CharSequence detailsMessage = intent.getCharSequenceExtra(
134 ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
135 if (TextUtils.isEmpty(headerMessage)) {
136 headerMessage = getString(getDefaultHeader());
138 if (TextUtils.isEmpty(detailsMessage)) {
139 detailsMessage = getString(getDefaultDetails());
141 mHeaderTextView.setText(headerMessage);
142 mDetailsTextView.setText(detailsMessage);
144 int currentType = mPasswordEntry.getInputType();
145 mPasswordEntry.setInputType(mIsAlpha ? currentType
146 : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
150 private int getDefaultHeader() {
151 return mIsAlpha ? R.string.lockpassword_confirm_your_password_header
152 : R.string.lockpassword_confirm_your_pin_header;
155 private int getDefaultDetails() {
156 return mIsAlpha ? R.string.lockpassword_confirm_your_password_generic
157 : R.string.lockpassword_confirm_your_pin_generic;
160 private int getErrorMessage() {
161 return mIsAlpha ? R.string.lockpassword_invalid_password
162 : R.string.lockpassword_invalid_pin;
166 public void onPause() {
168 if (mCountdownTimer != null) {
169 mCountdownTimer.cancel();
170 mCountdownTimer = null;
172 if (mPendingLockCheck != null) {
173 mPendingLockCheck.cancel(false);
174 mPendingLockCheck = null;
179 protected int getMetricsCategory() {
180 return MetricsLogger.CONFIRM_LOCK_PASSWORD;
184 public void onResume() {
186 long deadline = mLockPatternUtils.getLockoutAttemptDeadline(UserHandle.myUserId());
188 handleAttemptLockout(deadline);
195 public void onSaveInstanceState(Bundle outState) {
196 super.onSaveInstanceState(outState);
197 outState.putInt(KEY_NUM_WRONG_CONFIRM_ATTEMPTS, mNumWrongConfirmAttempts);
201 protected void authenticationSucceeded() {
202 Intent intent = new Intent();
203 getActivity().setResult(RESULT_OK, intent);
204 getActivity().finish();
208 public void onFingerprintIconVisibilityChanged(boolean visible) {
209 mUsingFingerprint = visible;
212 private void resetState() {
213 mPasswordEntry.setEnabled(true);
214 mPasswordEntryInputDisabler.setInputEnabled(true);
215 if (shouldAutoShowSoftKeyboard()) {
216 mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
220 private boolean shouldAutoShowSoftKeyboard() {
221 return mPasswordEntry.isEnabled() && !mUsingFingerprint;
224 public void onWindowFocusChanged(boolean hasFocus) {
228 // Post to let window focus logic to finish to allow soft input show/hide properly.
229 mPasswordEntry.post(new Runnable() {
232 if (shouldAutoShowSoftKeyboard()) {
237 mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(),
238 InputMethodManager.HIDE_IMPLICIT_ONLY);
243 private void handleNext() {
244 mPasswordEntryInputDisabler.setInputEnabled(false);
245 if (mPendingLockCheck != null) {
246 mPendingLockCheck.cancel(false);
249 final String pin = mPasswordEntry.getText().toString();
250 final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
251 ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
252 Intent intent = new Intent();
253 if (verifyChallenge) {
254 if (isInternalActivity()) {
255 startVerifyPassword(pin, intent);
259 startCheckPassword(pin, intent);
263 onPasswordChecked(false, intent, 0);
266 private boolean isInternalActivity() {
267 return getActivity() instanceof ConfirmLockPassword.InternalActivity;
270 private void startVerifyPassword(final String pin, final Intent intent) {
271 long challenge = getActivity().getIntent().getLongExtra(
272 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
273 mPendingLockCheck = LockPatternChecker.verifyPassword(
277 UserHandle.myUserId(),
278 new LockPatternChecker.OnVerifyCallback() {
280 public void onVerified(byte[] token, int timeoutMs) {
281 mPendingLockCheck = null;
282 boolean matched = false;
286 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
289 onPasswordChecked(matched, intent, timeoutMs);
294 private void startCheckPassword(final String pin, final Intent intent) {
295 mPendingLockCheck = LockPatternChecker.checkPassword(
298 UserHandle.myUserId(),
299 new LockPatternChecker.OnCheckCallback() {
301 public void onChecked(boolean matched, int timeoutMs) {
302 mPendingLockCheck = null;
303 if (matched && isInternalActivity()) {
304 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
305 mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
306 : StorageManager.CRYPT_TYPE_PIN);
308 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
310 onPasswordChecked(matched, intent, timeoutMs);
315 private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs) {
316 mPasswordEntryInputDisabler.setInputEnabled(true);
318 getActivity().setResult(RESULT_OK, intent);
319 getActivity().finish();
322 long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
323 UserHandle.myUserId(), timeoutMs);
324 handleAttemptLockout(deadline);
326 showError(getErrorMessage());
331 private void handleAttemptLockout(long elapsedRealtimeDeadline) {
332 long elapsedRealtime = SystemClock.elapsedRealtime();
333 mPasswordEntry.setEnabled(false);
334 mCountdownTimer = new CountDownTimer(
335 elapsedRealtimeDeadline - elapsedRealtime,
336 LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
339 public void onTick(long millisUntilFinished) {
340 final int secondsCountdown = (int) (millisUntilFinished / 1000);
342 R.string.lockpattern_too_many_failed_confirmation_attempts,
343 secondsCountdown), 0);
347 public void onFinish() {
349 mErrorTextView.setText("");
350 mNumWrongConfirmAttempts = 0;
355 public void onClick(View v) {
357 case R.id.next_button:
361 case R.id.cancel_button:
362 getActivity().setResult(RESULT_CANCELED);
363 getActivity().finish();
368 private void showError(int msg) {
369 showError(msg, ERROR_MESSAGE_TIMEOUT);
372 private final Runnable mResetErrorRunnable = new Runnable() {
374 mErrorTextView.setText("");
378 private void showError(CharSequence msg, long timeout) {
379 mErrorTextView.setText(msg);
380 mErrorTextView.announceForAccessibility(mErrorTextView.getText());
381 mPasswordEntry.setText(null);
382 mHandler.removeCallbacks(mResetErrorRunnable);
384 mHandler.postDelayed(mResetErrorRunnable, timeout);
388 private void showError(int msg, long timeout) {
389 showError(getText(msg), timeout);
392 // {@link OnEditorActionListener} methods.
393 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
394 // Check if this was the result of hitting the enter or "done" key
395 if (actionId == EditorInfo.IME_NULL
396 || actionId == EditorInfo.IME_ACTION_DONE
397 || actionId == EditorInfo.IME_ACTION_NEXT) {