16/n: Add PIN/Password
Bug: 140127687 Make AuthCredentialView abstract, and have the following subclasses 1) AuthCredentialPatternView 2) AuthCredentialPasswordView Back button cancels password authentication Test: manual test with pattern, pin, password Test: atest com.android.systemui.biometrics Change-Id: I95e42144616a59827da25d10d063990452714f76
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<com.android.systemui.biometrics.AuthCredentialView
|
||||
<com.android.systemui.biometrics.AuthCredentialPatternView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -110,4 +110,4 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.android.systemui.biometrics.AuthCredentialView>
|
||||
</com.android.systemui.biometrics.AuthCredentialPatternView>
|
||||
101
packages/SystemUI/res/layout/auth_credential_password_view.xml
Normal file
101
packages/SystemUI/res/layout/auth_credential_password_view.xml
Normal file
@@ -0,0 +1,101 @@
|
||||
<!--
|
||||
~ Copyright (C) 2019 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<com.android.systemui.biometrics.AuthCredentialPasswordView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:elevation="@dimen/biometric_dialog_elevation">
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/auth_dialog_lock"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:textSize="20sp"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
android:textSize="16sp"
|
||||
android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/colorError"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/lockPassword"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginLeft="100dp"
|
||||
android:layout_marginRight="100dp"
|
||||
android:layout_width="208dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:inputType="textPassword"
|
||||
android:maxLength="500"
|
||||
android:textSize="16sp"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:imeOptions="flagForceAscii"
|
||||
style="@style/LockPatternStyleBiometricPrompt"/>
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="5"/>
|
||||
|
||||
</com.android.systemui.biometrics.AuthCredentialPasswordView>
|
||||
@@ -14,7 +14,7 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<com.android.systemui.biometrics.AuthCredentialView
|
||||
<com.android.systemui.biometrics.AuthCredentialPatternView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -94,4 +94,4 @@
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
</com.android.systemui.biometrics.AuthCredentialView>
|
||||
</com.android.systemui.biometrics.AuthCredentialPatternView>
|
||||
@@ -629,19 +629,17 @@ public abstract class AuthBiometricView extends LinearLayout {
|
||||
|
||||
final String negativeText;
|
||||
if (isDeviceCredentialAllowed()) {
|
||||
final LockPatternUtils lpu = new LockPatternUtils(mContext);
|
||||
switch (lpu.getKeyguardStoredPasswordQuality(mUserId)) {
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
|
||||
negativeText = getResources().getString(R.string.biometric_dialog_use_pattern);
|
||||
break;
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
|
||||
|
||||
final @Utils.CredentialType int credentialType =
|
||||
Utils.getCredentialType(mContext, mUserId);
|
||||
switch(credentialType) {
|
||||
case Utils.CREDENTIAL_PIN:
|
||||
negativeText = getResources().getString(R.string.biometric_dialog_use_pin);
|
||||
break;
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
|
||||
case Utils.CREDENTIAL_PATTERN:
|
||||
negativeText = getResources().getString(R.string.biometric_dialog_use_pattern);
|
||||
break;
|
||||
case Utils.CREDENTIAL_PASSWORD:
|
||||
negativeText = getResources().getString(R.string.biometric_dialog_use_password);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -307,8 +307,22 @@ public class AuthContainerView extends LinearLayout
|
||||
*/
|
||||
private void addCredentialView(boolean animatePanel, boolean animateContents) {
|
||||
final LayoutInflater factory = LayoutInflater.from(mContext);
|
||||
mCredentialView = (AuthCredentialView) factory.inflate(
|
||||
R.layout.auth_credential_view, null, false);
|
||||
final int credentialType = Utils.getCredentialType(mContext, mConfig.mUserId);
|
||||
switch (credentialType) {
|
||||
case Utils.CREDENTIAL_PATTERN:
|
||||
mCredentialView = (AuthCredentialView) factory.inflate(
|
||||
R.layout.auth_credential_pattern_view, null, false);
|
||||
break;
|
||||
case Utils.CREDENTIAL_PIN:
|
||||
case Utils.CREDENTIAL_PASSWORD:
|
||||
mCredentialView = (AuthCredentialView) factory.inflate(
|
||||
R.layout.auth_credential_password_view, null, false);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown credential type: " + credentialType);
|
||||
}
|
||||
|
||||
mCredentialView.setContainerView(this);
|
||||
mCredentialView.setUser(mConfig.mUserId);
|
||||
mCredentialView.setCallback(mCredentialCallback);
|
||||
mCredentialView.setBiometricPromptBundle(mConfig.mBiometricPromptBundle);
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.InputType;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.widget.LockPatternChecker;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Pin and Password UI
|
||||
*/
|
||||
public class AuthCredentialPasswordView extends AuthCredentialView
|
||||
implements TextView.OnEditorActionListener {
|
||||
|
||||
private static final String TAG = "BiometricPrompt/AuthCredentialPasswordView";
|
||||
|
||||
private final InputMethodManager mImm;
|
||||
private EditText mPasswordField;
|
||||
|
||||
public AuthCredentialPasswordView(Context context,
|
||||
AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mImm = mContext.getSystemService(InputMethodManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mPasswordField = findViewById(R.id.lockPassword);
|
||||
mPasswordField.setOnEditorActionListener(this);
|
||||
|
||||
if (mCredentialType == Utils.CREDENTIAL_PIN) {
|
||||
mPasswordField.setInputType(
|
||||
InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
||||
}
|
||||
|
||||
mPasswordField.setOnKeyListener((v, keyCode, event) -> {
|
||||
if (keyCode != KeyEvent.KEYCODE_BACK) {
|
||||
return false;
|
||||
}
|
||||
if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
// Wait a bit to focus the field so the focusable flag on the window is already set then.
|
||||
post(() -> {
|
||||
mPasswordField.requestFocus();
|
||||
mImm.showSoftInput(mPasswordField, InputMethodManager.SHOW_IMPLICIT);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
// Check if this was the result of hitting the enter key
|
||||
final boolean isSoftImeEvent = event == null
|
||||
&& (actionId == EditorInfo.IME_NULL
|
||||
|| actionId == EditorInfo.IME_ACTION_DONE
|
||||
|| actionId == EditorInfo.IME_ACTION_NEXT);
|
||||
final boolean isKeyboardEnterKey = event != null
|
||||
&& KeyEvent.isConfirmKey(event.getKeyCode())
|
||||
&& event.getAction() == KeyEvent.ACTION_DOWN;
|
||||
if (isSoftImeEvent || isKeyboardEnterKey) {
|
||||
checkPasswordAndUnlock();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkPasswordAndUnlock() {
|
||||
final byte[] password = LockPatternUtils.charSequenceToByteArray(mPasswordField.getText());
|
||||
if (password == null || password.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPendingLockCheck = LockPatternChecker.checkPassword(mLockPatternUtils,
|
||||
password, mUserId, this::onCredentialChecked);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCredentialChecked(boolean matched, int timeoutMs) {
|
||||
super.onCredentialChecked(matched, timeoutMs);
|
||||
|
||||
if (matched) {
|
||||
mImm.hideSoftInputFromWindow(getWindowToken(), 0 /* flags */);
|
||||
} else {
|
||||
mPasswordField.setText("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.android.internal.widget.LockPatternChecker;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockPatternView;
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Pattern UI
|
||||
*/
|
||||
public class AuthCredentialPatternView extends AuthCredentialView {
|
||||
|
||||
private LockPatternView mLockPatternView;
|
||||
|
||||
private class UnlockPatternListener implements LockPatternView.OnPatternListener {
|
||||
|
||||
@Override
|
||||
public void onPatternStart() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPatternCleared() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
|
||||
if (mPendingLockCheck != null) {
|
||||
mPendingLockCheck.cancel(false);
|
||||
}
|
||||
|
||||
mLockPatternView.setEnabled(false);
|
||||
|
||||
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
|
||||
// Pattern size is less than the minimum, do not count it as a failed attempt.
|
||||
onPatternChecked(false /* matched */, 0 /* timeoutMs */);
|
||||
return;
|
||||
}
|
||||
|
||||
mPendingLockCheck = LockPatternChecker.checkPattern(
|
||||
mLockPatternUtils,
|
||||
pattern,
|
||||
mUserId,
|
||||
this::onPatternChecked);
|
||||
}
|
||||
|
||||
private void onPatternChecked(boolean matched, int timeoutMs) {
|
||||
AuthCredentialPatternView.this.onCredentialChecked(matched, timeoutMs);
|
||||
if (timeoutMs > 0) {
|
||||
mLockPatternView.setEnabled(false);
|
||||
} else {
|
||||
mLockPatternView.setEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onErrorTimeoutFinish() {
|
||||
super.onErrorTimeoutFinish();
|
||||
mLockPatternView.setEnabled(true);
|
||||
}
|
||||
|
||||
public AuthCredentialPatternView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mLockPatternView = findViewById(R.id.lockPattern);
|
||||
mLockPatternView.setOnPatternListener(new UnlockPatternListener());
|
||||
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(mUserId));
|
||||
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
|
||||
}
|
||||
}
|
||||
@@ -31,31 +31,23 @@ import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.widget.LockPatternChecker;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.LockPatternView;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Shows Pin, Pattern, or Password for
|
||||
* Abstract base class for Pin, Pattern, or Password authentication, for
|
||||
* {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)}
|
||||
*/
|
||||
public class AuthCredentialView extends LinearLayout {
|
||||
public abstract class AuthCredentialView extends LinearLayout {
|
||||
|
||||
private static final String TAG = "BiometricPrompt/AuthCredentialView";
|
||||
private static final int ERROR_DURATION_MS = 3000;
|
||||
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
private final LockPatternUtils mLockPatternUtils;
|
||||
private final Handler mHandler;
|
||||
|
||||
private LockPatternView mLockPatternView;
|
||||
private int mUserId;
|
||||
private AsyncTask<?, ?, ?> mPendingLockCheck;
|
||||
private Callback mCallback;
|
||||
private ErrorTimer mErrorTimer;
|
||||
protected final Handler mHandler;
|
||||
|
||||
private Bundle mBiometricPromptBundle;
|
||||
private AuthPanelController mPanelController;
|
||||
private boolean mShouldAnimatePanel;
|
||||
@@ -64,13 +56,21 @@ public class AuthCredentialView extends LinearLayout {
|
||||
private TextView mTitleView;
|
||||
private TextView mSubtitleView;
|
||||
private TextView mDescriptionView;
|
||||
private TextView mErrorView;
|
||||
protected TextView mErrorView;
|
||||
|
||||
protected @Utils.CredentialType int mCredentialType;
|
||||
protected final LockPatternUtils mLockPatternUtils;
|
||||
protected AuthContainerView mContainerView;
|
||||
protected Callback mCallback;
|
||||
protected AsyncTask<?, ?, ?> mPendingLockCheck;
|
||||
protected int mUserId;
|
||||
protected ErrorTimer mErrorTimer;
|
||||
|
||||
interface Callback {
|
||||
void onCredentialMatched();
|
||||
}
|
||||
|
||||
private static class ErrorTimer extends CountDownTimer {
|
||||
protected static class ErrorTimer extends CountDownTimer {
|
||||
private final TextView mErrorView;
|
||||
private final Context mContext;
|
||||
|
||||
@@ -102,74 +102,7 @@ public class AuthCredentialView extends LinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
private class UnlockPatternListener implements LockPatternView.OnPatternListener {
|
||||
|
||||
@Override
|
||||
public void onPatternStart() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPatternCleared() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
|
||||
if (mPendingLockCheck != null) {
|
||||
mPendingLockCheck.cancel(false);
|
||||
}
|
||||
|
||||
mLockPatternView.setEnabled(false);
|
||||
|
||||
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
|
||||
// Pattern size is less than the minimum, do not count it as a failed attempt.
|
||||
onPatternChecked(false /* matched */, 0 /* timeoutMs */);
|
||||
return;
|
||||
}
|
||||
|
||||
mPendingLockCheck = LockPatternChecker.checkPattern(
|
||||
mLockPatternUtils,
|
||||
pattern,
|
||||
mUserId,
|
||||
this::onPatternChecked);
|
||||
}
|
||||
|
||||
private void onPatternChecked(boolean matched, int timeoutMs) {
|
||||
mLockPatternView.setEnabled(true);
|
||||
|
||||
if (matched) {
|
||||
mClearErrorRunnable.run();
|
||||
mCallback.onCredentialMatched();
|
||||
} else {
|
||||
if (timeoutMs > 0) {
|
||||
mHandler.removeCallbacks(mClearErrorRunnable);
|
||||
mLockPatternView.setEnabled(false);
|
||||
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(mUserId, timeoutMs);
|
||||
mErrorTimer = new ErrorTimer(mContext,
|
||||
deadline - SystemClock.elapsedRealtime(),
|
||||
LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS,
|
||||
mErrorView) {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
mClearErrorRunnable.run();
|
||||
mLockPatternView.setEnabled(true);
|
||||
}
|
||||
};
|
||||
mErrorTimer.start();
|
||||
} else {
|
||||
showError(getResources().getString(R.string.biometric_dialog_wrong_pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Runnable mClearErrorRunnable = new Runnable() {
|
||||
protected final Runnable mClearErrorRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mErrorView.setText("");
|
||||
@@ -179,12 +112,12 @@ public class AuthCredentialView extends LinearLayout {
|
||||
public AuthCredentialView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mLockPatternUtils = new LockPatternUtils(mContext);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
|
||||
}
|
||||
|
||||
private void showError(String error) {
|
||||
protected void showError(String error) {
|
||||
mHandler.removeCallbacks(mClearErrorRunnable);
|
||||
mErrorView.setText(error);
|
||||
mHandler.postDelayed(mClearErrorRunnable, ERROR_DURATION_MS);
|
||||
@@ -225,6 +158,10 @@ public class AuthCredentialView extends LinearLayout {
|
||||
mShouldAnimateContents = animateContents;
|
||||
}
|
||||
|
||||
void setContainerView(AuthContainerView containerView) {
|
||||
mContainerView = containerView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
@@ -263,14 +200,11 @@ public class AuthCredentialView extends LinearLayout {
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mCredentialType = Utils.getCredentialType(mContext, mUserId);
|
||||
mTitleView = findViewById(R.id.title);
|
||||
mSubtitleView = findViewById(R.id.subtitle);
|
||||
mDescriptionView = findViewById(R.id.description);
|
||||
mErrorView = findViewById(R.id.error);
|
||||
mLockPatternView = findViewById(R.id.lockPattern);
|
||||
mLockPatternView.setOnPatternListener(new UnlockPatternListener());
|
||||
mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(mUserId));
|
||||
mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -286,4 +220,45 @@ public class AuthCredentialView extends LinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
protected void onErrorTimeoutFinish() {}
|
||||
|
||||
protected void onCredentialChecked(boolean matched, int timeoutMs) {
|
||||
if (matched) {
|
||||
mClearErrorRunnable.run();
|
||||
mCallback.onCredentialMatched();
|
||||
} else {
|
||||
if (timeoutMs > 0) {
|
||||
mHandler.removeCallbacks(mClearErrorRunnable);
|
||||
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(mUserId, timeoutMs);
|
||||
mErrorTimer = new ErrorTimer(mContext,
|
||||
deadline - SystemClock.elapsedRealtime(),
|
||||
LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS,
|
||||
mErrorView) {
|
||||
@Override
|
||||
public void onFinish() {
|
||||
onErrorTimeoutFinish();
|
||||
mClearErrorRunnable.run();
|
||||
}
|
||||
};
|
||||
mErrorTimer.start();
|
||||
} else {
|
||||
final int error;
|
||||
switch (mCredentialType) {
|
||||
case Utils.CREDENTIAL_PIN:
|
||||
error = R.string.biometric_dialog_wrong_pin;
|
||||
break;
|
||||
case Utils.CREDENTIAL_PATTERN:
|
||||
error = R.string.biometric_dialog_wrong_pattern;
|
||||
break;
|
||||
case Utils.CREDENTIAL_PASSWORD:
|
||||
error = R.string.biometric_dialog_wrong_password;
|
||||
break;
|
||||
default:
|
||||
error = R.string.biometric_dialog_wrong_password;
|
||||
break;
|
||||
}
|
||||
showError(getResources().getString(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package com.android.systemui.biometrics;
|
||||
|
||||
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.Context;
|
||||
import android.hardware.biometrics.Authenticator;
|
||||
import android.hardware.biometrics.BiometricPrompt;
|
||||
@@ -28,7 +30,23 @@ import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static final int CREDENTIAL_PIN = 1;
|
||||
public static final int CREDENTIAL_PATTERN = 2;
|
||||
public static final int CREDENTIAL_PASSWORD = 3;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD})
|
||||
@interface CredentialType {}
|
||||
|
||||
|
||||
static float dpToPixels(Context context, float dp) {
|
||||
return dp * ((float) context.getResources().getDisplayMetrics().densityDpi
|
||||
/ DisplayMetrics.DENSITY_DEFAULT);
|
||||
@@ -63,4 +81,22 @@ public class Utils {
|
||||
static int getAuthenticators(Bundle biometricPromptBundle) {
|
||||
return biometricPromptBundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
|
||||
}
|
||||
|
||||
static @CredentialType int getCredentialType(Context context, int userId) {
|
||||
final LockPatternUtils lpu = new LockPatternUtils(context);
|
||||
switch (lpu.getKeyguardStoredPasswordQuality(userId)) {
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
|
||||
return CREDENTIAL_PATTERN;
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
|
||||
return CREDENTIAL_PIN;
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
|
||||
return CREDENTIAL_PASSWORD;
|
||||
default:
|
||||
return CREDENTIAL_PASSWORD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user