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

Commit 41d65a1b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Improve BiometricPrompt support for face + UDFPS" into sc-dev

parents 194d95b1 d1ae004b
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@
    <FrameLayout
        android:id="@+id/biometric_icon_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal">

        <ImageView
@@ -80,7 +80,7 @@
        android:layout_height="88dp"
        style="?android:attr/buttonBarStyle"
        android:orientation="horizontal"
        android:paddingTop="16dp">
        android:paddingTop="24dp">

        <Space android:id="@+id/leftSpacer"
            android:layout_width="8dp"
+25 −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.
  -->

<com.android.systemui.biometrics.AuthBiometricFaceToUdfpsView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <include layout="@layout/auth_biometric_contents"/>

</com.android.systemui.biometrics.AuthBiometricFaceToUdfpsView>
 No newline at end of file
+147 −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 android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;

import com.android.systemui.R;

/**
 * Manages the layout of an auth dialog for devices with a face sensor and an under-display
 * fingerprint sensor (UDFPS). Face authentication is attempted first, followed by fingerprint if
 * the initial attempt is unsuccessful.
 */
public class AuthBiometricFaceToUdfpsView extends AuthBiometricFaceView {
    private static final String TAG = "BiometricPrompt/AuthBiometricFaceToUdfpsView";

    protected static class UdfpsIconController extends IconController {
        protected UdfpsIconController(
                @NonNull Context context, @NonNull ImageView iconView, @NonNull TextView textView) {
            super(context, iconView, textView);
        }

        @Override
        protected void updateState(int lastState, int newState) {
            final boolean lastStateIsErrorIcon =
                    lastState == STATE_ERROR || lastState == STATE_HELP;

            switch (newState) {
                case STATE_IDLE:
                case STATE_AUTHENTICATING_ANIMATING_IN:
                case STATE_AUTHENTICATING:
                case STATE_PENDING_CONFIRMATION:
                case STATE_AUTHENTICATED:
                    if (lastStateIsErrorIcon) {
                        animateOnce(R.drawable.fingerprint_dialog_error_to_fp);
                    } else {
                        showStaticDrawable(R.drawable.fingerprint_dialog_fp_to_error);
                    }
                    mIconView.setContentDescription(mContext.getString(
                            R.string.accessibility_fingerprint_dialog_fingerprint_icon));
                    break;

                case STATE_ERROR:
                case STATE_HELP:
                    if (!lastStateIsErrorIcon) {
                        animateOnce(R.drawable.fingerprint_dialog_fp_to_error);
                    } else {
                        showStaticDrawable(R.drawable.fingerprint_dialog_error_to_fp);
                    }
                    mIconView.setContentDescription(mContext.getString(
                            R.string.biometric_dialog_try_again));
                    break;

                default:
                    Log.e(TAG, "Unknown biometric dialog state: " + newState);
                    break;
            }

            mState = newState;
        }
    }

    @BiometricAuthenticator.Modality private int mActiveSensorType = TYPE_FACE;

    @Nullable UdfpsDialogMeasureAdapter mMeasureAdapter;
    @Nullable private UdfpsIconController mUdfpsIconController;

    public AuthBiometricFaceToUdfpsView(Context context) {
        super(context);
    }

    public AuthBiometricFaceToUdfpsView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    void setFingerprintSensorProps(@NonNull FingerprintSensorPropertiesInternal sensorProps) {
        if (mMeasureAdapter == null || mMeasureAdapter.getSensorProps() != sensorProps) {
            mMeasureAdapter = new UdfpsDialogMeasureAdapter(this, sensorProps);
        }
    }

    @Override
    protected int getDelayAfterAuthenticatedDurationMs() {
        return mActiveSensorType == TYPE_FINGERPRINT ? 0
                : super.getDelayAfterAuthenticatedDurationMs();
    }

    @Override
    protected boolean supportsManualRetry() {
        return false;
    }

    @Override
    @NonNull
    protected IconController getIconController() {
        if (mActiveSensorType == TYPE_FINGERPRINT) {
            if (!(mIconController instanceof UdfpsIconController)) {
                mIconController = new UdfpsIconController(getContext(), mIconView, mIndicatorView);
            }
            return mIconController;
        }
        return super.getIconController();
    }

    @Override
    public void updateState(int newState) {
        if (mState == STATE_HELP || mState == STATE_ERROR) {
            mActiveSensorType = TYPE_FINGERPRINT;
            setRequireConfirmation(false);
        }
        super.updateState(newState);
    }

    @Override
    @NonNull
    AuthDialog.LayoutParams onMeasureInternal(int width, int height) {
        final AuthDialog.LayoutParams layoutParams = super.onMeasureInternal(width, height);
        return mMeasureAdapter != null
                ? mMeasureAdapter.onMeasureInternal(width, height, layoutParams)
                : layoutParams;
    }
}
+32 −23
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.biometrics;

import android.annotation.NonNull;
import android.content.Context;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -28,7 +29,6 @@ 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 {
@@ -38,15 +38,15 @@ public class AuthBiometricFaceView extends AuthBiometricView {
    // Delay before dismissing after being authenticated/confirmed.
    private static final int HIDE_DELAY_MS = 500;

    public static class IconController extends Animatable2.AnimationCallback {
        Context mContext;
        ImageView mIconView;
        TextView mTextView;
        Handler mHandler;
        boolean mLastPulseLightToDark; // false = dark to light, true = light to dark
        @BiometricState int mState;
    protected static class IconController extends Animatable2.AnimationCallback {
        protected Context mContext;
        protected ImageView mIconView;
        protected TextView mTextView;
        protected Handler mHandler;
        protected boolean mLastPulseLightToDark; // false = dark to light, true = light to dark
        protected @BiometricState int mState;

        IconController(Context context, ImageView iconView, TextView textView) {
        protected IconController(Context context, ImageView iconView, TextView textView) {
            mContext = context;
            mIconView = iconView;
            mTextView = textView;
@@ -54,15 +54,15 @@ public class AuthBiometricFaceView extends AuthBiometricView {
            showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light);
        }

        void animateOnce(int iconRes) {
        protected void animateOnce(int iconRes) {
            animateIcon(iconRes, false);
        }

        public void showStaticDrawable(int iconRes) {
        protected void showStaticDrawable(int iconRes) {
            mIconView.setImageDrawable(mContext.getDrawable(iconRes));
        }

        void animateIcon(int iconRes, boolean repeat) {
        protected void animateIcon(int iconRes, boolean repeat) {
            final AnimatedVectorDrawable icon =
                    (AnimatedVectorDrawable) mContext.getDrawable(iconRes);
            mIconView.setImageDrawable(icon);
@@ -73,12 +73,12 @@ public class AuthBiometricFaceView extends AuthBiometricView {
            icon.start();
        }

        void startPulsing() {
        protected void startPulsing() {
            mLastPulseLightToDark = false;
            animateIcon(R.drawable.face_dialog_pulse_dark_to_light, true);
        }

        void pulseInNextDirection() {
        protected void pulseInNextDirection() {
            int iconRes = mLastPulseLightToDark ? R.drawable.face_dialog_pulse_dark_to_light
                    : R.drawable.face_dialog_pulse_light_to_dark;
            animateIcon(iconRes, true /* repeat */);
@@ -93,7 +93,7 @@ public class AuthBiometricFaceView extends AuthBiometricView {
            }
        }

        public void updateState(int lastState, int newState) {
        protected void updateState(int lastState, int newState) {
            final boolean lastStateIsErrorIcon =
                    lastState == STATE_ERROR || lastState == STATE_HELP;

@@ -138,7 +138,7 @@ public class AuthBiometricFaceView extends AuthBiometricView {
        }
    }

    @VisibleForTesting IconController mIconController;
    protected IconController mIconController;

    public AuthBiometricFaceView(Context context) {
        this(context, null);
@@ -174,14 +174,21 @@ public class AuthBiometricFaceView extends AuthBiometricView {
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
    protected boolean supportsManualRetry() {
        return true;
    }

    @NonNull
    protected IconController getIconController() {
        if (mIconController == null) {
            mIconController = new IconController(mContext, mIconView, mIndicatorView);
        }
        return mIconController;
    }

    @Override
    public void updateState(@BiometricState int newState) {
        mIconController.updateState(mState, newState);
        getIconController().updateState(mState, newState);

        if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
                (newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) {
@@ -195,11 +202,13 @@ public class AuthBiometricFaceView extends AuthBiometricView {
    @Override
    public void onAuthenticationFailed(String failureReason) {
        if (getSize() == AuthDialog.SIZE_MEDIUM) {
            if (supportsManualRetry()) {
                mTryAgainButton.setVisibility(View.VISIBLE);
                mConfirmButton.setVisibility(View.GONE);
            }
        }

        // Do this last since wa want to know if the button is being animated (in the case of
        // Do this last since we want to know if the button is being animated (in the case of
        // small -> medium dialog)
        super.onAuthenticationFailed(failureReason);
    }
+9 −275

File changed.

Preview size limit exceeded, changes collapsed.

Loading