Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9bb6bec6 authored by Austin Delgado's avatar Austin Delgado
Browse files

Remove unused UdfpsOverlay and UdfpsOverlay View

Remove the experimental UdfpsOverlay for testing ellipse that has now been integrated into the exisiting UdfpsControllerOverlay and UdfpsView.

Bug: 274637712
Test: atest SystemUITests:com.android.systemui.biometrics
Change-Id: Iae013670c8d6885c106f1b931fd311d366f5cefd
parent 25b58217
Loading
Loading
Loading
Loading
+0 −345
Original line number Diff line number Diff line
/*
 * 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.annotation.SuppressLint
import android.content.Context
import android.graphics.PixelFormat
import android.graphics.Point
import android.graphics.Rect
import android.hardware.biometrics.BiometricOverlayConstants
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import android.hardware.fingerprint.IUdfpsOverlay
import android.os.Handler
import android.provider.Settings
import android.view.MotionEvent
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.settingslib.udfps.UdfpsOverlayParams
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
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.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Provider
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin

private const val TAG = "UdfpsOverlay"

const val SETTING_OVERLAY_DEBUG = "udfps_overlay_debug"

// Number of sensor points needed inside ellipse for good overlap
private const val NEEDED_POINTS = 2

@SuppressLint("ClickableViewAccessibility")
@SysUISingleton
class UdfpsOverlay
@Inject
constructor(
    private val context: Context,
    private val execution: Execution,
    private val windowManager: WindowManager,
    private val fingerprintManager: FingerprintManager?,
    private val handler: Handler,
    private val biometricExecutor: Executor,
    private val alternateTouchProvider: Optional<Provider<AlternateUdfpsTouchProvider>>,
    @Main private val fgExecutor: DelayableExecutor,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val authController: AuthController,
    private val udfpsLogger: UdfpsLogger,
    private var featureFlags: FeatureFlags
) : CoreStartable {

    /** The view, when [isShowing], or null. */
    var overlayView: UdfpsOverlayView? = null
        private set

    private var requestId: Long = 0
    private var onFingerDown = false
    val size = windowManager.maximumWindowMetrics.bounds

    val udfpsProps: MutableList<FingerprintSensorPropertiesInternal> = mutableListOf()
    var points: Array<Point> = emptyArray()
    var processedMotionEvent = false
    var isShowing = false

    private var params: UdfpsOverlayParams = UdfpsOverlayParams()

    private val coreLayoutParams =
        WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
                0 /* flags set in computeLayoutParams() */,
                PixelFormat.TRANSLUCENT
            )
            .apply {
                title = TAG
                fitInsetsTypes = 0
                gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
                layoutInDisplayCutoutMode =
                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
                flags = Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS
                privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
                // Avoid announcing window title.
                accessibilityTitle = " "
                inputFeatures = INPUT_FEATURE_SPY
            }

    fun onTouch(event: MotionEvent): Boolean {
        val view = overlayView!!

        return when (event.action) {
            MotionEvent.ACTION_DOWN,
            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
                                .map(Provider<AlternateUdfpsTouchProvider>::get)
                                .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
                                        .map(Provider<AlternateUdfpsTouchProvider>::get)
                                        .get()
                                        .onUiReady()
                                }
                            }

                            processedMotionEvent = true
                        }
                    }

                    view.invalidate()
                }
                true
            }
            MotionEvent.ACTION_UP,
            MotionEvent.ACTION_CANCEL -> {
                if (processedMotionEvent && alternateTouchProvider.isPresent) {
                    biometricExecutor.execute {
                        alternateTouchProvider
                            .map(Provider<AlternateUdfpsTouchProvider>::get)
                            .get()
                            .onPointerUp(requestId)
                    }
                    fgExecutor.execute {
                        if (keyguardUpdateMonitor.isFingerprintDetectionRunning) {
                            keyguardUpdateMonitor.onUdfpsPointerUp(requestId.toInt())
                        }
                    }

                    processedMotionEvent = 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(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 show(requestId: Long) {
        if (!featureFlags.isEnabled(Flags.NEW_UDFPS_OVERLAY)) {
            return
        }

        this.requestId = requestId
        fgExecutor.execute {
            if (overlayView == null && alternateTouchProvider.isPresent) {
                UdfpsOverlayView(context, null).let {
                    it.overlayParams = params
                    it.setUdfpsDisplayMode(
                        UdfpsDisplayMode(context, execution, authController, udfpsLogger)
                    )
                    it.setOnTouchListener { _, event -> onTouch(event) }
                    it.sensorPoints = points
                    it.debugOverlay =
                        Settings.Global.getInt(
                            context.contentResolver,
                            SETTING_OVERLAY_DEBUG,
                            0 /* def */
                        ) != 0
                    overlayView = it
                }
                windowManager.addView(overlayView, coreLayoutParams)
                isShowing = true
            }
        }
    }

    fun hide() {
        if (!featureFlags.isEnabled(Flags.NEW_UDFPS_OVERLAY)) {
            return
        }

        fgExecutor.execute {
            if (overlayView != null && isShowing && alternateTouchProvider.isPresent) {
                if (processedMotionEvent) {
                    biometricExecutor.execute {
                        alternateTouchProvider
                            .map(Provider<AlternateUdfpsTouchProvider>::get)
                            .get()
                            .onPointerUp(requestId)
                    }
                    fgExecutor.execute {
                        if (keyguardUpdateMonitor.isFingerprintDetectionRunning) {
                            keyguardUpdateMonitor.onUdfpsPointerUp(requestId.toInt())
                        }
                    }
                }

                if (overlayView!!.isDisplayConfigured) {
                    overlayView!!.unconfigureDisplay()
                }

                overlayView?.apply {
                    windowManager.removeView(this)
                    setOnTouchListener(null)
                }

                isShowing = false
                overlayView = null
                processedMotionEvent = false
            }
        }
    }

    @Override
    override fun start() {
        fingerprintManager?.addAuthenticatorsRegisteredCallback(
            object : IFingerprintAuthenticatorsRegisteredCallback.Stub() {
                override fun onAllAuthenticatorsRegistered(
                    sensors: List<FingerprintSensorPropertiesInternal>
                ) {
                    handler.post { handleAllFingerprintAuthenticatorsRegistered(sensors) }
                }
            }
        )

        fingerprintManager?.setUdfpsOverlay(
            object : IUdfpsOverlay.Stub() {
                override fun show(
                    requestId: Long,
                    sensorId: Int,
                    @BiometricOverlayConstants.ShowReason reason: Int
                ) = show(requestId)

                override fun hide(sensorId: Int) = hide()
            }
        )
    }

    private fun handleAllFingerprintAuthenticatorsRegistered(
        sensors: List<FingerprintSensorPropertiesInternal>
    ) {
        for (props in sensors) {
            if (props.isAnyUdfpsType) {
                udfpsProps.add(props)
            }
        }

        // Setup param size
        if (udfpsProps.isNotEmpty()) {
            params =
                UdfpsOverlayParams(
                    sensorBounds = udfpsProps[0].location.rect,
                    overlayBounds = Rect(0, size.height() / 2, size.width(), size.height()),
                    naturalDisplayWidth = size.width(),
                    naturalDisplayHeight = size.height(),
                    scaleFactor = 1f
                )

            val sensorX = params.sensorBounds.centerX()
            val sensorY = params.sensorBounds.centerY()
            val cornerOffset: Int = params.sensorBounds.width() / 4
            val sideOffset: Int = params.sensorBounds.width() / 3

            points =
                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)
                )
        }
    }
}
+0 −143
Original line number Diff line number Diff line
/*
 * 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.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
import com.android.settingslib.udfps.UdfpsOverlayParams

private const val TAG = "UdfpsOverlayView"
private const val POINT_SIZE = 10f

class UdfpsOverlayView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
    var overlayParams = UdfpsOverlayParams()
    private var mUdfpsDisplayMode: UdfpsDisplayMode? = null

    var debugOverlay = false

    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)
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()

        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)

        if (debugOverlay) {
            // Draw overlay and sensor bounds
            canvas.drawRect(overlayParams.overlayBounds, overlayPaint)
            canvas.drawRect(overlayParams.sensorBounds, sensorPaint)
        }

        // Draw sensor circle
        canvas.drawCircle(
            overlayParams.sensorBounds.exactCenterX(),
            overlayParams.sensorBounds.exactCenterY(),
            overlayParams.sensorBounds.width().toFloat() / 2,
            centerPaint
        )

        if (debugOverlay) {
            // 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?) {
        mUdfpsDisplayMode = udfpsDisplayMode
    }

    fun configureDisplay(onDisplayConfigured: Runnable) {
        isDisplayConfigured = true
        mUdfpsDisplayMode?.enable(onDisplayConfigured)
    }

    fun unconfigureDisplay() {
        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()
    }
}
+1 −16
Original line number Diff line number Diff line
@@ -50,8 +50,7 @@ private const val MAJOR = 10F
 */
@SysUISingleton
class UdfpsShell @Inject constructor(
    commandRegistry: CommandRegistry,
    private val udfpsOverlay: UdfpsOverlay
    commandRegistry: CommandRegistry
) : Command {

    /**
@@ -69,10 +68,6 @@ class UdfpsShell @Inject constructor(
    override fun execute(pw: PrintWriter, args: List<String>) {
        if (args.size == 1 && args[0] == "hide") {
            hideOverlay()
        } else if (args.size == 2 && args[0] == "udfpsOverlay" && args[1] == "show") {
            showUdfpsOverlay()
        } else if (args.size == 2 && args[0] == "udfpsOverlay" && args[1] == "hide") {
            hideUdfpsOverlay()
        } else if (args.size == 2 && args[0] == "show") {
            showOverlay(getEnrollmentReason(args[1]))
        } else if (args.size == 1 && args[0] == "onUiReady") {
@@ -131,16 +126,6 @@ class UdfpsShell @Inject constructor(
        )
    }

    private fun showUdfpsOverlay() {
        Log.v(TAG, "showUdfpsOverlay")
        udfpsOverlay.show(REQUEST_ID)
    }

    private fun hideUdfpsOverlay() {
        Log.v(TAG, "hideUdfpsOverlay")
        udfpsOverlay.hide()
    }

    private fun hideOverlay() {
        udfpsOverlayController?.hideUdfpsOverlay(SENSOR_ID)
    }
+0 −7
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import com.android.systemui.SliceBroadcastRelayHandler
import com.android.systemui.accessibility.SystemActions
import com.android.systemui.accessibility.WindowMagnification
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.UdfpsOverlay
import com.android.systemui.clipboardoverlay.ClipboardListener
import com.android.systemui.controls.dagger.StartControlsStartableModule
import com.android.systemui.dagger.qualifiers.PerUser
@@ -229,12 +228,6 @@ abstract class SystemUICoreStartableModule {
    @ClassKey(KeyguardLiftController::class)
    abstract fun bindKeyguardLiftController(sysui: KeyguardLiftController): CoreStartable

    /** Inject into UdfpsOverlay.  */
    @Binds
    @IntoMap
    @ClassKey(UdfpsOverlay::class)
    abstract fun bindUdfpsOverlay(sysui: UdfpsOverlay): CoreStartable

    /** Inject into MediaTttSenderCoordinator. */
    @Binds
    @IntoMap
+1 −2
Original line number Diff line number Diff line
@@ -49,7 +49,6 @@ class UdfpsShellTest : SysuiTestCase() {
    private lateinit var udfpsShell: UdfpsShell

    @Mock lateinit var commandRegistry: CommandRegistry
    @Mock lateinit var udfpsOverlay: UdfpsOverlay
    @Mock lateinit var udfpsOverlayController: UdfpsOverlayController

    @Captor private lateinit var motionEvent: ArgumentCaptor<MotionEvent>
@@ -60,7 +59,7 @@ class UdfpsShellTest : SysuiTestCase() {
    fun setup() {
        whenEver(udfpsOverlayController.sensorBounds).thenReturn(sensorBounds)

        udfpsShell = UdfpsShell(commandRegistry, udfpsOverlay)
        udfpsShell = UdfpsShell(commandRegistry)
        udfpsShell.udfpsOverlayController = udfpsOverlayController
    }