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.app.Fragment;
20 import android.app.admin.DevicePolicyManager;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.os.AsyncTask;
24 import android.os.Bundle;
25 import android.os.CountDownTimer;
26 import android.os.SystemClock;
27 import android.os.UserManager;
28 import android.os.storage.StorageManager;
29 import android.text.InputType;
30 import android.text.TextUtils;
31 import android.view.KeyEvent;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.View.OnClickListener;
35 import android.view.ViewGroup;
36 import android.view.animation.AnimationUtils;
37 import android.view.inputmethod.EditorInfo;
38 import android.view.inputmethod.InputMethodManager;
39 import android.widget.TextView;
40 import android.widget.TextView.OnEditorActionListener;
42 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
43 import com.android.internal.widget.LockPatternChecker;
44 import com.android.internal.widget.LockPatternUtils;
45 import com.android.internal.widget.TextViewInputDisabler;
46 import com.android.settingslib.animation.AppearAnimationUtils;
47 import com.android.settingslib.animation.DisappearAnimationUtils;
49 import java.util.ArrayList;
51 public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
53 // The index of the array is isStrongAuth << 2 + isProfile << 1 + isAlpha.
54 private static final int[] DETAIL_TEXTS = new int[] {
55 R.string.lockpassword_confirm_your_pin_generic,
56 R.string.lockpassword_confirm_your_password_generic,
57 R.string.lockpassword_confirm_your_pin_generic_profile,
58 R.string.lockpassword_confirm_your_password_generic_profile,
59 R.string.lockpassword_strong_auth_required_reason_restart_device_pin,
60 R.string.lockpassword_strong_auth_required_reason_restart_device_password,
61 R.string.lockpassword_strong_auth_required_reason_restart_work_pin,
62 R.string.lockpassword_strong_auth_required_reason_restart_work_password,
65 public static class InternalActivity extends ConfirmLockPassword {
69 public Intent getIntent() {
70 Intent modIntent = new Intent(super.getIntent());
71 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ConfirmLockPasswordFragment.class.getName());
76 protected boolean isValidFragment(String fragmentName) {
77 if (ConfirmLockPasswordFragment.class.getName().equals(fragmentName)) return true;
82 public void onWindowFocusChanged(boolean hasFocus) {
83 super.onWindowFocusChanged(hasFocus);
84 Fragment fragment = getFragmentManager().findFragmentById(R.id.main_content);
85 if (fragment != null && fragment instanceof ConfirmLockPasswordFragment) {
86 ((ConfirmLockPasswordFragment)fragment).onWindowFocusChanged(hasFocus);
90 public static class ConfirmLockPasswordFragment extends ConfirmDeviceCredentialBaseFragment
91 implements OnClickListener, OnEditorActionListener,
92 CredentialCheckResultTracker.Listener {
93 private static final long ERROR_MESSAGE_TIMEOUT = 3000;
94 private static final String FRAGMENT_TAG_CHECK_LOCK_RESULT = "check_lock_result";
95 private TextView mPasswordEntry;
96 private TextViewInputDisabler mPasswordEntryInputDisabler;
97 private AsyncTask<?, ?, ?> mPendingLockCheck;
98 private CredentialCheckResultTracker mCredentialCheckResultTracker;
99 private boolean mDisappearing = false;
100 private TextView mHeaderTextView;
101 private TextView mDetailsTextView;
102 private CountDownTimer mCountdownTimer;
103 private boolean mIsAlpha;
104 private InputMethodManager mImm;
105 private boolean mUsingFingerprint = false;
106 private AppearAnimationUtils mAppearAnimationUtils;
107 private DisappearAnimationUtils mDisappearAnimationUtils;
108 private boolean mBlockImm;
110 // required constructor for fragments
111 public ConfirmLockPasswordFragment() {
116 public void onCreate(Bundle savedInstanceState) {
117 super.onCreate(savedInstanceState);
121 public View onCreateView(LayoutInflater inflater, ViewGroup container,
122 Bundle savedInstanceState) {
123 final int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(
126 ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
127 View view = inflater.inflate(
128 activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.INTERNAL
129 ? R.layout.confirm_lock_password_internal
130 : R.layout.confirm_lock_password,
134 mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
135 mPasswordEntry.setOnEditorActionListener(this);
136 mPasswordEntryInputDisabler = new TextViewInputDisabler(mPasswordEntry);
138 mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
139 mDetailsTextView = (TextView) view.findViewById(R.id.detailsText);
140 mErrorTextView = (TextView) view.findViewById(R.id.errorText);
141 mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == storedQuality
142 || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == storedQuality
143 || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == storedQuality
144 || DevicePolicyManager.PASSWORD_QUALITY_MANAGED == storedQuality;
146 mImm = (InputMethodManager) getActivity().getSystemService(
147 Context.INPUT_METHOD_SERVICE);
149 Intent intent = getActivity().getIntent();
150 if (intent != null) {
151 CharSequence headerMessage = intent.getCharSequenceExtra(
152 ConfirmDeviceCredentialBaseFragment.HEADER_TEXT);
153 CharSequence detailsMessage = intent.getCharSequenceExtra(
154 ConfirmDeviceCredentialBaseFragment.DETAILS_TEXT);
155 if (TextUtils.isEmpty(headerMessage)) {
156 headerMessage = getString(getDefaultHeader());
158 if (TextUtils.isEmpty(detailsMessage)) {
159 detailsMessage = getString(getDefaultDetails());
161 mHeaderTextView.setText(headerMessage);
162 mDetailsTextView.setText(detailsMessage);
164 int currentType = mPasswordEntry.getInputType();
165 mPasswordEntry.setInputType(mIsAlpha ? currentType
166 : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
167 mAppearAnimationUtils = new AppearAnimationUtils(getContext(),
168 220, 2f /* translationScale */, 1f /* delayScale*/,
169 AnimationUtils.loadInterpolator(getContext(),
170 android.R.interpolator.linear_out_slow_in));
171 mDisappearAnimationUtils = new DisappearAnimationUtils(getContext(),
172 110, 1f /* translationScale */,
173 0.5f /* delayScale */, AnimationUtils.loadInterpolator(
174 getContext(), android.R.interpolator.fast_out_linear_in));
175 setAccessibilityTitle(mHeaderTextView.getText());
177 mCredentialCheckResultTracker = (CredentialCheckResultTracker) getFragmentManager()
178 .findFragmentByTag(FRAGMENT_TAG_CHECK_LOCK_RESULT);
179 if (mCredentialCheckResultTracker == null) {
180 mCredentialCheckResultTracker = new CredentialCheckResultTracker();
181 getFragmentManager().beginTransaction().add(mCredentialCheckResultTracker,
182 FRAGMENT_TAG_CHECK_LOCK_RESULT).commit();
188 private int getDefaultHeader() {
189 return mIsAlpha ? R.string.lockpassword_confirm_your_password_header
190 : R.string.lockpassword_confirm_your_pin_header;
193 private int getDefaultDetails() {
194 boolean isStrongAuthRequired = isFingerprintDisallowedByStrongAuth();
195 boolean isProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId);
196 // Map boolean flags to an index by isStrongAuth << 2 + isProfile << 1 + isAlpha.
197 int index = ((isStrongAuthRequired ? 1 : 0) << 2) + ((isProfile ? 1 : 0) << 1)
198 + (mIsAlpha ? 1 : 0);
199 return DETAIL_TEXTS[index];
202 private int getErrorMessage() {
203 return mIsAlpha ? R.string.lockpassword_invalid_password
204 : R.string.lockpassword_invalid_pin;
208 protected int getLastTryErrorMessage() {
209 return mIsAlpha ? R.string.lock_profile_wipe_warning_content_password
210 : R.string.lock_profile_wipe_warning_content_pin;
214 public void prepareEnterAnimation() {
215 super.prepareEnterAnimation();
216 mHeaderTextView.setAlpha(0f);
217 mDetailsTextView.setAlpha(0f);
218 mCancelButton.setAlpha(0f);
219 mPasswordEntry.setAlpha(0f);
220 mFingerprintIcon.setAlpha(0f);
224 private View[] getActiveViews() {
225 ArrayList<View> result = new ArrayList<>();
226 result.add(mHeaderTextView);
227 result.add(mDetailsTextView);
228 if (mCancelButton.getVisibility() == View.VISIBLE) {
229 result.add(mCancelButton);
231 result.add(mPasswordEntry);
232 if (mFingerprintIcon.getVisibility() == View.VISIBLE) {
233 result.add(mFingerprintIcon);
235 return result.toArray(new View[] {});
239 public void startEnterAnimation() {
240 super.startEnterAnimation();
241 mAppearAnimationUtils.startAnimation(getActiveViews(), new Runnable() {
251 public void onPause() {
253 if (mCountdownTimer != null) {
254 mCountdownTimer.cancel();
255 mCountdownTimer = null;
257 mCredentialCheckResultTracker.setListener(null);
261 public int getMetricsCategory() {
262 return MetricsEvent.CONFIRM_LOCK_PASSWORD;
266 public void onResume() {
268 long deadline = mLockPatternUtils.getLockoutAttemptDeadline(mEffectiveUserId);
270 mCredentialCheckResultTracker.clearResult();
271 handleAttemptLockout(deadline);
274 mErrorTextView.setText("");
275 if (isProfileChallenge()) {
276 updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts(
280 mCredentialCheckResultTracker.setListener(this);
284 protected void authenticationSucceeded() {
285 mCredentialCheckResultTracker.setResult(true, new Intent(), 0, mEffectiveUserId);
289 public void onFingerprintIconVisibilityChanged(boolean visible) {
290 mUsingFingerprint = visible;
293 private void resetState() {
294 if (mBlockImm) return;
295 mPasswordEntry.setEnabled(true);
296 mPasswordEntryInputDisabler.setInputEnabled(true);
297 if (shouldAutoShowSoftKeyboard()) {
298 mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
302 private boolean shouldAutoShowSoftKeyboard() {
303 return mPasswordEntry.isEnabled() && !mUsingFingerprint;
306 public void onWindowFocusChanged(boolean hasFocus) {
307 if (!hasFocus || mBlockImm) {
310 // Post to let window focus logic to finish to allow soft input show/hide properly.
311 mPasswordEntry.post(new Runnable() {
314 if (shouldAutoShowSoftKeyboard()) {
319 mImm.hideSoftInputFromWindow(mPasswordEntry.getWindowToken(),
320 InputMethodManager.HIDE_IMPLICIT_ONLY);
325 private void handleNext() {
326 if (mPendingLockCheck != null || mDisappearing) {
330 final String pin = mPasswordEntry.getText().toString();
331 if (TextUtils.isEmpty(pin)) {
335 mPasswordEntryInputDisabler.setInputEnabled(false);
336 final boolean verifyChallenge = getActivity().getIntent().getBooleanExtra(
337 ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, false);
339 Intent intent = new Intent();
340 if (verifyChallenge) {
341 if (isInternalActivity()) {
342 startVerifyPassword(pin, intent);
346 startCheckPassword(pin, intent);
350 mCredentialCheckResultTracker.setResult(false, intent, 0, mEffectiveUserId);
353 private boolean isInternalActivity() {
354 return getActivity() instanceof ConfirmLockPassword.InternalActivity;
357 private void startVerifyPassword(final String pin, final Intent intent) {
358 long challenge = getActivity().getIntent().getLongExtra(
359 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, 0);
360 final int localEffectiveUserId = mEffectiveUserId;
361 final int localUserId = mUserId;
362 final LockPatternChecker.OnVerifyCallback onVerifyCallback =
363 new LockPatternChecker.OnVerifyCallback() {
365 public void onVerified(byte[] token, int timeoutMs) {
366 mPendingLockCheck = null;
367 boolean matched = false;
370 if (mReturnCredentials) {
372 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN,
376 mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
380 mPendingLockCheck = (localEffectiveUserId == localUserId)
381 ? LockPatternChecker.verifyPassword(
382 mLockPatternUtils, pin, challenge, localUserId, onVerifyCallback)
383 : LockPatternChecker.verifyTiedProfileChallenge(
384 mLockPatternUtils, pin, false, challenge, localUserId,
388 private void startCheckPassword(final String pin, final Intent intent) {
389 final int localEffectiveUserId = mEffectiveUserId;
390 mPendingLockCheck = LockPatternChecker.checkPassword(
393 localEffectiveUserId,
394 new LockPatternChecker.OnCheckCallback() {
396 public void onChecked(boolean matched, int timeoutMs) {
397 mPendingLockCheck = null;
398 if (matched && isInternalActivity() && mReturnCredentials) {
399 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_TYPE,
400 mIsAlpha ? StorageManager.CRYPT_TYPE_PASSWORD
401 : StorageManager.CRYPT_TYPE_PIN);
403 ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD, pin);
405 mCredentialCheckResultTracker.setResult(matched, intent, timeoutMs,
406 localEffectiveUserId);
411 private void startDisappearAnimation(final Intent intent) {
415 mDisappearing = true;
417 final ConfirmLockPassword activity = (ConfirmLockPassword) getActivity();
418 // Bail if there is no active activity.
419 if (activity == null || activity.isFinishing()) {
422 if (activity.getConfirmCredentialTheme() == ConfirmCredentialTheme.DARK) {
423 mDisappearAnimationUtils.startAnimation(getActiveViews(), () -> {
424 activity.setResult(RESULT_OK, intent);
426 activity.overridePendingTransition(
427 R.anim.confirm_credential_close_enter,
428 R.anim.confirm_credential_close_exit);
431 activity.setResult(RESULT_OK, intent);
436 private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs,
437 int effectiveUserId, boolean newResult) {
438 mPasswordEntryInputDisabler.setInputEnabled(true);
441 reportSuccessfullAttempt();
443 startDisappearAnimation(intent);
444 checkForPendingIntent();
448 long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
449 effectiveUserId, timeoutMs);
450 handleAttemptLockout(deadline);
452 showError(getErrorMessage(), ERROR_MESSAGE_TIMEOUT);
455 reportFailedAttempt();
461 public void onCredentialChecked(boolean matched, Intent intent, int timeoutMs,
462 int effectiveUserId, boolean newResult) {
463 onPasswordChecked(matched, intent, timeoutMs, effectiveUserId, newResult);
467 protected void onShowError() {
468 mPasswordEntry.setText(null);
471 private void handleAttemptLockout(long elapsedRealtimeDeadline) {
472 long elapsedRealtime = SystemClock.elapsedRealtime();
473 mPasswordEntry.setEnabled(false);
474 mCountdownTimer = new CountDownTimer(
475 elapsedRealtimeDeadline - elapsedRealtime,
476 LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
479 public void onTick(long millisUntilFinished) {
480 final int secondsCountdown = (int) (millisUntilFinished / 1000);
482 R.string.lockpattern_too_many_failed_confirmation_attempts,
483 secondsCountdown), 0);
487 public void onFinish() {
489 mErrorTextView.setText("");
490 if (isProfileChallenge()) {
491 updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts(
498 public void onClick(View v) {
500 case R.id.next_button:
504 case R.id.cancel_button:
505 getActivity().setResult(RESULT_CANCELED);
506 getActivity().finish();
511 // {@link OnEditorActionListener} methods.
512 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
513 // Check if this was the result of hitting the enter or "done" key
514 if (actionId == EditorInfo.IME_NULL
515 || actionId == EditorInfo.IME_ACTION_DONE
516 || actionId == EditorInfo.IME_ACTION_NEXT) {