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

Commit 81219cb7 authored by Curtis Belmonte's avatar Curtis Belmonte Committed by Android (Google) Code Review
Browse files

Merge "Adjust UDFPS prompt for 3-button nav and landscape" into sc-dev

parents 56547bfd 0c706591
Loading
Loading
Loading
Loading
+8 −10
Original line number Diff line number Diff line
@@ -39,37 +39,34 @@

    <Space android:id="@+id/space_above_icon"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:visibility="visible" />
        android:layout_height="48dp" />

    <!-- Use a frame layout since certain biometrics (such as UDFPS) require the icon to be centered
         within a certain area on the display. This makes it easy to 1) guarantee max size, and
         2) center the icon within the reserved area. -->
    <FrameLayout android:id="@+id/biometric_icon_frame"
    <FrameLayout
        android:id="@+id/biometric_icon_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center_horizontal">

        <ImageView
            android:id="@+id/biometric_icon"
            android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
            android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
            android:layout_gravity="center"
            android:scaleType="fitXY" />

    </FrameLayout>

    <!-- For sensors such as UDFPS, this view is used during custom measurement/layout to add extra
         padding so that the biometric icon is always in the right physical position. -->
    <Space android:id="@+id/space_below_icon"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:visibility="gone" />
        android:layout_height="12dp" />

    <TextView
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingHorizontal="24dp"
        android:paddingVertical="12dp"
        android:textSize="12sp"
        android:gravity="center_horizontal"
        android:accessibilityLiveRegion="polite"
@@ -84,6 +81,7 @@
        style="?android:attr/buttonBarStyle"
        android:orientation="horizontal"
        android:paddingTop="16dp">

        <Space android:id="@+id/leftSpacer"
            android:layout_width="8dp"
            android:layout_height="match_parent"
+6 −0
Original line number Diff line number Diff line
@@ -244,6 +244,8 @@
        <item name="android:paddingTop">12dp</item>
        <item name="android:paddingHorizontal">24dp</item>
        <item name="android:textSize">24sp</item>
        <item name="android:singleLine">true</item>
        <item name="android:ellipsize">marquee</item>
    </style>

    <style name="TextAppearance.AuthCredential.Subtitle">
@@ -251,6 +253,8 @@
        <item name="android:paddingTop">8dp</item>
        <item name="android:paddingHorizontal">24dp</item>
        <item name="android:textSize">16sp</item>
        <item name="android:singleLine">true</item>
        <item name="android:ellipsize">marquee</item>
    </style>

    <style name="TextAppearance.AuthCredential.Description">
@@ -258,6 +262,8 @@
        <item name="android:paddingTop">8dp</item>
        <item name="android:paddingHorizontal">24dp</item>
        <item name="android:textSize">14sp</item>
        <item name="android:singleLine">true</item>
        <item name="android:ellipsize">marquee</item>
    </style>

    <style name="TextAppearance.AuthCredential.Error">
+226 −52
Original line number Diff line number Diff line
@@ -16,17 +16,22 @@

package com.android.systemui.biometrics;

import android.annotation.IdRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.FrameLayout;

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

/**
@@ -51,53 +56,38 @@ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
        mSensorProps = prop;
    }

    /**
     * For devices where the sensor is too high up, calculates the amount of padding necessary to
     * move/center the biometric icon within the sensor's physical location.
     */
    static int calculateBottomSpacerHeight(int displayHeightPx, int navbarHeightPx,
            int dialogBottomMarginPx, @NonNull View buttonBar, @NonNull View textIndicator,
            @NonNull FingerprintSensorPropertiesInternal sensorProperties) {
        final int sensorDistanceFromBottom = displayHeightPx - sensorProperties.sensorLocationY
                - sensorProperties.sensorRadius;

        final int spacerHeight = sensorDistanceFromBottom
                - textIndicator.getMeasuredHeight()
                - buttonBar.getMeasuredHeight()
                - dialogBottomMarginPx
                - navbarHeightPx;

        Log.d(TAG, "Display height: " + displayHeightPx
                + ", Distance from bottom: " + sensorDistanceFromBottom
                + ", Bottom margin: " + dialogBottomMarginPx
                + ", Navbar height: " + navbarHeightPx
                + ", Spacer height: " + spacerHeight);

        return spacerHeight;
    }

    @Override
    @NonNull
    AuthDialog.LayoutParams onMeasureInternal(int width, int height) {
        final View spaceBelowIcon = findViewById(R.id.space_below_icon);
        spaceBelowIcon.setVisibility(View.VISIBLE);
        final int displayRotation = getDisplay().getRotation();
        switch (displayRotation) {
            case Surface.ROTATION_0:
                return onMeasureInternalPortrait(width, height);
            case Surface.ROTATION_90:
            case Surface.ROTATION_270:
                return onMeasureInternalLandscape(width, height);
            default:
                Log.e(TAG, "Unsupported display rotation: " + displayRotation);
                return super.onMeasureInternal(width, height);
        }
    }

    @NonNull
    private AuthDialog.LayoutParams onMeasureInternalPortrait(int width, int height) {
        // Get the height of the everything below the icon. Currently, that's the indicator and
        // button bar
        final View textIndicator = findViewById(R.id.indicator);
        final View buttonBar = findViewById(R.id.button_bar);
        // button bar.
        final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
        final int buttonBarHeight = getViewHeightPx(R.id.button_bar);

        // Figure out where the bottom of the sensor anim should be.
        // Navbar + dialogMargin + buttonBar + textIndicator + spacerHeight = sensorDistFromBottom
        final int dialogBottomMarginPx = getResources()
                .getDimensionPixelSize(R.dimen.biometric_dialog_border_padding);
        final WindowManager wm = getContext().getSystemService(WindowManager.class);
        final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
        final int navbarHeight = wm.getCurrentWindowMetrics().getWindowInsets()
                .getInsets(WindowInsets.Type.navigationBars()).toRect().height();
        final int displayHeight = bounds.height();

        final int spacerHeight = calculateBottomSpacerHeight(displayHeight, navbarHeight,
                dialogBottomMarginPx, buttonBar, textIndicator, mSensorProps);
        final int dialogMargin = getDialogMarginPx();
        final WindowManager windowManager = getContext().getSystemService(WindowManager.class);
        final int displayHeight = getWindowBounds(windowManager).height();
        final Insets navbarInsets = getNavbarInsets(windowManager);
        final int bottomSpacerHeight = calculateBottomSpacerHeightForPortrait(
                mSensorProps, displayHeight, textIndicatorHeight, buttonBarHeight,
                dialogMargin, navbarInsets.bottom);

        // Go through each of the children and do the custom measurement.
        int totalHeight = 0;
@@ -105,22 +95,25 @@ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
        final int sensorDiameter = mSensorProps.sensorRadius * 2;
        for (int i = 0; i < numChildren; i++) {
            final View child = getChildAt(i);

            if (child.getId() == R.id.biometric_icon_frame) {
                // Create a frame that's exactly the size of the sensor circle
                child.measure(
                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY));
            } else if (child.getId() == R.id.biometric_icon) {
                // Icon should never be larger than the circle
                child.measure(
                final FrameLayout iconFrame = (FrameLayout) child;
                final View icon = iconFrame.getChildAt(0);

                // Ensure that the icon is never larger than the sensor.
                icon.measure(
                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST),
                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST));

                // Create a frame that's exactly the height of the sensor circle.
                iconFrame.measure(
                        MeasureSpec.makeMeasureSpec(
                                child.getLayoutParams().width, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY));
            } else if (child.getId() == R.id.space_above_icon) {
                child.measure(
                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
                                MeasureSpec.EXACTLY));
                        MeasureSpec.makeMeasureSpec(
                                child.getLayoutParams().height, MeasureSpec.EXACTLY));
            } else if (child.getId() == R.id.button_bar) {
                child.measure(
                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
@@ -128,8 +121,9 @@ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
                                MeasureSpec.EXACTLY));
            } else if (child.getId() == R.id.space_below_icon) {
                // Set the spacer height so the fingerprint icon is on the physical sensor area
                child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(spacerHeight, MeasureSpec.EXACTLY));
                child.measure(
                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(bottomSpacerHeight, MeasureSpec.EXACTLY));
            } else {
                child.measure(
                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
@@ -143,4 +137,184 @@ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {

        return new AuthDialog.LayoutParams(width, totalHeight);
    }

    @NonNull
    private AuthDialog.LayoutParams onMeasureInternalLandscape(int width, int height) {
        // Find the spacer height needed to vertically align the icon with the sensor.
        final int titleHeight = getViewHeightPx(R.id.title);
        final int subtitleHeight = getViewHeightPx(R.id.subtitle);
        final int descriptionHeight = getViewHeightPx(R.id.description);
        final int topSpacerHeight = getViewHeightPx(R.id.space_above_icon);
        final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
        final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
        final WindowManager windowManager = getContext().getSystemService(WindowManager.class);
        final Insets navbarInsets = getNavbarInsets(windowManager);
        final int bottomSpacerHeight = calculateBottomSpacerHeightForLandscape(titleHeight,
                subtitleHeight, descriptionHeight, topSpacerHeight, textIndicatorHeight,
                buttonBarHeight, navbarInsets.bottom);

        // Find the spacer width needed to horizontally align the icon with the sensor.
        final int displayWidth = getWindowBounds(windowManager).width();
        final int dialogMargin = getDialogMarginPx();
        final int horizontalInset = navbarInsets.left + navbarInsets.right;
        final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape(
                mSensorProps, displayWidth, dialogMargin, horizontalInset);

        final int sensorDiameter = mSensorProps.sensorRadius * 2;
        final int remeasuredWidth = sensorDiameter + 2 * horizontalSpacerWidth;

        int remeasuredHeight = 0;
        final int numChildren = getChildCount();
        for (int i = 0; i < numChildren; i++) {
            final View child = getChildAt(i);
            if (child.getId() == R.id.biometric_icon_frame) {
                final FrameLayout iconFrame = (FrameLayout) child;
                final View icon = iconFrame.getChildAt(0);

                // Ensure that the icon is never larger than the sensor.
                icon.measure(
                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST),
                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST));

                // Create a frame that's exactly the height of the sensor circle.
                iconFrame.measure(
                        MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY));
            } else if (child.getId() == R.id.space_above_icon || child.getId() == R.id.button_bar) {
                // Adjust the width of the top spacer and button bar while preserving their heights.
                child.measure(
                        MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(
                                child.getLayoutParams().height, MeasureSpec.EXACTLY));
            } else if (child.getId() == R.id.space_below_icon) {
                // Adjust the bottom spacer height to align the fingerprint icon with the sensor.
                child.measure(
                        MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(bottomSpacerHeight, MeasureSpec.EXACTLY));
            } else {
                // Use the remeasured width for all other child views.
                child.measure(
                        MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
            }

            if (child.getVisibility() != View.GONE) {
                remeasuredHeight += child.getMeasuredHeight();
            }
        }

        return new AuthDialog.LayoutParams(remeasuredWidth, remeasuredHeight);
    }

    private int getViewHeightPx(@IdRes int viewId) {
        final View view = findViewById(viewId);
        return view != null ? view.getMeasuredHeight() : 0;
    }

    private int getDialogMarginPx() {
        return getResources().getDimensionPixelSize(R.dimen.biometric_dialog_border_padding);
    }

    @NonNull
    private static Insets getNavbarInsets(@Nullable WindowManager windowManager) {
        return windowManager != null && windowManager.getCurrentWindowMetrics() != null
                ? windowManager.getCurrentWindowMetrics().getWindowInsets()
                        .getInsets(WindowInsets.Type.navigationBars())
                : Insets.NONE;
    }

    @NonNull
    private static Rect getWindowBounds(@Nullable WindowManager windowManager) {
        return windowManager != null && windowManager.getCurrentWindowMetrics() != null
                ? windowManager.getCurrentWindowMetrics().getBounds()
                : new Rect();
    }

    /**
     * For devices in portrait orientation where the sensor is too high up, calculates the amount of
     * padding necessary to center the biometric icon within the sensor's physical location.
     */
    @VisibleForTesting
    static int calculateBottomSpacerHeightForPortrait(
            @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayHeightPx,
            int textIndicatorHeightPx, int buttonBarHeightPx, int dialogMarginPx,
            int navbarBottomInsetPx) {

        final int sensorDistanceFromBottom = displayHeightPx
                - sensorProperties.sensorLocationY
                - sensorProperties.sensorRadius;

        final int spacerHeight = sensorDistanceFromBottom
                - textIndicatorHeightPx
                - buttonBarHeightPx
                - dialogMarginPx
                - navbarBottomInsetPx;

        Log.d(TAG, "Display height: " + displayHeightPx
                + ", Distance from bottom: " + sensorDistanceFromBottom
                + ", Bottom margin: " + dialogMarginPx
                + ", Navbar bottom inset: " + navbarBottomInsetPx
                + ", Bottom spacer height (portrait): " + spacerHeight);

        return spacerHeight;
    }

    /**
     * For devices in landscape orientation where the sensor is too high up, calculates the amount
     * of padding necessary to center the biometric icon within the sensor's physical location.
     */
    @VisibleForTesting
    static int calculateBottomSpacerHeightForLandscape(int titleHeightPx, int subtitleHeightPx,
            int descriptionHeightPx, int topSpacerHeightPx, int textIndicatorHeightPx,
            int buttonBarHeightPx, int navbarBottomInsetPx) {

        final int dialogHeightAboveIcon = titleHeightPx
                + subtitleHeightPx
                + descriptionHeightPx
                + topSpacerHeightPx;

        final int dialogHeightBelowIcon = textIndicatorHeightPx + buttonBarHeightPx;

        final int bottomSpacerHeight = dialogHeightAboveIcon
                - dialogHeightBelowIcon
                - navbarBottomInsetPx;

        Log.d(TAG, "Title height: " + titleHeightPx
                + ", Subtitle height: " + subtitleHeightPx
                + ", Description height: " + descriptionHeightPx
                + ", Top spacer height: " + topSpacerHeightPx
                + ", Text indicator height: " + textIndicatorHeightPx
                + ", Button bar height: " + buttonBarHeightPx
                + ", Navbar bottom inset: " + navbarBottomInsetPx
                + ", Bottom spacer height (landscape): " + bottomSpacerHeight);

        return bottomSpacerHeight;
    }

    /**
     * For devices in landscape orientation where the sensor is too left/right, calculates the
     * amount of padding necessary to center the biometric icon within the sensor's physical
     * location.
     */
    @VisibleForTesting
    static int calculateHorizontalSpacerWidthForLandscape(
            @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayWidthPx,
            int dialogMarginPx, int navbarHorizontalInsetPx) {

        final int sensorDistanceFromEdge = displayWidthPx
                - sensorProperties.sensorLocationY
                - sensorProperties.sensorRadius;

        final int horizontalPadding = sensorDistanceFromEdge
                - dialogMarginPx
                - navbarHorizontalInsetPx;

        Log.d(TAG, "Display width: " + displayWidthPx
                + ", Distance from edge: " + sensorDistanceFromEdge
                + ", Dialog margin: " + dialogMarginPx
                + ", Navbar horizontal inset: " + navbarHorizontalInsetPx
                + ", Horizontal spacer width (landscape): " + horizontalPadding);

        return horizontalPadding;
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -742,6 +742,7 @@ public abstract class AuthBiometricView extends LinearLayout {
     * @param height Height to constrain the measurements to.
     * @return See {@link AuthDialog.LayoutParams}
     */
    @NonNull
    AuthDialog.LayoutParams onMeasureInternal(int width, int height) {
        int totalHeight = 0;
        final int numChildren = getChildCount();
+32 −0
Original line number Diff line number Diff line
@@ -32,8 +32,10 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.UserManager;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -433,6 +435,29 @@ public class AuthContainerView extends LinearLayout
                    + mConfig.mPromptInfo.getAuthenticators());
        }

        if (mBiometricView instanceof AuthBiometricUdfpsView) {
            final int displayRotation = getDisplay().getRotation();
            switch (displayRotation) {
                case Surface.ROTATION_0:
                    mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
                    setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
                    break;
                case Surface.ROTATION_90:
                    mPanelController.setPosition(AuthPanelController.POSITION_RIGHT);
                    setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
                    break;
                case Surface.ROTATION_270:
                    mPanelController.setPosition(AuthPanelController.POSITION_LEFT);
                    setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
                    break;
                default:
                    Log.e(TAG, "Unsupported display rotation: " + displayRotation);
                    mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
                    setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
                    break;
            }
        }

        if (mConfig.mSkipIntro) {
            mContainerState = STATE_SHOWING;
        } else {
@@ -476,6 +501,13 @@ public class AuthContainerView extends LinearLayout
        }
    }

    private void setScrollViewGravity(int gravity) {
        final FrameLayout.LayoutParams params =
                (FrameLayout.LayoutParams) mBiometricScrollView.getLayoutParams();
        params.gravity = gravity;
        mBiometricScrollView.setLayoutParams(params);
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
Loading