diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java index 9a9e47868b856..3a9d143425453 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java @@ -43,24 +43,36 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna * The location of the center of the sensor if applicable. For example, sensors of type * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the * distance in pixels, measured from the left edge of the screen. - * TODO: Value should be provided from the HAL */ - public final int sensorLocationX = 540; + public final int sensorLocationX; /** * The location of the center of the sensor if applicable. For example, sensors of type * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the * distance in pixels, measured from the top edge of the screen. - * TODO: Value should be provided from the HAL + * */ - public final int sensorLocationY = 1636; + public final int sensorLocationY; /** * The radius of the sensor if applicable. For example, sensors of type * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius * of the sensor, in pixels. */ - public final int sensorRadius = 130; + public final int sensorRadius; + + public FingerprintSensorPropertiesInternal(int sensorId, + @SensorProperties.Strength int strength, int maxEnrollmentsPerUser, + @FingerprintSensorProperties.SensorType int sensorType, + boolean resetLockoutRequiresHardwareAuthToken, int sensorLocationX, int sensorLocationY, + int sensorRadius) { + super(sensorId, strength, maxEnrollmentsPerUser); + this.sensorType = sensorType; + this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken; + this.sensorLocationX = sensorLocationX; + this.sensorLocationY = sensorLocationY; + this.sensorRadius = sensorRadius; + } /** * Initializes SensorProperties with specified values @@ -69,15 +81,19 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna @SensorProperties.Strength int strength, int maxEnrollmentsPerUser, @FingerprintSensorProperties.SensorType int sensorType, boolean resetLockoutRequiresHardwareAuthToken) { - super(sensorId, strength, maxEnrollmentsPerUser); - this.sensorType = sensorType; - this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken; + // TODO: Value should be provided from the HAL + this(sensorId, strength, maxEnrollmentsPerUser, sensorType, + resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */, + 1636 /* sensorLocationY */, 130 /* sensorRadius */); } protected FingerprintSensorPropertiesInternal(Parcel in) { super(in); sensorType = in.readInt(); resetLockoutRequiresHardwareAuthToken = in.readBoolean(); + sensorLocationX = in.readInt(); + sensorLocationY = in.readInt(); + sensorRadius = in.readInt(); } public static final Creator CREATOR = @@ -103,6 +119,9 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna super.writeToParcel(dest, flags); dest.writeInt(sensorType); dest.writeBoolean(resetLockoutRequiresHardwareAuthToken); + dest.writeInt(sensorLocationX); + dest.writeInt(sensorLocationY); + dest.writeInt(sensorRadius); } public boolean isAnyUdfpsType() { diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml index a87c7b3fa9275..2439fedd05182 100644 --- a/packages/SystemUI/res/layout/auth_biometric_contents.xml +++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml @@ -37,13 +37,32 @@ android:gravity="@integer/biometric_dialog_text_gravity" style="@style/TextAppearance.AuthCredential.Description"/> - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java index 1d47fc520ec2a..9a40541ac754e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java @@ -184,7 +184,7 @@ public class AuthBiometricFaceView extends AuthBiometricView { mIconController.updateState(mState, newState); if (newState == STATE_AUTHENTICATING_ANIMATING_IN || - (newState == STATE_AUTHENTICATING && mSize == AuthDialog.SIZE_MEDIUM)) { + (newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) { resetErrorView(mContext, mIndicatorView); } @@ -194,7 +194,7 @@ public class AuthBiometricFaceView extends AuthBiometricView { @Override public void onAuthenticationFailed(String failureReason) { - if (mSize == AuthDialog.SIZE_MEDIUM) { + if (getSize() == AuthDialog.SIZE_MEDIUM) { mTryAgainButton.setVisibility(View.VISIBLE); mPositiveButton.setVisibility(View.GONE); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java new file mode 100644 index 0000000000000..cc608ef87bc6b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2020 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.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Rect; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.WindowInsets; +import android.view.WindowManager; + +import com.android.systemui.R; + +/** + * Manages the layout for under-display fingerprint sensors (UDFPS). Ensures that UI elements + * do not overlap with + */ +public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView { + + private static final String TAG = "AuthBiometricUdfpsView"; + + @Nullable private FingerprintSensorPropertiesInternal mSensorProps; + + public AuthBiometricUdfpsView(Context context) { + this(context, null /* attrs */); + } + + public AuthBiometricUdfpsView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + void setSensorProps(@NonNull FingerprintSensorPropertiesInternal prop) { + 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 + AuthDialog.LayoutParams onMeasureInternal(int width, int height) { + final View spaceBelowIcon = findViewById(R.id.space_below_icon); + spaceBelowIcon.setVisibility(View.VISIBLE); + + // 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); + + // 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); + + // Go through each of the children and do the custom measurement. + int totalHeight = 0; + final int numChildren = getChildCount(); + 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( + MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST)); + } else if (child.getId() == R.id.space_above_icon) { + child.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, + MeasureSpec.EXACTLY)); + } else if (child.getId() == R.id.button_bar) { + child.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, + 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)); + } else { + child.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); + } + + if (child.getVisibility() != View.GONE) { + totalHeight += child.getMeasuredHeight(); + } + } + + return new AuthDialog.LayoutParams(width, totalHeight); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index ee6c465f71cc5..0608ca236f86d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -144,6 +144,10 @@ public abstract class AuthBiometricView extends LinearLayout { return mBiometricView.findViewById(R.id.biometric_icon); } + public View getIconHolderView() { + return mBiometricView.findViewById(R.id.biometric_icon_frame); + } + public int getDelayAfterError() { return BiometricPrompt.HIDE_DIALOG_DELAY; } @@ -164,11 +168,12 @@ public abstract class AuthBiometricView extends LinearLayout { private boolean mRequireConfirmation; private int mUserId; private int mEffectiveUserId; - @AuthDialog.DialogSize int mSize = AuthDialog.SIZE_UNKNOWN; + private @AuthDialog.DialogSize int mSize = AuthDialog.SIZE_UNKNOWN; private TextView mTitleView; private TextView mSubtitleView; private TextView mDescriptionView; + private View mIconHolderView; protected ImageView mIconView; @VisibleForTesting protected TextView mIndicatorView; @VisibleForTesting Button mNegativeButton; @@ -176,8 +181,7 @@ public abstract class AuthBiometricView extends LinearLayout { @VisibleForTesting Button mTryAgainButton; // Measurements when biometric view is showing text, buttons, etc. - private int mMediumHeight; - private int mMediumWidth; + @Nullable @VisibleForTesting AuthDialog.LayoutParams mLayoutParams; private Callback mCallback; protected @BiometricState int mState; @@ -302,13 +306,13 @@ public abstract class AuthBiometricView extends LinearLayout { final float iconPadding = getResources() .getDimension(R.dimen.biometric_dialog_icon_padding); - mIconView.setY(getHeight() - mIconView.getHeight() - iconPadding); + mIconHolderView.setY(getHeight() - mIconHolderView.getHeight() - iconPadding); // Subtract the vertical padding from the new height since it's only used to create // extra space between the other elements, and not part of the actual icon. - final int newHeight = mIconView.getHeight() + 2 * (int) iconPadding - - mIconView.getPaddingTop() - mIconView.getPaddingBottom(); - mPanelController.updateForContentDimensions(mMediumWidth, newHeight, + final int newHeight = mIconHolderView.getHeight() + 2 * (int) iconPadding + - mIconHolderView.getPaddingTop() - mIconHolderView.getPaddingBottom(); + mPanelController.updateForContentDimensions(mLayoutParams.mMediumWidth, newHeight, 0 /* animateDurationMs */); mSize = newSize; @@ -320,9 +324,9 @@ public abstract class AuthBiometricView extends LinearLayout { // Animate the icon back to original position final ValueAnimator iconAnimator = - ValueAnimator.ofFloat(mIconView.getY(), mIconOriginalY); + ValueAnimator.ofFloat(mIconHolderView.getY(), mIconOriginalY); iconAnimator.addUpdateListener((animation) -> { - mIconView.setY((float) animation.getAnimatedValue()); + mIconHolderView.setY((float) animation.getAnimatedValue()); }); // Animate the text @@ -374,10 +378,12 @@ public abstract class AuthBiometricView extends LinearLayout { as.play(iconAnimator).with(opacityAnimator); as.start(); // Animate the panel - mPanelController.updateForContentDimensions(mMediumWidth, mMediumHeight, + mPanelController.updateForContentDimensions(mLayoutParams.mMediumWidth, + mLayoutParams.mMediumHeight, AuthDialog.ANIMATE_SMALL_TO_MEDIUM_DURATION_MS); } else if (newSize == AuthDialog.SIZE_MEDIUM) { - mPanelController.updateForContentDimensions(mMediumWidth, mMediumHeight, + mPanelController.updateForContentDimensions(mLayoutParams.mMediumWidth, + mLayoutParams.mMediumHeight, 0 /* animateDurationMs */); mSize = newSize; } else if (newSize == AuthDialog.SIZE_LARGE) { @@ -566,6 +572,7 @@ public abstract class AuthBiometricView extends LinearLayout { mIndicatorView.setText(message); mIndicatorView.setTextColor(mTextColorError); mIndicatorView.setVisibility(View.VISIBLE); + mIndicatorView.setSelected(true); mHandler.postDelayed(resetMessageRunnable, BiometricPrompt.HIDE_DIALOG_DELAY); Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); @@ -586,6 +593,7 @@ public abstract class AuthBiometricView extends LinearLayout { mSubtitleView = mInjector.getSubtitleView(); mDescriptionView = mInjector.getDescriptionView(); mIconView = mInjector.getIconView(); + mIconHolderView = mInjector.getIconHolderView(); mIndicatorView = mInjector.getIndicatorView(); mNegativeButton = mInjector.getNegativeButton(); mPositiveButton = mInjector.getPositiveButton(); @@ -688,29 +696,45 @@ public abstract class AuthBiometricView extends LinearLayout { mHandler.removeCallbacksAndMessages(null /* all */); } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int width = MeasureSpec.getSize(widthMeasureSpec); - final int height = MeasureSpec.getSize(heightMeasureSpec); - final int newWidth = Math.min(width, height); - + /** + * Contains all of the testable logic that should be invoked when {@link #onMeasure(int, int)} + * is invoked. In addition, this allows subclasses to implement custom measuring logic while + * allowing the base class to have common code to apply the custom measurements. + * + * @param width Width to constrain the measurements to. + * @param height Height to constrain the measurements to. + * @return See {@link AuthDialog.LayoutParams} + */ + AuthDialog.LayoutParams onMeasureInternal(int width, int height) { int totalHeight = 0; final int numChildren = getChildCount(); for (int i = 0; i < numChildren; i++) { final View child = getChildAt(i); - if (child.getId() == R.id.biometric_icon) { + if (child.getId() == R.id.space_above_icon) { child.measure( - MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, + MeasureSpec.EXACTLY)); + } else if (child.getId() == R.id.biometric_icon_frame) { + final View iconView = findViewById(R.id.biometric_icon); + child.measure( + MeasureSpec.makeMeasureSpec(iconView.getLayoutParams().width, + MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(iconView.getLayoutParams().height, + MeasureSpec.EXACTLY)); + } else if (child.getId() == R.id.biometric_icon) { + child.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); - } else if (child.getId() == R.id.button_bar) { + } else if (child.getId() == R.id.button_bar) { child.measure( - MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, MeasureSpec.EXACTLY)); } else { child.measure( - MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); } @@ -719,11 +743,19 @@ public abstract class AuthBiometricView extends LinearLayout { } } - // Use the new width so it's centered horizontally - setMeasuredDimension(newWidth, totalHeight); + return new AuthDialog.LayoutParams(width, totalHeight); + } - mMediumHeight = totalHeight; - mMediumWidth = getMeasuredWidth(); + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int width = MeasureSpec.getSize(widthMeasureSpec); + final int height = MeasureSpec.getSize(heightMeasureSpec); + final int newWidth = Math.min(width, height); + + // Use "newWidth" instead, so the landscape dialog width is the same as the portrait + // width. + mLayoutParams = onMeasureInternal(newWidth, height); + setMeasuredDimension(mLayoutParams.mMediumWidth, mLayoutParams.mMediumHeight); } @Override @@ -741,7 +773,7 @@ public abstract class AuthBiometricView extends LinearLayout { // 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(); + mIconOriginalY = mIconHolderView.getY(); if (mSavedState == null) { updateSize(!mRequireConfirmation && supportsSmallDialog() ? AuthDialog.SIZE_SMALL : AuthDialog.SIZE_MEDIUM); @@ -764,4 +796,8 @@ public abstract class AuthBiometricView extends LinearLayout { private boolean isDeviceCredentialAllowed() { return Utils.isDeviceCredentialAllowed(mPromptInfo); } + + @AuthDialog.DialogSize int getSize() { + return mSize; + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 24ab6355c2bd4..07e1f1b7f4c34 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -21,11 +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.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.PromptInfo; import android.hardware.face.FaceSensorPropertiesInternal; -import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Binder; import android.os.Bundle; @@ -286,8 +284,23 @@ public class AuthContainerView extends LinearLayout if (config.mSensorIds.length == 1) { final int singleSensorAuthId = config.mSensorIds[0]; if (Utils.containsSensorId(mFpProps, singleSensorAuthId)) { - mBiometricView = (AuthBiometricFingerprintView) - factory.inflate(R.layout.auth_biometric_fingerprint_view, null, false); + FingerprintSensorPropertiesInternal sensorProps = null; + for (FingerprintSensorPropertiesInternal prop : mFpProps) { + if (prop.sensorId == singleSensorAuthId) { + sensorProps = prop; + break; + } + } + + if (sensorProps.isAnyUdfpsType()) { + AuthBiometricUdfpsView udfpsView = (AuthBiometricUdfpsView) factory + .inflate(R.layout.auth_biometric_udfps_view, null, false); + udfpsView.setSensorProps(sensorProps); + mBiometricView = udfpsView; + } else { + mBiometricView = (AuthBiometricFingerprintView) factory + .inflate(R.layout.auth_biometric_fingerprint_view, null, false); + } } else if (Utils.containsSensorId(mFaceProps, singleSensorAuthId)) { mBiometricView = (AuthBiometricFaceView) factory.inflate(R.layout.auth_biometric_face_view, null, false); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java index ca95f9d736fcf..0f3643c8c359e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java @@ -58,6 +58,20 @@ public interface AuthDialog { @IntDef({SIZE_UNKNOWN, SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE}) @interface DialogSize {} + /** + * Parameters used when laying out {@link AuthBiometricView}, its sublclasses, and + * {@link AuthPanelController}. + */ + class LayoutParams { + final int mMediumHeight; + final int mMediumWidth; + + LayoutParams(int mediumWidth, int mediumHeight) { + mMediumWidth = mediumWidth; + mMediumHeight = mediumHeight; + } + } + /** * Animation duration, from small to medium dialog, including back panel, icon translation, etc */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java index 91dbd6011e225..e2517f27c0ea0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java @@ -22,11 +22,16 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; import android.hardware.biometrics.PromptInfo; +import android.hardware.biometrics.SensorProperties; +import android.hardware.fingerprint.FingerprintSensorProperties; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -62,6 +67,7 @@ public class AuthBiometricViewTest extends SysuiTestCase { @Mock private TextView mDescriptionView; @Mock private TextView mIndicatorView; @Mock private ImageView mIconView; + @Mock private View mIconHolderView; private TestableBiometricView mBiometricView; @@ -200,6 +206,7 @@ public class AuthBiometricViewTest extends SysuiTestCase { @Test public void testBackgroundClicked_whenSmallDialog_neverSendsUserCanceled() { initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector()); + mBiometricView.mLayoutParams = new AuthDialog.LayoutParams(0, 0); mBiometricView.updateSize(AuthDialog.SIZE_SMALL); View view = new View(mContext); @@ -275,7 +282,7 @@ public class AuthBiometricViewTest extends SysuiTestCase { } @Test - public void testNegativeButton_whenDeviceCredentialAllowed() throws InterruptedException { + public void testNegativeButton_whenDeviceCredentialAllowed() { Button negativeButton = new Button(mContext); initDialog(mContext, true /* allowDeviceCredential */, mCallback, new MockInjector() { @Override @@ -290,6 +297,32 @@ public class AuthBiometricViewTest extends SysuiTestCase { verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL); } + @Test + public void testUdfpsBottomSpacerCalculation() { + final int displayHeightPx = 3000; + final int navbarHeightPx = 10; + final int dialogBottomMarginPx = 20; + + final View buttonBar = mock(View.class); + when(buttonBar.getMeasuredHeight()).thenReturn(100); + + final View textIndicator = mock(View.class); + when(textIndicator.getMeasuredHeight()).thenReturn(200); + + final int sensorLocationX = 540; + final int sensorLocationY = 1600; + final int sensorRadius = 100; + final FingerprintSensorPropertiesInternal props = new FingerprintSensorPropertiesInternal( + 0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, + FingerprintSensorProperties.TYPE_UDFPS_OPTICAL, + true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY, + sensorRadius); + + assertEquals(970, AuthBiometricUdfpsView.calculateBottomSpacerHeight( + displayHeightPx, navbarHeightPx, dialogBottomMarginPx, buttonBar, textIndicator, + props)); + } + private PromptInfo buildPromptInfo(boolean allowDeviceCredential) { PromptInfo promptInfo = new PromptInfo(); promptInfo.setTitle("Title"); @@ -362,6 +395,11 @@ public class AuthBiometricViewTest extends SysuiTestCase { return mIconView; } + @Override + public View getIconHolderView() { + return mIconHolderView; + } + @Override public int getDelayAfterError() { return 0; // Keep this at 0 for tests to invoke callback immediately.