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

Commit 504f77d8 authored by Kevin Chyn's avatar Kevin Chyn
Browse files

2/n: Start plumbing authentication signals to the UI

Test: Authentication in both small/medium dialog
Test: Negative button, Cancel button, Try again button
Test: atest AuthBiometricFaceViewTest
Test: atest AuthBiometricViewTest
Test: atest AuthContainerViewTest

Bug: 123378871

Change-Id: Iab5edf863d0ef26855a65585c73e2d495b2ab6e9
parent fc46826f
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -65,10 +65,10 @@
        android:textSize="12sp"
        android:gravity="center_horizontal"
        android:accessibilityLiveRegion="polite"
        android:textColor="@color/biometric_dialog_gray"
        android:text="ERROR"/>
        android:textColor="@color/biometric_dialog_gray"/>

    <LinearLayout
        android:id="@+id/button_bar"
        android:layout_width="match_parent"
        android:layout_height="72dip"
        android:paddingTop="24dp"
@@ -85,8 +85,7 @@
            android:layout_height="match_parent"
            style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
            android:gravity="center"
            android:maxLines="2"
            android:text="NEGATIVE"/>
            android:maxLines="2"/>
        <Space android:id="@+id/middleSpacer"
            android:layout_width="0dp"
            android:layout_height="match_parent"
+5 −0
Original line number Diff line number Diff line
@@ -61,6 +61,11 @@ public interface BiometricDialog {
    @IntDef({SIZE_UNKNOWN, SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE})
    @interface DialogSize {}

    /**
     * Animation duration, e.g. small to medium dialog, icon translation, etc.
     */
    int ANIMATE_DURATION_MS = 150;

    /**
     * Show the dialog.
     * @param wm
+92 −10
Original line number Diff line number Diff line
@@ -23,33 +23,44 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
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;
import com.android.systemui.biometrics.BiometricDialog;

public class AuthBiometricFaceView extends AuthBiometricView {

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

    // 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
        @State int mState;

        IconController(Context context, ImageView iconView) {
        IconController(Context context, ImageView iconView, TextView textView) {
            mContext = context;
            mIconView = iconView;
            mTextView = textView;
            mHandler = new Handler(Looper.getMainLooper());
            showIcon(R.drawable.face_dialog_pulse_dark_to_light);
            showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light);
        }

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

        void showIcon(int iconRes) {
            final Drawable drawable = mContext.getDrawable(iconRes);
            mIconView.setImageDrawable(drawable);
        public void showStaticDrawable(int iconRes) {
            mIconView.setImageDrawable(mContext.getDrawable(iconRes));
        }

        void animateIcon(int iconRes, boolean repeat) {
@@ -78,14 +89,45 @@ public class AuthBiometricFaceView extends AuthBiometricView {
        @Override
        public void onAnimationEnd(Drawable drawable) {
            super.onAnimationEnd(drawable);
            if (mState == STATE_AUTHENTICATING) {
            if (mState == STATE_AUTHENTICATING || mState == STATE_HELP) {
                pulseInNextDirection();
            }
        }

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

            if (newState == STATE_AUTHENTICATING_ANIMATING_IN) {
                showStaticDrawable(R.drawable.face_dialog_pulse_dark_to_light);
                mIconView.setContentDescription(mContext.getString(
                        R.string.biometric_dialog_face_icon_description_authenticating));
            } else if (newState == STATE_AUTHENTICATING) {
                startPulsing();
                mIconView.setContentDescription(mContext.getString(
                        R.string.biometric_dialog_face_icon_description_authenticating));
            } else if (lastState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) {
                animateOnce(R.drawable.face_dialog_dark_to_checkmark);
                mIconView.setContentDescription(mContext.getString(
                        R.string.biometric_dialog_face_icon_description_confirmed));
            } else if (lastStateIsErrorIcon && newState == STATE_IDLE) {
                animateOnce(R.drawable.face_dialog_error_to_idle);
            } else if (lastStateIsErrorIcon && newState == STATE_AUTHENTICATED) {
                animateOnce(R.drawable.face_dialog_dark_to_checkmark);
            } else if (newState == STATE_ERROR && lastState != STATE_ERROR) {
                animateOnce(R.drawable.face_dialog_dark_to_error);
            } else if (lastState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) {
                animateOnce(R.drawable.face_dialog_dark_to_checkmark);
                mIconView.setContentDescription(mContext.getString(
                        R.string.biometric_dialog_face_icon_description_authenticated));
            } else if (newState == STATE_PENDING_CONFIRMATION) {
                animateOnce(R.drawable.face_dialog_wink_from_dark);
                mIconView.setContentDescription(mContext.getString(
                        R.string.biometric_dialog_face_icon_description_authenticated));
            } else if (newState == STATE_IDLE) {
                showStaticDrawable(R.drawable.face_dialog_idle_static);
            } else {
                Log.w(TAG, "Unhandled state: " + newState);
            }
            mState = newState;
        }
@@ -106,15 +148,55 @@ public class AuthBiometricFaceView extends AuthBiometricView {
        return HIDE_DELAY_MS;
    }

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

    @Override
    protected void handleResetAfterError() {
        resetErrorView(mContext, mErrorView);
    }

    @Override
    protected void handleResetAfterHelp() {
        resetErrorView(mContext, mErrorView);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mIconController = new IconController(mContext, mIconView);
        mIconController = new IconController(mContext, mIconView, mErrorView);
    }

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

        if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
                (newState == STATE_AUTHENTICATING && mSize == BiometricDialog.SIZE_MEDIUM)) {
            resetErrorView(mContext, mErrorView);
        }

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

    @Override
    public void onAuthenticationFailed(String failureReason) {
        if (mSize == BiometricDialog.SIZE_MEDIUM) {
            mTryAgainButton.setVisibility(View.VISIBLE);
            mPositiveButton.setVisibility(View.GONE);
        }

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

    static void resetErrorView(Context context, TextView textView) {
        textView.setTextColor(context.getResources().getColor(
                R.color.biometric_dialog_gray, context.getTheme()));
        textView.setVisibility(View.INVISIBLE);
    }
}
+292 −45
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.systemui.biometrics.ui;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.content.Context;
import android.hardware.biometrics.BiometricPrompt;
@@ -31,6 +35,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.biometrics.BiometricDialog;

@@ -56,21 +61,26 @@ public abstract class AuthBiometricView extends LinearLayout {
     * UI animated in, authentication hardware active.
     */
    protected static final int STATE_AUTHENTICATING = 2;
    /**
     * UI animated in, authentication hardware active.
     */
    protected static final int STATE_HELP = 3;
    /**
     * Hard error, e.g. ERROR_TIMEOUT. Authentication hardware idle.
     */
    protected static final int STATE_ERROR = 3;
    protected static final int STATE_ERROR = 4;
    /**
     * Authenticated, waiting for user confirmation. Authentication hardware idle.
     */
    protected static final int STATE_PENDING_CONFIRMATION = 4;
    protected static final int STATE_PENDING_CONFIRMATION = 5;
    /**
     * Authenticated, dialog animating away soon.
     */
    protected static final int STATE_AUTHENTICATED = 5;
    protected static final int STATE_AUTHENTICATED = 6;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_ERROR,
            STATE_PENDING_CONFIRMATION, STATE_AUTHENTICATED})
    @IntDef({STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_HELP,
            STATE_ERROR, STATE_PENDING_CONFIRMATION, STATE_AUTHENTICATED})
    @interface State {}

    /**
@@ -78,6 +88,9 @@ public abstract class AuthBiometricView extends LinearLayout {
     */
    interface Callback {
        int ACTION_AUTHENTICATED = 1;
        int ACTION_USER_CANCELED = 2;
        int ACTION_BUTTON_NEGATIVE = 3;
        int ACTION_BUTTON_TRY_AGAIN = 4;

        /**
         * When an action has occurred. The caller will only invoke this when the callback should
@@ -87,46 +100,104 @@ public abstract class AuthBiometricView extends LinearLayout {
        void onAction(int action);
    }

    @VisibleForTesting
    static class Injector {
        AuthBiometricView mBiometricView;

        public Button getNegativeButton() {
            return mBiometricView.findViewById(R.id.button_negative);
        }

        public Button getPositiveButton() {
            return mBiometricView.findViewById(R.id.button_positive);
        }

        public Button getTryAgainButton() {
            return mBiometricView.findViewById(R.id.button_try_again);
        }

        public TextView getErrorView() {
            return mBiometricView.findViewById(R.id.error);
        }
    }

    private final Injector mInjector;
    private final Handler mHandler;
    private final int mTextColorError;
    private final int mTextColorHint;

    private AuthPanelController mPanelController;
    private Bundle mBundle;
    private boolean mRequireConfirmation;
    private @BiometricDialog.DialogSize int mSize = BiometricDialog.SIZE_UNKNOWN;
    @BiometricDialog.DialogSize int mSize = BiometricDialog.SIZE_UNKNOWN;

    private TextView mTitleView;
    private TextView mSubtitleView;
    private TextView mDescriptionView;
    protected ImageView mIconView;
    private TextView mErrorView;
    private Button mNegativeButton;
    private Button mPositiveButton;
    private Button mTryAgainButton;
    @VisibleForTesting protected TextView mErrorView;
    @VisibleForTesting Button mNegativeButton;
    @VisibleForTesting Button mPositiveButton;
    @VisibleForTesting Button mTryAgainButton;

    // Measurements when biometric view is showing text, buttons, etc.
    private int mMediumHeight;
    private int mMediumWidth;

    private int mCurrentHeight;
    private int mCurrentWidth;
    private Callback mCallback;
    protected @State int mState;

    private float mIconOriginalY;

    protected boolean mDialogAnimatedIn;
    protected boolean mDialogSizeAnimating;

    /**
     * Delay after authentication is confirmed, before the dialog should be animated away.
     */
    protected abstract int getDelayAfterAuthenticatedDurationMs();
    /**
     * State that the dialog/icon should be in after showing a help message.
     */
    protected abstract int getStateForAfterError();
    /**
     * Invoked when the error message is being cleared.
     */
    protected abstract void handleResetAfterError();
    /**
     * Invoked when the help message is being cleared.
     */
    protected abstract void handleResetAfterHelp();

    private final Runnable mResetErrorRunnable = () -> {
        updateState(getStateForAfterError());
        handleResetAfterError();
    };

    private final Runnable mResetHelpRunnable = () -> {
        updateState(STATE_AUTHENTICATING);
        handleResetAfterHelp();
    };

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

    public AuthBiometricView(Context context, AttributeSet attrs) {
        this(context, attrs, new Injector());
    }

    @VisibleForTesting
    AuthBiometricView(Context context, AttributeSet attrs, Injector injector) {
        super(context, attrs);
        mHandler = new Handler(Looper.getMainLooper());
        mTextColorError = getResources().getColor(
                R.color.biometric_dialog_error, context.getTheme());
        mTextColorHint = getResources().getColor(
                R.color.biometric_dialog_gray, context.getTheme());

        addOnLayoutChangeListener(new OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom,
                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
                updateSize(mRequireConfirmation ? BiometricDialog.SIZE_MEDIUM
                        : BiometricDialog.SIZE_SMALL);
                mPanelController.updateForContentDimensions(mCurrentWidth, mCurrentHeight);
            }
        });
        mInjector = injector;
        mInjector.mBiometricView = this;
    }

    public void setPanelController(AuthPanelController panelController) {
@@ -145,12 +216,8 @@ public abstract class AuthBiometricView extends LinearLayout {
        mRequireConfirmation = requireConfirmation;
    }

    public void updateSize(@BiometricDialog.DialogSize int newSize) {
        if (mSize == newSize) {
            Log.w(TAG, "Skipping updating size: " + mSize);
            return;
        }

    private void updateSize(@BiometricDialog.DialogSize int newSize) {
        Log.v(TAG, "Current: " + mSize + " New: " + newSize);
        if (newSize == BiometricDialog.SIZE_SMALL) {
            mTitleView.setVisibility(View.GONE);
            mSubtitleView.setVisibility(View.GONE);
@@ -162,23 +229,127 @@ public abstract class AuthBiometricView extends LinearLayout {
                    .getDimension(R.dimen.biometric_dialog_icon_padding);
            mIconView.setY(getHeight() - mIconView.getHeight() - iconPadding);

            mCurrentHeight = mIconView.getHeight() + 2 * (int) iconPadding;
            final int newHeight = mIconView.getHeight() + 2 * (int) iconPadding;
            mPanelController.updateForContentDimensions(mMediumWidth, newHeight,
                    false /* animate */);

            mSize = newSize;
        } else if (mSize == BiometricDialog.SIZE_SMALL && newSize == BiometricDialog.SIZE_MEDIUM) {
            if (mDialogSizeAnimating) {
                return;
            }
            mDialogSizeAnimating = true;

            // Animate the icon back to original position
            final ValueAnimator iconAnimator =
                    ValueAnimator.ofFloat(mIconView.getY(), mIconOriginalY);
            iconAnimator.addUpdateListener((animation) -> {
                mIconView.setY((float) animation.getAnimatedValue());
            });

            // Animate the text
            final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(0, 1);
            opacityAnimator.setDuration(BiometricDialog.ANIMATE_DURATION_MS);
            opacityAnimator.addUpdateListener((animation) -> {
                final float opacity = (float) animation.getAnimatedValue();

                mTitleView.setAlpha(opacity);
                mErrorView.setAlpha(opacity);
                mNegativeButton.setAlpha(opacity);
                mTryAgainButton.setAlpha(opacity);

                if (!TextUtils.isEmpty(mSubtitleView.getText())) {
                    mSubtitleView.setAlpha(opacity);
                }
                if (!TextUtils.isEmpty(mDescriptionView.getText())) {
                    mDescriptionView.setAlpha(opacity);
                }
            });

            // Choreograph together
            final AnimatorSet as = new AnimatorSet();
            as.setDuration(BiometricDialog.ANIMATE_DURATION_MS);
            as.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    super.onAnimationStart(animation);
                    mTitleView.setVisibility(View.VISIBLE);
                    mErrorView.setVisibility(View.VISIBLE);
                    mNegativeButton.setVisibility(View.VISIBLE);
                    mTryAgainButton.setVisibility(View.VISIBLE);

                    if (!TextUtils.isEmpty(mSubtitleView.getText())) {
                        mSubtitleView.setVisibility(View.VISIBLE);
                    }
                    if (!TextUtils.isEmpty(mDescriptionView.getText())) {
                        mDescriptionView.setVisibility(View.VISIBLE);
                    }
                }
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    mSize = newSize;
                    mDialogSizeAnimating = false;
                }
            });

            as.play(iconAnimator).with(opacityAnimator);
            as.start();
            // Animate the panel
            mPanelController.updateForContentDimensions(mMediumWidth, mMediumHeight,
                    true /* animate */);
        } else if (newSize == BiometricDialog.SIZE_MEDIUM) {
            mPanelController.updateForContentDimensions(mMediumWidth, mMediumHeight,
                    false /* animate */);
            mSize = newSize;
        } else {
            Log.e(TAG, "Unknown transition from: " + mSize + " to: " + newSize);
        }
    }

    public void updateState(@State int newState) {
        Log.v(TAG, "newState: " + newState);
        if (newState == STATE_AUTHENTICATED) {
        switch (newState) {
            case STATE_AUTHENTICATING_ANIMATING_IN:
            case STATE_AUTHENTICATING:
                removePendingAnimations();
                if (mRequireConfirmation) {
                    mPositiveButton.setEnabled(false);
                    mPositiveButton.setVisibility(View.VISIBLE);
                }
                break;

            } else {
                mHandler.postDelayed(() -> {
                    mCallback.onAction(Callback.ACTION_AUTHENTICATED);
                }, getDelayAfterAuthenticatedDurationMs());
            case STATE_AUTHENTICATED:
                if (mSize != BiometricDialog.SIZE_SMALL) {
                    mPositiveButton.setVisibility(View.GONE);
                    mNegativeButton.setVisibility(View.GONE);
                    mErrorView.setVisibility(View.INVISIBLE);
                }
                mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED),
                        getDelayAfterAuthenticatedDurationMs());
                break;

            case STATE_PENDING_CONFIRMATION:
                removePendingAnimations();
                mNegativeButton.setText(R.string.cancel);
                mNegativeButton.setContentDescription(getResources().getString(R.string.cancel));
                mPositiveButton.setEnabled(true);
                mErrorView.setTextColor(mTextColorHint);
                mErrorView.setText(R.string.biometric_dialog_tap_confirm);
                mErrorView.setVisibility(View.VISIBLE);
                break;

            case STATE_ERROR:
                if (mSize == BiometricDialog.SIZE_SMALL) {
                    updateSize(BiometricDialog.SIZE_MEDIUM);
                }
                break;

            default:
                Log.w(TAG, "Unhandled state: " + newState);
                break;
        }

        mState = newState;
    }

@@ -187,6 +358,7 @@ public abstract class AuthBiometricView extends LinearLayout {
    }

    public void onAuthenticationSucceeded() {
        removePendingAnimations();
        if (mRequireConfirmation) {
            updateState(STATE_PENDING_CONFIRMATION);
        } else {
@@ -194,6 +366,19 @@ public abstract class AuthBiometricView extends LinearLayout {
        }
    }

    public void onAuthenticationFailed(String failureReason) {
        showTemporaryMessage(failureReason, mResetErrorRunnable);
        updateState(STATE_ERROR);
    }

    public void onHelp(String help) {
        if (mSize != BiometricDialog.SIZE_MEDIUM) {
            return;
        }
        showTemporaryMessage(help, mResetHelpRunnable);
        updateState(STATE_HELP);
    }

    private void setTextOrHide(TextView view, String string) {
        if (TextUtils.isEmpty(string)) {
            view.setVisibility(View.GONE);
@@ -202,23 +387,67 @@ public abstract class AuthBiometricView extends LinearLayout {
        }
    }

    private void setText(TextView view, String string) {
        view.setText(string);
    }

    // Remove all pending icon and text animations
    private void removePendingAnimations() {
        mHandler.removeCallbacks(mResetHelpRunnable);
        mHandler.removeCallbacks(mResetErrorRunnable);
    }

    private void showTemporaryMessage(String message, Runnable resetMessageRunnable) {
        removePendingAnimations();
        mErrorView.setText(message);
        mErrorView.setTextColor(mTextColorError);
        mErrorView.setVisibility(View.VISIBLE);
        mHandler.postDelayed(resetMessageRunnable, BiometricPrompt.HIDE_DIALOG_DELAY);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        initializeViews();
    }

    @VisibleForTesting
    void initializeViews() {
        mTitleView = findViewById(R.id.title);
        mSubtitleView = findViewById(R.id.subtitle);
        mDescriptionView = findViewById(R.id.description);
        mIconView = findViewById(R.id.biometric_icon);
        mErrorView = findViewById(R.id.error);
        mNegativeButton = findViewById(R.id.button_negative);
        mPositiveButton = findViewById(R.id.button_positive);
        mTryAgainButton = findViewById(R.id.button_try_again);

        mErrorView = mInjector.getErrorView();
        mNegativeButton = mInjector.getNegativeButton();
        mPositiveButton = mInjector.getPositiveButton();
        mTryAgainButton = mInjector.getTryAgainButton();

        mNegativeButton.setOnClickListener((view) -> {
            if (mState == STATE_PENDING_CONFIRMATION) {
                mCallback.onAction(Callback.ACTION_USER_CANCELED);
            } else {
                mCallback.onAction(Callback.ACTION_BUTTON_NEGATIVE);
            }
        });

        mPositiveButton.setOnClickListener((view) -> {
            updateState(STATE_AUTHENTICATED);
        });

        mTryAgainButton.setOnClickListener((view) -> {
            updateState(STATE_AUTHENTICATING);
            mCallback.onAction(Callback.ACTION_BUTTON_TRY_AGAIN);
            mTryAgainButton.setVisibility(View.GONE);
        });
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        setTextOrHide(mTitleView, mBundle.getString(BiometricPrompt.KEY_TITLE));
        setText(mTitleView, mBundle.getString(BiometricPrompt.KEY_TITLE));
        setText(mNegativeButton, mBundle.getString(BiometricPrompt.KEY_NEGATIVE_TEXT));

        setTextOrHide(mSubtitleView, mBundle.getString(BiometricPrompt.KEY_SUBTITLE));
        setTextOrHide(mDescriptionView, mBundle.getString(BiometricPrompt.KEY_DESCRIPTION));

@@ -240,6 +469,11 @@ public abstract class AuthBiometricView extends LinearLayout {
                child.measure(
                        MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.AT_MOST),
                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
            } else if (child.getId() == R.id.button_bar) {
                child.measure(
                        MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
                                MeasureSpec.EXACTLY));
            } else {
                child.measure(
                        MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY),
@@ -251,7 +485,20 @@ public abstract class AuthBiometricView extends LinearLayout {
        // Use the new width so it's centered horizontally
        setMeasuredDimension(newWidth, totalHeight);

        mCurrentHeight = getMeasuredHeight();
        mCurrentWidth = getMeasuredWidth();
        mMediumHeight = totalHeight;
        mMediumWidth = getMeasuredWidth();
    }

    @Override
    public void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        // Start with initial size only once. Subsequent layout changes don't matter since we
        // only care about the initial icon position.
        if (mIconOriginalY == 0) {
            mIconOriginalY = mIconView.getY();
            updateSize(mRequireConfirmation ? BiometricDialog.SIZE_MEDIUM
                    : BiometricDialog.SIZE_SMALL);
        }
    }
}
+31 −14

File changed.

Preview size limit exceeded, changes collapsed.

Loading