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

Commit 889de4c6 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

6/n: Add fingerprint support to the refactored UI

Bug: 123378871

Test: atest com.android.systemui.biometrics
Test: manual test of fingerprint auth

Change-Id: Iac308557d5715c2450a2486d84a5a8292e4d3e42
parent ded4f36e
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
<!--
  ~ 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.AuthBiometricFingerprintView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/contents"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

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

</com.android.systemui.biometrics.AuthBiometricFingerprintView>
 No newline at end of file
+5 −0
Original line number Diff line number Diff line
@@ -162,6 +162,11 @@ public class AuthBiometricFaceView extends AuthBiometricView {
        resetErrorView(mContext, mIndicatorView);
    }

    @Override
    protected boolean supportsSmallDialog() {
        return true;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
+147 −0
Original line number Diff line number Diff line
/*
 * 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.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;

import com.android.systemui.R;

public class AuthBiometricFingerprintView extends AuthBiometricView {

    private static final String TAG = "BiometricPrompt/AuthBiometricFingerprintView";

    public AuthBiometricFingerprintView(Context context) {
        this(context, null);
    }

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

    @Override
    protected int getDelayAfterAuthenticatedDurationMs() {
        return 0;
    }

    @Override
    protected int getStateForAfterError() {
        return STATE_AUTHENTICATING;
    }

    @Override
    protected void handleResetAfterError() {
        showTouchSensorString();
    }

    @Override
    protected void handleResetAfterHelp() {
        showTouchSensorString();
    }

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

    @Override
    public void updateState(@BiometricState int newState) {
        updateIcon(mState, newState);

        // Do this last since the state variable gets updated.
        super.updateState(newState);
    }

    @Override
    void onAttachedToWindowInternal() {
        super.onAttachedToWindowInternal();
        showTouchSensorString();
    }

    private void showTouchSensorString() {
        mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor);
        mIndicatorView.setTextColor(R.color.biometric_dialog_gray);
    }

    private void updateIcon(int lastState, int newState) {
        final Drawable icon = getAnimationForTransition(lastState, newState);
        if (icon == null) {
            Log.e(TAG, "Animation not found, " + lastState + " -> " + newState);
            return;
        }

        final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
                ? (AnimatedVectorDrawable) icon
                : null;

        mIconView.setImageDrawable(icon);

        if (animation != null && shouldAnimateForTransition(lastState, newState)) {
            animation.forceAnimationOnUI();
            animation.start();
        }
    }

    private boolean shouldAnimateForTransition(int oldState, int newState) {
        switch (newState) {
            case STATE_HELP:
            case STATE_ERROR:
                return true;
            case STATE_AUTHENTICATING_ANIMATING_IN:
            case STATE_AUTHENTICATING:
                if (oldState == STATE_ERROR || oldState == STATE_HELP) {
                    return true;
                } else {
                    return false;
                }
            case STATE_AUTHENTICATED:
                return false;
            default:
                return false;
        }
    }

    private Drawable getAnimationForTransition(int oldState, int newState) {
        int iconRes;

        switch (newState) {
            case STATE_HELP:
            case STATE_ERROR:
                iconRes = R.drawable.fingerprint_dialog_fp_to_error;
                break;
            case STATE_AUTHENTICATING_ANIMATING_IN:
            case STATE_AUTHENTICATING:
                if (oldState == STATE_ERROR || oldState == STATE_HELP) {
                    iconRes = R.drawable.fingerprint_dialog_error_to_fp;
                } else {
                    iconRes = R.drawable.fingerprint_dialog_fp_to_error;
                }
                break;
            case STATE_AUTHENTICATED:
                iconRes = R.drawable.fingerprint_dialog_fp_to_error;
                break;
            default:
                return null;
        }

        return mContext.getDrawable(iconRes);
    }
}
+33 −11
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ public abstract class AuthBiometricView extends LinearLayout {
        int ACTION_USER_CANCELED = 2;
        int ACTION_BUTTON_NEGATIVE = 3;
        int ACTION_BUTTON_TRY_AGAIN = 4;
        int ACTION_ERROR = 5;

        /**
         * When an action has occurred. The caller will only invoke this when the callback should
@@ -136,6 +137,10 @@ public abstract class AuthBiometricView extends LinearLayout {
        public ImageView getIconView() {
            return mBiometricView.findViewById(R.id.biometric_icon);
        }

        public int getDelayAfterError() {
            return BiometricPrompt.HIDE_DIALOG_DELAY;
        }
    }

    private final Injector mInjector;
@@ -186,6 +191,11 @@ public abstract class AuthBiometricView extends LinearLayout {
     */
    protected abstract void handleResetAfterHelp();

    /**
     * @return true if the dialog supports {@link AuthDialog.DialogSize#SIZE_SMALL}
     */
    protected abstract boolean supportsSmallDialog();

    private final Runnable mResetErrorRunnable = () -> {
        updateState(getStateForAfterError());
        handleResetAfterError();
@@ -250,7 +260,7 @@ public abstract class AuthBiometricView extends LinearLayout {

    @VisibleForTesting
    void updateSize(@AuthDialog.DialogSize int newSize) {
        Log.v(TAG, "Current: " + mSize + " New: " + newSize);
        Log.v(TAG, "Current size: " + mSize + " New size: " + newSize);
        if (newSize == AuthDialog.SIZE_SMALL) {
            mTitleView.setVisibility(View.GONE);
            mSubtitleView.setVisibility(View.GONE);
@@ -406,8 +416,18 @@ public abstract class AuthBiometricView extends LinearLayout {
        updateState(STATE_ERROR);
    }

    public void onError(String error) {
        showTemporaryMessage(error, mResetErrorRunnable);
        updateState(STATE_ERROR);

        mHandler.postDelayed(() -> {
            mCallback.onAction(Callback.ACTION_ERROR);
        }, mInjector.getDelayAfterError());
    }

    public void onHelp(String help) {
        if (mSize != AuthDialog.SIZE_MEDIUM) {
            Log.w(TAG, "Help received in size: " + mSize);
            return;
        }
        showTemporaryMessage(help, mResetHelpRunnable);
@@ -527,15 +547,6 @@ public abstract class AuthBiometricView extends LinearLayout {
            // Restore positive button state
            mTryAgainButton.setVisibility(
                    mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY));

            // Restore indicator text state
            final String indicatorText =
                    mSavedState.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING);
            if (mSavedState.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_HELP_SHOWING)) {
                onHelp(indicatorText);
            } else if (mSavedState.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)) {
                onAuthenticationFailed(indicatorText);
            }
        }
    }

@@ -600,9 +611,20 @@ public abstract class AuthBiometricView extends LinearLayout {
        if (mIconOriginalY == 0) {
            mIconOriginalY = mIconView.getY();
            if (mSavedState == null) {
                updateSize(mRequireConfirmation ? AuthDialog.SIZE_MEDIUM : AuthDialog.SIZE_SMALL);
                updateSize(!mRequireConfirmation && supportsSmallDialog() ? AuthDialog.SIZE_SMALL
                        : AuthDialog.SIZE_MEDIUM);
            } else {
                updateSize(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_DIALOG_SIZE));

                // Restore indicator text state only after size has been restored
                final String indicatorText =
                        mSavedState.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING);
                if (mSavedState.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_HELP_SHOWING)) {
                    onHelp(indicatorText);
                } else if (mSavedState.getBoolean(
                        AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)) {
                    onAuthenticationFailed(indicatorText);
                }
            }
        }
    }
+35 −12
Original line number Diff line number Diff line
@@ -21,9 +21,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PixelFormat;
import android.hardware.biometrics.BiometricAuthenticator;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.view.KeyEvent;
@@ -136,7 +136,8 @@ public class AuthContainerView extends LinearLayout
            return this;
        }

        public AuthContainerView build(int modalityMask) { // TODO
        public AuthContainerView build(int modalityMask) {
            mConfig.mModalityMask = modalityMask;
            return new AuthContainerView(mConfig);
        }
    }
@@ -158,6 +159,9 @@ public class AuthContainerView extends LinearLayout
                case AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN:
                    mConfig.mCallback.onTryAgainPressed();
                    break;
                case AuthBiometricView.Callback.ACTION_ERROR:
                    animateAway(AuthDialogCallback.DISMISSED_ERROR);
                    break;
                default:
                    Log.e(TAG, "Unhandled action: " + action);
            }
@@ -181,15 +185,26 @@ public class AuthContainerView extends LinearLayout
        mContainerView = (ViewGroup) factory.inflate(
                R.layout.auth_container_view, this, false /* attachToRoot */);

        // TODO: Depends on modality
        mPanelView = mContainerView.findViewById(R.id.panel);
        mPanelController = new AuthPanelController(mContext, mPanelView);

        // TODO: Update with new controllers if multi-modal authentication can occur simultaneously
        if (config.mModalityMask == BiometricAuthenticator.TYPE_FINGERPRINT) {
            mBiometricView = (AuthBiometricFingerprintView)
                    factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false);
        } else if (config.mModalityMask == BiometricAuthenticator.TYPE_FACE) {
            mBiometricView = (AuthBiometricFaceView)
                    factory.inflate(R.layout.auth_biometric_face_view, null, false);
        } else {
            Log.e(TAG, "Unsupported modality mask: " + config.mModalityMask);
            mBiometricView = null;
            mBackgroundView = null;
            mScrollView = null;
            return;
        }

        mBackgroundView = mContainerView.findViewById(R.id.background);

        mPanelView = mContainerView.findViewById(R.id.panel);
        mPanelController = new AuthPanelController(mContext, mPanelView);

        mBiometricView.setRequireConfirmation(mConfig.mRequireConfirmation);
        mBiometricView.setPanelController(mPanelController);
        mBiometricView.setBiometricPromptBundle(config.mBiometricPromptBundle);
@@ -281,13 +296,13 @@ public class AuthContainerView extends LinearLayout
        if (animate) {
            animateAway(false /* sendReason */, 0 /* reason */);
        } else {
            mWindowManager.removeView(this);
            removeWindowIfAttached();
        }
    }

    @Override
    public void dismissFromSystemServer() {
        mWindowManager.removeView(this);
        removeWindowIfAttached();
    }

    @Override
@@ -307,7 +322,7 @@ public class AuthContainerView extends LinearLayout

    @Override
    public void onError(String error) {

        mBiometricView.onError(error);
    }

    @Override
@@ -340,7 +355,7 @@ public class AuthContainerView extends LinearLayout

        final Runnable endActionRunnable = () -> {
            setVisibility(View.INVISIBLE);
            mWindowManager.removeView(this);
            removeWindowIfAttached();
            if (sendReason) {
                mConfig.mCallback.onDismissed(reason);
            }
@@ -369,6 +384,14 @@ public class AuthContainerView extends LinearLayout
        });
    }

    private void removeWindowIfAttached() {
        if (mContainerState == STATE_GONE) {
            return;
        }
        mContainerState = STATE_GONE;
        mWindowManager.removeView(this);
    }

    private void onDialogAnimatedIn() {
        if (mContainerState == STATE_PENDING_DISMISS) {
            Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now");
Loading