From e4ce54dfa68caa96f53f5b44b045829aa8c90d1a Mon Sep 17 00:00:00 2001 From: Austin Delgado Date: Thu, 3 Nov 2022 14:43:20 -0700 Subject: [PATCH] Extend UdfpsView to bottom half of screen Extends UdfpsView on keyguard, enrollment, and biometric prompt to bottom half of screen. Enable with: adb shell cmd statusbar flag 2200 on Test: atest SystemUITests:com.android.systemui.biometrics Bug: 257118043 Change-Id: I87fa7feb74e6235793fcf01e961aad60d12e172a --- .../biometrics/UdfpsAnimationView.java | 21 ++++++++++ .../systemui/biometrics/UdfpsController.java | 27 ++++++++++--- .../biometrics/UdfpsControllerOverlay.kt | 38 +++++++++++++++---- .../systemui/biometrics/UdfpsEnrollView.java | 30 +++++++++++++++ .../biometrics/UdfpsEnrollViewController.java | 8 ++++ .../biometrics/UdfpsKeyguardView.java | 27 ++++++++++++- .../biometrics/UdfpsKeyguardViewController.kt | 11 +++++- .../android/systemui/biometrics/UdfpsView.kt | 26 +++++++++---- .../src/com/android/systemui/flags/Flags.kt | 6 +++ .../UdfpsKeyguardViewControllerBaseTest.java | 19 ++++++++-- .../UdfpsKeyguardViewControllerTest.java | 38 ++++++++++++++++++- ...eyguardViewControllerWithCoroutinesTest.kt | 5 ++- 12 files changed, 228 insertions(+), 28 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index ad966125b9e8e..bdad41348c958 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -37,6 +37,9 @@ public abstract class UdfpsAnimationView extends FrameLayout { private float mDialogSuggestedAlpha = 1f; private float mNotificationShadeExpansion = 0f; + // Used for Udfps ellipse detection when flag is true, set by AnimationViewController + boolean mUseExpandedOverlay = false; + // mAlpha takes into consideration the status bar expansion amount and dialog suggested alpha private int mAlpha; boolean mPauseAuth; @@ -117,6 +120,24 @@ public abstract class UdfpsAnimationView extends FrameLayout { return (int) ((1 - percent) * 255); } + /** + * Converts coordinates of RectF relative to the screen to coordinates relative to this view. + * + * @param bounds RectF based off screen coordinates in current orientation + */ + RectF getBoundsRelativeToView(RectF bounds) { + int[] pos = getLocationOnScreen(); + + RectF output = new RectF( + bounds.left - pos[0], + bounds.top - pos[1], + bounds.right - pos[0], + bounds.bottom - pos[1] + ); + + return output; + } + /** * Set the suggested alpha based on whether a dialog was recently shown or hidden. * @param dialogSuggestedAlpha value from 0f to 1f. diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index bc10868e36c8e..d7908db0c8b95 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -66,6 +66,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.plugins.FalsingManager; @@ -459,8 +460,17 @@ public class UdfpsController implements DozeReceiver, Dumpable { mVelocityTracker.clear(); } - boolean withinSensorArea = - isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView); + boolean withinSensorArea; + if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { + withinSensorArea = + isWithinSensorArea(udfpsView, event.getRawX(), + event.getRawY(), fromUdfpsView); + } else { + withinSensorArea = + isWithinSensorArea(udfpsView, event.getX(), + event.getY(), fromUdfpsView); + } + if (withinSensorArea) { Trace.beginAsyncSection("UdfpsController.e2e.onPointerDown", 0); Log.v(TAG, "onTouch | action down"); @@ -491,9 +501,16 @@ public class UdfpsController implements DozeReceiver, Dumpable { ? event.getPointerId(0) : event.findPointerIndex(mActivePointerId); if (idx == event.getActionIndex()) { - boolean actionMoveWithinSensorArea = - isWithinSensorArea(udfpsView, event.getX(idx), event.getY(idx), - fromUdfpsView); + boolean actionMoveWithinSensorArea; + if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { + actionMoveWithinSensorArea = + isWithinSensorArea(udfpsView, event.getRawX(idx), + event.getRawY(idx), fromUdfpsView); + } else { + actionMoveWithinSensorArea = + isWithinSensorArea(udfpsView, event.getX(idx), + event.getY(idx), fromUdfpsView); + } if ((fromUdfpsView || actionMoveWithinSensorArea) && shouldTryToDismissKeyguard()) { Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 0bb24f8663ec9..8db4927ee0597 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -49,6 +49,7 @@ import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionStateManager @@ -103,6 +104,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( private set private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams() + private var sensorBounds: Rect = Rect() private var overlayTouchListener: TouchExplorationStateChangeListener? = null @@ -120,6 +122,10 @@ class UdfpsControllerOverlay @JvmOverloads constructor( privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY // Avoid announcing window title. accessibilityTitle = " " + + if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { + inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY + } } /** A helper if the [requestReason] was due to enrollment. */ @@ -160,6 +166,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( fun show(controller: UdfpsController, params: UdfpsOverlayParams): Boolean { if (overlayView == null) { overlayParams = params + sensorBounds = Rect(params.sensorBounds) try { overlayView = (inflater.inflate( R.layout.udfps_view, null, false @@ -178,6 +185,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( } windowManager.addView(this, coreLayoutParams.updateDimensions(animation)) + sensorRect = sensorBounds touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled overlayTouchListener = TouchExplorationStateChangeListener { if (accessibilityManager.isTouchExplorationEnabled) { @@ -194,6 +202,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( overlayTouchListener!! ) overlayTouchListener?.onTouchExplorationStateChanged(true) + useExpandedOverlay = featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) } } catch (e: RuntimeException) { Log.e(TAG, "showUdfpsOverlay | failed to add window", e) @@ -225,13 +234,14 @@ class UdfpsControllerOverlay @JvmOverloads constructor( REASON_ENROLL_ENROLLING -> { UdfpsEnrollViewController( view.addUdfpsView(R.layout.udfps_enroll_view) { - updateSensorLocation(overlayParams.sensorBounds) + updateSensorLocation(sensorBounds) }, enrollHelper ?: throw IllegalStateException("no enrollment helper"), statusBarStateController, shadeExpansionStateManager, dialogManager, dumpManager, + featureFlags, overlayParams.scaleFactor ) } @@ -420,7 +430,12 @@ class UdfpsControllerOverlay @JvmOverloads constructor( } // Original sensorBounds assume portrait mode. - val rotatedSensorBounds = Rect(overlayParams.sensorBounds) + var rotatedBounds = + if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { + Rect(overlayParams.overlayBounds) + } else { + Rect(overlayParams.sensorBounds) + } val rot = overlayParams.rotation if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) { @@ -434,18 +449,27 @@ class UdfpsControllerOverlay @JvmOverloads constructor( } else { Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot)) RotationUtils.rotateBounds( - rotatedSensorBounds, + rotatedBounds, overlayParams.naturalDisplayWidth, overlayParams.naturalDisplayHeight, rot ) + + if (featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { + RotationUtils.rotateBounds( + sensorBounds, + overlayParams.naturalDisplayWidth, + overlayParams.naturalDisplayHeight, + rot + ) + } } } - x = rotatedSensorBounds.left - paddingX - y = rotatedSensorBounds.top - paddingY - height = rotatedSensorBounds.height() + 2 * paddingX - width = rotatedSensorBounds.width() + 2 * paddingY + x = rotatedBounds.left - paddingX + y = rotatedBounds.top - paddingY + height = rotatedBounds.height() + 2 * paddingX + width = rotatedBounds.width() + 2 * paddingY return this } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java index e5c485547382b..1cc4141508352 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java @@ -18,6 +18,7 @@ package com.android.systemui.biometrics; import android.content.Context; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Handler; import android.os.Looper; import android.util.AttributeSet; @@ -41,6 +42,9 @@ public class UdfpsEnrollView extends UdfpsAnimationView { @NonNull private ImageView mFingerprintView; @NonNull private ImageView mFingerprintProgressView; + private LayoutParams mProgressParams; + private float mProgressBarRadius; + public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mFingerprintDrawable = new UdfpsEnrollDrawable(mContext, attrs); @@ -56,6 +60,32 @@ public class UdfpsEnrollView extends UdfpsAnimationView { mFingerprintProgressView.setImageDrawable(mFingerprintProgressDrawable); } + @Override + void onSensorRectUpdated(RectF bounds) { + if (mUseExpandedOverlay) { + RectF converted = getBoundsRelativeToView(bounds); + + mProgressParams = new LayoutParams( + (int) (converted.width() + mProgressBarRadius * 2), + (int) (converted.height() + mProgressBarRadius * 2)); + mProgressParams.setMargins( + (int) (converted.left - mProgressBarRadius), + (int) (converted.top - mProgressBarRadius), + (int) (converted.right + mProgressBarRadius), + (int) (converted.bottom + mProgressBarRadius) + ); + + mFingerprintProgressView.setLayoutParams(mProgressParams); + super.onSensorRectUpdated(converted); + } else { + super.onSensorRectUpdated(bounds); + } + } + + void setProgressBarRadius(float radius) { + mProgressBarRadius = radius; + } + @Override public UdfpsDrawable getDrawable() { return mFingerprintDrawable; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java index e01273f2a0929..40176654a8ec9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java @@ -21,6 +21,8 @@ import android.graphics.PointF; import com.android.systemui.R; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.phone.SystemUIDialogManager; @@ -57,6 +59,7 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController + mKeyguardViewManagerCallbackArgumentCaptor; + protected StatusBarKeyguardViewManager.KeyguardViewManagerCallback mKeyguardViewManagerCallback; + + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -143,15 +148,22 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase { } public UdfpsKeyguardViewController createUdfpsKeyguardViewController() { - return createUdfpsKeyguardViewController(false); + return createUdfpsKeyguardViewController(false, false); + } + + public void captureKeyGuardViewManagerCallback() { + verify(mStatusBarKeyguardViewManager).addCallback( + mKeyguardViewManagerCallbackArgumentCaptor.capture()); + mKeyguardViewManagerCallback = mKeyguardViewManagerCallbackArgumentCaptor.getValue(); } protected UdfpsKeyguardViewController createUdfpsKeyguardViewController( - boolean useModernBouncer) { + boolean useModernBouncer, boolean useExpandedOverlay) { mFeatureFlags.set(Flags.MODERN_BOUNCER, useModernBouncer); + mFeatureFlags.set(Flags.UDFPS_NEW_TOUCH_DETECTION, useExpandedOverlay); when(mStatusBarKeyguardViewManager.getPrimaryBouncer()).thenReturn( useModernBouncer ? null : mBouncer); - return new UdfpsKeyguardViewController( + UdfpsKeyguardViewController controller = new UdfpsKeyguardViewController( mView, mStatusBarStateController, mShadeExpansionStateManager, @@ -168,5 +180,6 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase { mActivityLaunchAnimator, mFeatureFlags, mPrimaryBouncerInteractor); + return controller; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index 16728b6f2ab91..babe5334e3ebe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -18,6 +18,7 @@ package com.android.systemui.biometrics; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeast; @@ -30,6 +31,7 @@ import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; +import android.view.MotionEvent; import androidx.test.filters.SmallTest; @@ -52,7 +54,8 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController @Override public UdfpsKeyguardViewController createUdfpsKeyguardViewController() { - return createUdfpsKeyguardViewController(/* useModernBouncer */ false); + return createUdfpsKeyguardViewController(/* useModernBouncer */ false, + /* useExpandedOverlay */ false); } @Test @@ -422,4 +425,37 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController verify(mBouncer).addBouncerExpansionCallback(mBouncerExpansionCallbackCaptor.capture()); mBouncerExpansionCallback = mBouncerExpansionCallbackCaptor.getValue(); } + + @Test + // TODO(b/259264861): Tracking Bug + public void testUdfpsExpandedOverlayOn() { + // GIVEN view is attached and useExpandedOverlay is true + mController = createUdfpsKeyguardViewController(false, true); + mController.onViewAttached(); + captureKeyGuardViewManagerCallback(); + + // WHEN a touch is received + mKeyguardViewManagerCallback.onTouch( + MotionEvent.obtain(0, 0, 0, 0, 0, 0)); + + // THEN udfpsController onTouch is not called + assertTrue(mView.mUseExpandedOverlay); + verify(mUdfpsController, never()).onTouch(any()); + } + + @Test + // TODO(b/259264861): Tracking Bug + public void testUdfpsExpandedOverlayOff() { + // GIVEN view is attached and useExpandedOverlay is false + mController.onViewAttached(); + captureKeyGuardViewManagerCallback(); + + // WHEN a touch is received + mKeyguardViewManagerCallback.onTouch( + MotionEvent.obtain(0, 0, 0, 0, 0, 0)); + + // THEN udfpsController onTouch is called + assertFalse(mView.mUseExpandedOverlay); + verify(mUdfpsController).onTouch(any()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt index 68e744e53843e..517e27a3ce2f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt @@ -72,7 +72,10 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle mock(KeyguardBypassController::class.java), mKeyguardUpdateMonitor ) - return createUdfpsKeyguardViewController(/* useModernBouncer */ true) + return createUdfpsKeyguardViewController( + /* useModernBouncer */ true, /* useExpandedOverlay */ + false + ) } /** After migration, replaces LockIconViewControllerTest version */