From 4ed191973e9a4878301630fd03bb60446b19d870 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 20 Apr 2023 15:26:27 +0800 Subject: [PATCH] Make auth dialog in large size expand into cutout area 1. Set cutout mode always and ignore systemBars insets in AuthContainerView to extend its content area into cutout and system bars areas. 2. In AuthBiometricView: - Account for navigation bar insets to calculate the actual dialog width/height. - Set the paddings for dialog with size other than Large according to the navigation bar insets. 3. Exclude the navigation bar insets when calculating auth panel outline for non fullscreen dialog. Make the UI more consistance in the status bar & cutout region. Bug: 256966736 Bug: 206761077 Bug: 159687661 Bug: 269056111 Test: build and manual check BP visual in portrait & landscape Test: atest AuthContainerViewTest CredentialPasswordViewScreenshotTest CredentialPatternViewScreenshotTest Change-Id: I250dff8885465d378074fd919d7b24456ba7a4e0 --- .../biometrics/AuthBiometricView.java | 41 +++++++++++++++++++ .../biometrics/AuthContainerView.java | 8 +++- .../biometrics/AuthPanelController.java | 35 ++++++++++++++-- .../com/android/systemui/biometrics/Utils.kt | 11 +++++ .../biometrics/AuthContainerViewTest.kt | 15 +++++++ 5 files changed, 106 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index e04dd06800607..533ac5e73caae 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -28,6 +28,7 @@ import android.annotation.Nullable; import android.annotation.StringRes; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Insets; import android.hardware.biometrics.BiometricAuthenticator.Modality; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.PromptInfo; @@ -299,9 +300,25 @@ public abstract class AuthBiometricView extends LinearLayout { mJankListener = jankListener; } + private void updatePaddings(int size) { + final Insets navBarInsets = Utils.getNavbarInsets(mContext); + if (size != AuthDialog.SIZE_LARGE) { + if (mPanelController.getPosition() == AuthPanelController.POSITION_LEFT) { + setPadding(navBarInsets.left, 0, 0, 0); + } else if (mPanelController.getPosition() == AuthPanelController.POSITION_RIGHT) { + setPadding(0, 0, navBarInsets.right, 0); + } else { + setPadding(0, 0, 0, navBarInsets.bottom); + } + } else { + setPadding(0, 0, 0, 0); + } + } + @VisibleForTesting final void updateSize(@AuthDialog.DialogSize int newSize) { Log.v(TAG, "Current size: " + mSize + " New size: " + newSize); + updatePaddings(newSize); if (newSize == AuthDialog.SIZE_SMALL) { mTitleView.setVisibility(View.GONE); mSubtitleView.setVisibility(View.GONE); @@ -521,6 +538,11 @@ public abstract class AuthBiometricView extends LinearLayout { mState = newState; } + void onOrientationChanged() { + // Update padding and AuthPanel outline by calling updateSize when the orientation changed. + updateSize(mSize); + } + public void onDialogAnimatedIn() { updateState(STATE_AUTHENTICATING); } @@ -864,6 +886,25 @@ public abstract class AuthBiometricView extends LinearLayout { } mLayoutParams = onMeasureInternal(width, height); + + final Insets navBarInsets = Utils.getNavbarInsets(mContext); + final int navBarHeight = navBarInsets.bottom; + final int navBarWidth; + if (mPanelController.getPosition() == AuthPanelController.POSITION_LEFT) { + navBarWidth = navBarInsets.left; + } else if (mPanelController.getPosition() == AuthPanelController.POSITION_RIGHT) { + navBarWidth = navBarInsets.right; + } else { + navBarWidth = 0; + } + + // The actual auth dialog w/h should include navigation bar size. + if (navBarWidth != 0 || navBarHeight != 0) { + mLayoutParams = new AuthDialog.LayoutParams( + mLayoutParams.mMediumWidth + navBarWidth, + mLayoutParams.mMediumHeight + navBarInsets.bottom); + } + setMeasuredDimension(mLayoutParams.mMediumWidth, mLayoutParams.mMediumHeight); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index aeebb010eb1e7..67beedd2750ce 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -19,6 +19,7 @@ package com.android.systemui.biometrics; import static android.hardware.biometrics.BiometricManager.BIOMETRIC_MULTI_SENSOR_DEFAULT; import static android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode; import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION; @@ -497,6 +498,9 @@ public class AuthContainerView extends LinearLayout @Override public void onOrientationChanged() { maybeUpdatePositionForUdfps(true /* invalidate */); + if (mBiometricView != null) { + mBiometricView.onOrientationChanged(); + } } @Override @@ -884,7 +888,9 @@ public class AuthContainerView extends LinearLayout windowFlags, PixelFormat.TRANSLUCENT); lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; - lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~WindowInsets.Type.ime()); + lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~WindowInsets.Type.ime() + & ~WindowInsets.Type.systemBars()); + lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; lp.setTitle("BiometricPrompt"); lp.accessibilityTitle = title; lp.dimAmount = BACKGROUND_DIM_AMOUNT; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java index 5c616f005d4d9..ad100716eceb7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java @@ -20,6 +20,7 @@ import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.content.Context; +import android.graphics.Insets; import android.graphics.Outline; import android.util.Log; import android.view.View; @@ -64,13 +65,12 @@ public class AuthPanelController extends ViewOutlineProvider { @Override public void getOutline(View view, Outline outline) { final int left = getLeftBound(mPosition); - final int right = left + mContentWidth; + final int right = getRightBound(mPosition, left); // If the content fits in the container, shrink the height to wrap it. Otherwise, expand to // fill the display (minus the margin), since the content is scrollable. final int top = getTopBound(mPosition); - final int bottom = Math.min(top + mContentHeight, mContainerHeight - mMargin); - + final int bottom = getBottomBound(top); outline.setRoundRect(left, top, right, bottom, mCornerRadius); } @@ -79,6 +79,10 @@ public class AuthPanelController extends ViewOutlineProvider { case POSITION_BOTTOM: return (mContainerWidth - mContentWidth) / 2; case POSITION_LEFT: + if (!mUseFullScreen) { + final Insets navBarInsets = Utils.getNavbarInsets(mContext); + return mMargin + navBarInsets.left; + } return mMargin; case POSITION_RIGHT: return mContainerWidth - mContentWidth - mMargin; @@ -88,6 +92,27 @@ public class AuthPanelController extends ViewOutlineProvider { } } + private int getRightBound(@Position int position, int left) { + if (!mUseFullScreen) { + final Insets navBarInsets = Utils.getNavbarInsets(mContext); + if (position == POSITION_RIGHT) { + return left + mContentWidth - navBarInsets.right; + } else if (position == POSITION_LEFT) { + return left + mContentWidth - navBarInsets.left; + } + } + return left + mContentWidth; + } + + private int getBottomBound(int top) { + if (!mUseFullScreen) { + final Insets navBarInsets = Utils.getNavbarInsets(mContext); + return Math.min(top + mContentHeight - navBarInsets.bottom, + mContainerHeight - mMargin - navBarInsets.bottom); + } + return Math.min(top + mContentHeight, mContainerHeight - mMargin); + } + private int getTopBound(@Position int position) { switch (position) { case POSITION_BOTTOM: @@ -113,6 +138,10 @@ public class AuthPanelController extends ViewOutlineProvider { mPosition = position; } + public @Position int getPosition() { + return mPosition; + } + public void setUseFullScreen(boolean fullScreen) { mUseFullScreen = fullScreen; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt index d0d6f4cbf166a..b538085fa40de 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt @@ -26,13 +26,16 @@ import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING import android.content.Context import android.content.pm.PackageManager +import android.graphics.Insets import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.PromptInfo import android.hardware.biometrics.SensorPropertiesInternal import android.os.UserManager import android.util.DisplayMetrics import android.view.ViewGroup +import android.view.WindowInsets import android.view.WindowManager +import android.view.WindowMetrics import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityManager import com.android.internal.widget.LockPatternUtils @@ -114,6 +117,14 @@ object Utils { return hasPermission && "android" == clientPackage } + @JvmStatic + fun getNavbarInsets(context: Context): Insets { + val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java) + val windowMetrics: WindowMetrics? = windowManager?.maximumWindowMetrics + return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars()) + ?: Insets.NONE + } + @Retention(RetentionPolicy.SOURCE) @IntDef(CREDENTIAL_PIN, CREDENTIAL_PATTERN, CREDENTIAL_PASSWORD) internal annotation class CredentialType diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 6d4c467aca7d5..7cf3d0022904f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -483,6 +483,21 @@ class AuthContainerViewTest : SysuiTestCase() { waitForIdleSync() assertThat(isAttachedToWindow()).isTrue() } + + @Test + fun testLayoutParams_hasCutoutModeAlwaysFlag() { + val layoutParams = AuthContainerView.getLayoutParams(windowToken, "") + val lpFlags = layoutParams.flags + + assertThat((lpFlags and WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) + != 0).isTrue() + } + + @Test + fun testLayoutParams_excludesSystemBarInsets() { + val layoutParams = AuthContainerView.getLayoutParams(windowToken, "") + assertThat((layoutParams.fitInsetsTypes and WindowInsets.Type.systemBars()) == 0).isTrue() + } } private fun AuthContainerView.hasBiometricPrompt() =