Loading packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlay.kt +90 −20 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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. */ Loading @@ -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() Loading Loading @@ -95,13 +107,23 @@ constructor( MotionEvent.ACTION_MOVE -> { onFingerDown = true if (!view.isDisplayConfigured && alternateTouchProvider.isPresent) { 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.x.toInt(), event.y.toInt(), event.rawX.toInt(), event.rawY.toInt(), event.touchMinor, event.touchMajor ) Loading @@ -115,13 +137,17 @@ constructor( 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) } Loading @@ -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) { Loading @@ -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) Loading Loading @@ -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) ) } } } packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayView.kt +54 −3 Original line number Diff line number Diff line Loading @@ -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) } Loading @@ -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( Loading @@ -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?) { Loading @@ -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() } } packages/SystemUI/src/com/android/systemui/flags/Flags.kt +5 −1 Original line number Diff line number Diff line /* * 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. Loading Loading @@ -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) Loading Loading
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlay.kt +90 −20 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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. */ Loading @@ -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() Loading Loading @@ -95,13 +107,23 @@ constructor( MotionEvent.ACTION_MOVE -> { onFingerDown = true if (!view.isDisplayConfigured && alternateTouchProvider.isPresent) { 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.x.toInt(), event.y.toInt(), event.rawX.toInt(), event.rawY.toInt(), event.touchMinor, event.touchMajor ) Loading @@ -115,13 +137,17 @@ constructor( 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) } Loading @@ -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) { Loading @@ -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) Loading Loading @@ -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) ) } } }
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayView.kt +54 −3 Original line number Diff line number Diff line Loading @@ -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) } Loading @@ -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( Loading @@ -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?) { Loading @@ -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() } }
packages/SystemUI/src/com/android/systemui/flags/Flags.kt +5 −1 Original line number Diff line number Diff line /* * 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. Loading Loading @@ -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) Loading