Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 47897b33 authored by Joe Bolinger's avatar Joe Bolinger
Browse files

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
parent fdc48a48
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -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);
    }

+6 −0
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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);
    }
+205 −0
Original line number Diff line number Diff line
/*
 * 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;
        }
    }
}