diff options
3 files changed, 149 insertions, 24 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlay.kt index 6e78f3d3d6aa..57652dc44aa6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlay.kt @@ -19,6 +19,7 @@ package com.android.systemui.biometrics import android.annotation.SuppressLint import android.content.Context import android.graphics.PixelFormat +import android.graphics.Point import android.graphics.Rect import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintSensorPropertiesInternal @@ -31,14 +32,22 @@ import android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.Execution import java.util.* import java.util.concurrent.Executor import javax.inject.Inject +import kotlin.math.cos +import kotlin.math.pow +import kotlin.math.sin private const val TAG = "UdfpsOverlay" +// Number of sensor points needed inside ellipse for good overlap +private const val NEEDED_POINTS = 3 + @SuppressLint("ClickableViewAccessibility") @SysUISingleton class UdfpsOverlay @@ -54,7 +63,8 @@ constructor( private val fgExecutor: DelayableExecutor, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val authController: AuthController, - private val udfpsLogger: UdfpsLogger + private val udfpsLogger: UdfpsLogger, + private var featureFlags: FeatureFlags ) : CoreStartable { /** The view, when [isShowing], or null. */ @@ -65,6 +75,8 @@ constructor( private var onFingerDown = false val size = windowManager.maximumWindowMetrics.bounds val udfpsProps: MutableList<FingerprintSensorPropertiesInternal> = mutableListOf() + var points: Array<Point> = emptyArray() + var processedMotionEvent = false private var params: UdfpsOverlayParams = UdfpsOverlayParams() @@ -95,33 +107,47 @@ constructor( MotionEvent.ACTION_MOVE -> { onFingerDown = true if (!view.isDisplayConfigured && alternateTouchProvider.isPresent) { - biometricExecutor.execute { - alternateTouchProvider - .get() - .onPointerDown( - requestId, - event.x.toInt(), - event.y.toInt(), - event.touchMinor, - event.touchMajor - ) - } - fgExecutor.execute { - if (keyguardUpdateMonitor.isFingerprintDetectionRunning) { - keyguardUpdateMonitor.onUdfpsPointerDown(requestId.toInt()) + view.processMotionEvent(event) + + val goodOverlap = + if (featureFlags.isEnabled(Flags.NEW_ELLIPSE_DETECTION)) { + isGoodEllipseOverlap(event) + } else { + isGoodCentroidOverlap(event) + } + + if (!processedMotionEvent && goodOverlap) { + biometricExecutor.execute { + alternateTouchProvider + .get() + .onPointerDown( + requestId, + event.rawX.toInt(), + event.rawY.toInt(), + event.touchMinor, + event.touchMajor + ) + } + fgExecutor.execute { + if (keyguardUpdateMonitor.isFingerprintDetectionRunning) { + keyguardUpdateMonitor.onUdfpsPointerDown(requestId.toInt()) + } + } + + view.configureDisplay { + biometricExecutor.execute { alternateTouchProvider.get().onUiReady() } } - } - view.configureDisplay { - biometricExecutor.execute { alternateTouchProvider.get().onUiReady() } + processedMotionEvent = true } } + view.invalidate() true } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { - if (onFingerDown && alternateTouchProvider.isPresent) { + if (processedMotionEvent && alternateTouchProvider.isPresent) { biometricExecutor.execute { alternateTouchProvider.get().onPointerUp(requestId) } @@ -130,18 +156,44 @@ constructor( keyguardUpdateMonitor.onUdfpsPointerUp(requestId.toInt()) } } + + processedMotionEvent = false } - onFingerDown = false + if (view.isDisplayConfigured) { view.unconfigureDisplay() } + view.invalidate() true } else -> false } } + fun isGoodEllipseOverlap(event: MotionEvent): Boolean { + return points.count { checkPoint(event, it) } >= NEEDED_POINTS + } + + fun isGoodCentroidOverlap(event: MotionEvent): Boolean { + return params.sensorBounds.contains(event.rawX.toInt(), event.rawY.toInt()) + } + + 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(0)(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 show(requestId: Long): Boolean { this.requestId = requestId if (overlayView == null && alternateTouchProvider.isPresent) { @@ -151,6 +203,7 @@ constructor( UdfpsDisplayMode(context, execution, authController, udfpsLogger) ) it.setOnTouchListener { v, event -> onTouch(v, event) } + it.sensorPoints = points overlayView = it } windowManager.addView(overlayView, coreLayoutParams) @@ -201,6 +254,23 @@ constructor( naturalDisplayHeight = size.height(), scaleFactor = 1f ) + + val sensorX = params.sensorBounds.centerX() + val sensorY = params.sensorBounds.centerY() + val gridOffset: Int = params.sensorBounds.width() / 3 + + points = + arrayOf( + Point(sensorX - gridOffset, sensorY - gridOffset), + Point(sensorX, sensorY - gridOffset), + Point(sensorX + gridOffset, sensorY - gridOffset), + Point(sensorX - gridOffset, sensorY), + Point(sensorX, sensorY), + Point(sensorX + gridOffset, sensorY), + Point(sensorX - gridOffset, sensorY + gridOffset), + Point(sensorX, sensorY + gridOffset), + Point(sensorX + gridOffset, sensorY + gridOffset) + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayView.kt index d37133239531..25e08d705736 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayView.kt @@ -20,26 +20,39 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint +import android.graphics.Point import android.graphics.RectF import android.util.AttributeSet +import android.view.MotionEvent import android.widget.FrameLayout private const val TAG = "UdfpsOverlayView" +private const val POINT_SIZE = 10f class UdfpsOverlayView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) { - - private val sensorRect = RectF() var overlayParams = UdfpsOverlayParams() private var mUdfpsDisplayMode: UdfpsDisplayMode? = null var overlayPaint = Paint() var sensorPaint = Paint() + var touchPaint = Paint() + var pointPaint = Paint() val centerPaint = Paint() + var oval = RectF() + /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */ var isDisplayConfigured: Boolean = false private set + var touchX: Float = 0f + var touchY: Float = 0f + var touchMinor: Float = 0f + var touchMajor: Float = 0f + var touchOrientation: Double = 0.0 + + var sensorPoints: Array<Point>? = null + init { this.setWillNotDraw(false) } @@ -47,16 +60,23 @@ class UdfpsOverlayView(context: Context, attrs: AttributeSet?) : FrameLayout(con override fun onAttachedToWindow() { super.onAttachedToWindow() - overlayPaint.color = Color.argb(120, 255, 0, 0) + overlayPaint.color = Color.argb(100, 255, 0, 0) overlayPaint.style = Paint.Style.FILL + touchPaint.color = Color.argb(200, 255, 255, 255) + touchPaint.style = Paint.Style.FILL + sensorPaint.color = Color.argb(150, 134, 204, 255) sensorPaint.style = Paint.Style.FILL + + pointPaint.color = Color.WHITE + pointPaint.style = Paint.Style.FILL } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) + // Draw overlay and sensor canvas.drawRect(overlayParams.overlayBounds, overlayPaint) canvas.drawRect(overlayParams.sensorBounds, sensorPaint) canvas.drawCircle( @@ -65,6 +85,29 @@ class UdfpsOverlayView(context: Context, attrs: AttributeSet?) : FrameLayout(con overlayParams.sensorBounds.width().toFloat() / 2, centerPaint ) + + // Draw Points + sensorPoints?.forEach { + canvas.drawCircle(it.x.toFloat(), it.y.toFloat(), POINT_SIZE, pointPaint) + } + + // Draw touch oval + canvas.save() + canvas.rotate(Math.toDegrees(touchOrientation).toFloat(), touchX, touchY) + + oval.setEmpty() + oval.set( + touchX - touchMinor / 2, + touchY + touchMajor / 2, + touchX + touchMinor / 2, + touchY - touchMajor / 2 + ) + + canvas.drawOval(oval, touchPaint) + + // Draw center point + canvas.drawCircle(touchX, touchY, POINT_SIZE, centerPaint) + canvas.restore() } fun setUdfpsDisplayMode(udfpsDisplayMode: UdfpsDisplayMode?) { @@ -80,4 +123,12 @@ class UdfpsOverlayView(context: Context, attrs: AttributeSet?) : FrameLayout(con isDisplayConfigured = false mUdfpsDisplayMode?.disable(null /* onDisabled */) } + + fun processMotionEvent(event: MotionEvent) { + touchX = event.rawX + touchY = event.rawY + touchMinor = event.touchMinor + touchMajor = event.touchMajor + touchOrientation = event.orientation.toDouble() + } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 4818bccb1f64..80d84446f73b 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -124,6 +124,10 @@ object Flags { */ @JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213, teamfood = true) + @JvmField val NEW_ELLIPSE_DETECTION = UnreleasedFlag(214) + + @JvmField val NEW_UDFPS_OVERLAY = UnreleasedFlag(215) + // 300 - power menu // TODO(b/254512600): Tracking Bug @JvmField val POWER_MENU_LITE = ReleasedFlag(300) |