From e4ce54dfa68caa96f53f5b44b045829aa8c90d1a Mon Sep 17 00:00:00 2001 From: Austin Delgado Date: Thu, 3 Nov 2022 14:43:20 -0700 Subject: [PATCH 1/2] 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 */ From c441a9a059a4e8b8a72cd2af7923b3e9e7ec859e Mon Sep 17 00:00:00 2001 From: Austin Delgado Date: Fri, 4 Nov 2022 13:22:57 -0700 Subject: [PATCH 2/2] Add ellipse detection to UdfpsController Enable with: adb shell cmd statusbar flag 2200 on (ExpandedOverlay) and adb shell cmd statusbar flag 2202 on (EllipseDetection) Bug: 257118043 Test: atest SystemUITests:com.android.systemui.biometrics Change-Id: I143f7e5df71d3668a79cb898f618a749eef6f263 --- .../systemui/biometrics/UdfpsController.java | 40 ++++++-- .../biometrics/UdfpsEllipseDetection.kt | 92 +++++++++++++++++++ .../biometrics/UdfpsKeyguardViewController.kt | 1 + 3 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index d7908db0c8b95..45595c80d36d3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -150,7 +150,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this. @Nullable private Runnable mAuthControllerUpdateUdfpsLocation; @Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider; - @Nullable private UdfpsDisplayMode mUdfpsDisplayMode; + @Nullable private UdfpsDisplayModeProvider mUdfpsDisplayMode; // Tracks the velocity of a touch to help filter out the touches that move too fast. @Nullable private VelocityTracker mVelocityTracker; @@ -165,6 +165,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { // The current request from FingerprintService. Null if no current request. @Nullable UdfpsControllerOverlay mOverlay; + @Nullable private UdfpsEllipseDetection mUdfpsEllipseDetection; // The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when // to turn off high brightness mode. To get around this limitation, the state of the AOD @@ -321,6 +322,10 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!mOverlayParams.equals(overlayParams)) { mOverlayParams = overlayParams; + if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) { + mUdfpsEllipseDetection.updateOverlayParams(overlayParams); + } + final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateBouncer(); // When the bounds change it's always necessary to re-create the overlay's window with @@ -462,13 +467,19 @@ public class UdfpsController implements DozeReceiver, Dumpable { boolean withinSensorArea; if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { - withinSensorArea = + if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) { + // Ellipse detection + withinSensorArea = mUdfpsEllipseDetection.isGoodEllipseOverlap(event); + } else { + // Centroid with expanded overlay + withinSensorArea = isWithinSensorArea(udfpsView, event.getRawX(), - event.getRawY(), fromUdfpsView); + event.getRawY(), fromUdfpsView); + } } else { + // Centroid with sensor sized view withinSensorArea = - isWithinSensorArea(udfpsView, event.getX(), - event.getY(), fromUdfpsView); + isWithinSensorArea(udfpsView, event.getX(), event.getY(), fromUdfpsView); } if (withinSensorArea) { @@ -503,14 +514,23 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (idx == event.getActionIndex()) { boolean actionMoveWithinSensorArea; if (mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)) { - actionMoveWithinSensorArea = + if (mFeatureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) { + // Ellipse detection + actionMoveWithinSensorArea = + mUdfpsEllipseDetection.isGoodEllipseOverlap(event); + } else { + // Centroid with expanded overlay + actionMoveWithinSensorArea = isWithinSensorArea(udfpsView, event.getRawX(idx), event.getRawY(idx), fromUdfpsView); + } } else { + // Centroid with sensor sized view actionMoveWithinSensorArea = - isWithinSensorArea(udfpsView, event.getX(idx), - event.getY(idx), fromUdfpsView); + isWithinSensorArea(udfpsView, event.getX(idx), + event.getY(idx), fromUdfpsView); } + if ((fromUdfpsView || actionMoveWithinSensorArea) && shouldTryToDismissKeyguard()) { Log.v(TAG, "onTouch | dismiss keyguard ACTION_MOVE"); @@ -708,6 +728,10 @@ public class UdfpsController implements DozeReceiver, Dumpable { udfpsHapticsSimulator.setUdfpsController(this); udfpsShell.setUdfpsOverlayController(mUdfpsOverlayController); + + if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) { + mUdfpsEllipseDetection = new UdfpsEllipseDetection(mOverlayParams); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt new file mode 100644 index 0000000000000..8ae4775467df7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEllipseDetection.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2022 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.graphics.Point +import android.graphics.Rect +import android.util.RotationUtils +import android.view.MotionEvent +import kotlin.math.cos +import kotlin.math.pow +import kotlin.math.sin + +private const val TAG = "UdfpsEllipseDetection" + +private const val NEEDED_POINTS = 2 + +class UdfpsEllipseDetection(overlayParams: UdfpsOverlayParams) { + var sensorRect = Rect() + var points: Array = emptyArray() + + init { + sensorRect = Rect(overlayParams.sensorBounds) + + points = calculateSensorPoints(sensorRect) + } + + fun updateOverlayParams(params: UdfpsOverlayParams) { + sensorRect = Rect(params.sensorBounds) + + val rot = params.rotation + RotationUtils.rotateBounds( + sensorRect, + params.naturalDisplayWidth, + params.naturalDisplayHeight, + rot + ) + + points = calculateSensorPoints(sensorRect) + } + + fun isGoodEllipseOverlap(event: MotionEvent): Boolean { + return points.count { checkPoint(event, it) } >= NEEDED_POINTS + } + + private fun checkPoint(event: MotionEvent, point: Point): Boolean { + // Calculate if sensor point is within ellipse + // Formula: ((cos(o)(xE - xS) + sin(o)(yE - yS))^2 / a^2) + ((sin(o)(xE - xS) + cos(o)(yE - + // yS))^2 / b^2) <= 1 + val a: Float = cos(event.orientation) * (point.x - event.rawX) + val b: Float = sin(event.orientation) * (point.y - event.rawY) + val c: Float = sin(event.orientation) * (point.x - event.rawX) + val d: Float = cos(event.orientation) * (point.y - event.rawY) + val result = + (a + b).pow(2) / (event.touchMinor / 2).pow(2) + + (c - d).pow(2) / (event.touchMajor / 2).pow(2) + + return result <= 1 + } +} + +fun calculateSensorPoints(sensorRect: Rect): Array { + val sensorX = sensorRect.centerX() + val sensorY = sensorRect.centerY() + val cornerOffset: Int = sensorRect.width() / 4 + val sideOffset: Int = sensorRect.width() / 3 + + return arrayOf( + Point(sensorX - cornerOffset, sensorY - cornerOffset), + Point(sensorX, sensorY - sideOffset), + Point(sensorX + cornerOffset, sensorY - cornerOffset), + Point(sensorX - sideOffset, sensorY), + Point(sensorX, sensorY), + Point(sensorX + sideOffset, sensorY), + Point(sensorX - cornerOffset, sensorY + cornerOffset), + Point(sensorX, sensorY + sideOffset), + Point(sensorX + cornerOffset, sensorY + cornerOffset) + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt index a20b8aa417d02..63144fcea7610 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt @@ -235,6 +235,7 @@ constructor( return } + // Forwarding touches not needed with expanded overlay if (useExpandedOverlay) { return } else {