diff --git a/packages/SystemUI/res/layout-land/auth_credential_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
similarity index 97%
rename from packages/SystemUI/res/layout-land/auth_credential_view.xml
rename to packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
index 4a34ede239203..c3fa39e5a87f5 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
@@ -14,7 +14,7 @@
~ limitations under the License.
-->
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
new file mode 100644
index 0000000000000..4aed0333e9ca8
--- /dev/null
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
similarity index 96%
rename from packages/SystemUI/res/layout/auth_credential_view.xml
rename to packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index 74f991c24130a..c9edcd6062774 100644
--- a/packages/SystemUI/res/layout/auth_credential_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -14,7 +14,7 @@
~ limitations under the License.
-->
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index ecc1f695d4f59..69149737b599d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -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:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 7c6cb085a245f..ced910f4b600c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -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);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
new file mode 100644
index 0000000000000..99a40ba5ebf41
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -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("");
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
new file mode 100644
index 0000000000000..6c36f82632376
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -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 pattern) {
+
+ }
+
+ @Override
+ public void onPatternDetected(List 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());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 1ba88c6add097..1c75ae2dccde4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -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 pattern) {
-
- }
-
- @Override
- public void onPatternDetected(List 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));
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index 485e667aae985..b78a9ad274ebd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -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;
+ }
+ }
}