From 47897b33f6d6ad8f7ca6cbd32782a0e5f0b980d7 Mon Sep 17 00:00:00 2001 From: Joe Bolinger Date: Wed, 19 May 2021 20:36:44 -0700 Subject: [PATCH] Remove confirm button when falling back to fingerprint auth. Fix: 186283063 Test: atest AuthBiometricFaceToFingerprintViewTest Test: manual (fail face auth and verify button disappers) Change-Id: I57f018e41b598fe8b2a9bccb37cdd369491e266b --- .../AuthBiometricFaceToFingerprintView.java | 18 +- .../biometrics/AuthBiometricFaceView.java | 6 + .../biometrics/AuthBiometricView.java | 2 +- ...uthBiometricFaceToFingerprintViewTest.java | 205 ++++++++++++++++++ 4 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java index 76929cfa07ab1..ebfd2068c55b4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java @@ -26,9 +26,11 @@ import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.util.AttributeSet; import android.util.Log; +import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; /** @@ -97,6 +99,11 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView { super(context, attrs); } + @VisibleForTesting + AuthBiometricFaceToFingerprintView(Context context, AttributeSet attrs, Injector injector) { + super(context, attrs, injector); + } + void setFingerprintSensorProps(@NonNull FingerprintSensorPropertiesInternal sensorProps) { if (!sensorProps.isAnyUdfpsType()) { return; @@ -135,19 +142,28 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView { protected IconController getIconController() { if (mActiveSensorType == TYPE_FINGERPRINT) { if (!(mIconController instanceof UdfpsIconController)) { - mIconController = new UdfpsIconController(getContext(), mIconView, mIndicatorView); + mIconController = createUdfpsIconController(); } return mIconController; } return super.getIconController(); } + @NonNull + protected IconController createUdfpsIconController() { + return new UdfpsIconController(getContext(), mIconView, mIndicatorView); + } + @Override public void updateState(int newState) { if (mState == STATE_HELP || mState == STATE_ERROR) { mActiveSensorType = TYPE_FINGERPRINT; + setRequireConfirmation(false); + mConfirmButton.setEnabled(false); + mConfirmButton.setVisibility(View.GONE); } + super.updateState(newState); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java index 4c5ca691939c4..f7d2d8c6d2ea9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java @@ -29,6 +29,7 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; public class AuthBiometricFaceView extends AuthBiometricView { @@ -148,6 +149,11 @@ public class AuthBiometricFaceView extends AuthBiometricView { super(context, attrs); } + @VisibleForTesting + AuthBiometricFaceView(Context context, AttributeSet attrs, Injector injector) { + super(context, attrs, injector); + } + @Override protected int getDelayAfterAuthenticatedDurationMs() { return HIDE_DELAY_MS; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index 99050160f38f0..1ac1df148000c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -612,7 +612,7 @@ public abstract class AuthBiometricView extends LinearLayout { mIndicatorView.setTextColor(mTextColorError); mIndicatorView.setVisibility(View.VISIBLE); mIndicatorView.setSelected(true); - mHandler.postDelayed(resetMessageRunnable, BiometricPrompt.HIDE_DIALOG_DELAY); + mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError()); Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java new file mode 100644 index 0000000000000..5f3d3cb2f7d3f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2021 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 static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase { + + @Mock AuthBiometricView.Callback mCallback; + + private AuthBiometricFaceToFingerprintView mFaceToFpView; + + @Mock private Button mNegativeButton; + @Mock private Button mCancelButton; + @Mock private Button mConfirmButton; + @Mock private Button mUseCredentialButton; + @Mock private Button mTryAgainButton; + + @Mock private TextView mTitleView; + @Mock private TextView mSubtitleView; + @Mock private TextView mDescriptionView; + @Mock private TextView mIndicatorView; + @Mock private ImageView mIconView; + @Mock private View mIconHolderView; + + @Mock private TextView mErrorView; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + mFaceToFpView = new TestableView(mContext); + mFaceToFpView.mIconController = mock(AuthBiometricFaceView.IconController.class); + mFaceToFpView.setCallback(mCallback); + + mFaceToFpView.mNegativeButton = mNegativeButton; + mFaceToFpView.mCancelButton = mCancelButton; + mFaceToFpView.mUseCredentialButton = mUseCredentialButton; + mFaceToFpView.mConfirmButton = mConfirmButton; + mFaceToFpView.mTryAgainButton = mTryAgainButton; + + mFaceToFpView.mIndicatorView = mErrorView; + } + + @Test + public void testStateUpdated_whenDialogAnimatedIn() { + mFaceToFpView.onDialogAnimatedIn(); + verify(mFaceToFpView.mIconController) + .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING)); + } + + @Test + public void testIconUpdatesState_whenDialogStateUpdated() { + mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING); + verify(mFaceToFpView.mIconController) + .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING)); + + mFaceToFpView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED); + verify(mFaceToFpView.mIconController).updateState( + eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING), + eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED)); + } + + @Test + public void testStateUpdated_whenSwitchToFingerprint() { + mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING); + verify(mFaceToFpView.mIconController) + .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING)); + + mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR); + mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING); + + InOrder order = inOrder(mFaceToFpView.mIconController); + order.verify(mFaceToFpView.mIconController).updateState( + eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING), + eq(AuthBiometricFaceToFingerprintView.STATE_ERROR)); + order.verify(mFaceToFpView.mIconController).updateState( + eq(AuthBiometricFaceToFingerprintView.STATE_ERROR), + eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING)); + + verify(mConfirmButton).setVisibility(eq(View.GONE)); + } + + public class TestableView extends AuthBiometricFaceToFingerprintView { + public TestableView(Context context) { + super(context, null, new MockInjector()); + } + + @Override + protected int getDelayAfterAuthenticatedDurationMs() { + return 0; + } + + @Override + protected IconController createUdfpsIconController() { + return mIconController; + } + } + + private class MockInjector extends AuthBiometricView.Injector { + @Override + public Button getNegativeButton() { + return mNegativeButton; + } + + @Override + public Button getCancelButton() { + return mCancelButton; + } + + @Override + public Button getUseCredentialButton() { + return mUseCredentialButton; + } + + @Override + public Button getConfirmButton() { + return mConfirmButton; + } + + @Override + public Button getTryAgainButton() { + return mTryAgainButton; + } + + @Override + public TextView getTitleView() { + return mTitleView; + } + + @Override + public TextView getSubtitleView() { + return mSubtitleView; + } + + @Override + public TextView getDescriptionView() { + return mDescriptionView; + } + + @Override + public TextView getIndicatorView() { + return mIndicatorView; + } + + @Override + public ImageView getIconView() { + return mIconView; + } + + @Override + public View getIconHolderView() { + return mIconHolderView; + } + + @Override + public int getDelayAfterError() { + return 0; + } + + @Override + public int getMediumToLargeAnimationDurationMs() { + return 0; + } + } +}