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

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

Create new UdfpsOverlay and UdfpsOverlayView

Prototype for UdfpsController refactor

Run `adb shell statusbar udpfs udpfsOverlay show` to view

Bug: 252897742
Test: N/A
Change-Id: I83953aeed6cadcaf35498888b1ca8a5d95658cba
parent 44bdd7fd
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -792,7 +792,11 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
            mUdfpsBounds = udfpsProp.getLocation().getRect();
            mUdfpsBounds.scale(mScaleFactor);
            mUdfpsController.updateOverlayParams(udfpsProp.sensorId,
                    new UdfpsOverlayParams(mUdfpsBounds, mCachedDisplayInfo.getNaturalWidth(),
                    new UdfpsOverlayParams(mUdfpsBounds, new Rect(
                            0, mCachedDisplayInfo.getNaturalHeight() / 2,
                            mCachedDisplayInfo.getNaturalWidth(),
                            mCachedDisplayInfo.getNaturalHeight()),
                            mCachedDisplayInfo.getNaturalWidth(),
                            mCachedDisplayInfo.getNaturalHeight(), mScaleFactor,
                            mCachedDisplayInfo.rotation));
            if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds)) {
+206 −0
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.Rect
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
import android.os.Handler
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
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.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.Execution
import java.util.*
import java.util.concurrent.Executor
import javax.inject.Inject

private const val TAG = "UdfpsOverlay"

@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<AlternateUdfpsTouchProvider>,
    private val fgExecutor: DelayableExecutor,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val authController: AuthController,
    private val udfpsLogger: UdfpsLogger
) : 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()

    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(v: View, event: MotionEvent): Boolean {
        val view = v as UdfpsOverlayView

        return when (event.action) {
            MotionEvent.ACTION_DOWN,
            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.configureDisplay {
                        biometricExecutor.execute { alternateTouchProvider.get().onUiReady() }
                    }
                }

                true
            }
            MotionEvent.ACTION_UP,
            MotionEvent.ACTION_CANCEL -> {
                if (onFingerDown && alternateTouchProvider.isPresent) {
                    biometricExecutor.execute {
                        alternateTouchProvider.get().onPointerUp(requestId)
                    }
                    fgExecutor.execute {
                        if (keyguardUpdateMonitor.isFingerprintDetectionRunning) {
                            keyguardUpdateMonitor.onUdfpsPointerUp(requestId.toInt())
                        }
                    }
                }
                onFingerDown = false
                if (view.isDisplayConfigured) {
                    view.unconfigureDisplay()
                }

                true
            }
            else -> false
        }
    }

    fun show(requestId: Long): Boolean {
        this.requestId = requestId
        if (overlayView == null && alternateTouchProvider.isPresent) {
            UdfpsOverlayView(context, null).let {
                it.overlayParams = params
                it.setUdfpsDisplayMode(
                    UdfpsDisplayMode(context, execution, authController, udfpsLogger)
                )
                it.setOnTouchListener { v, event -> onTouch(v, event) }
                overlayView = it
            }
            windowManager.addView(overlayView, coreLayoutParams)
            return true
        }

        return false
    }

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

        overlayView = null
    }

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

    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
                )
        }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.view.Surface.Rotation

data class UdfpsOverlayParams(
    val sensorBounds: Rect = Rect(),
    val overlayBounds: Rect = Rect(),
    val naturalDisplayWidth: Int = 0,
    val naturalDisplayHeight: Int = 0,
    val scaleFactor: Float = 1f,
+83 −0
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.RectF
import android.util.AttributeSet
import android.widget.FrameLayout

private const val TAG = "UdfpsOverlayView"

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()
    val centerPaint = Paint()

    /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */
    var isDisplayConfigured: Boolean = false
        private set

    init {
        this.setWillNotDraw(false)
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()

        overlayPaint.color = Color.argb(120, 255, 0, 0)
        overlayPaint.style = Paint.Style.FILL

        sensorPaint.color = Color.argb(150, 134, 204, 255)
        sensorPaint.style = Paint.Style.FILL
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        canvas.drawRect(overlayParams.overlayBounds, overlayPaint)
        canvas.drawRect(overlayParams.sensorBounds, sensorPaint)
        canvas.drawCircle(
            overlayParams.sensorBounds.exactCenterX(),
            overlayParams.sensorBounds.exactCenterY(),
            overlayParams.sensorBounds.width().toFloat() / 2,
            centerPaint
        )
    }

    fun setUdfpsDisplayMode(udfpsDisplayMode: UdfpsDisplayMode?) {
        mUdfpsDisplayMode = udfpsDisplayMode
    }

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

    fun unconfigureDisplay() {
        isDisplayConfigured = false
        mUdfpsDisplayMode?.disable(null /* onDisabled */)
    }
}
+23 −4
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.biometrics

import android.content.Context
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
@@ -23,9 +24,9 @@ import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTING
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
import android.hardware.fingerprint.IUdfpsOverlayController
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.util.Log
import android.view.LayoutInflater
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -41,14 +42,17 @@ private const val SENSOR_ID = 0
 */
@SysUISingleton
class UdfpsShell @Inject constructor(
    commandRegistry: CommandRegistry
    commandRegistry: CommandRegistry,
    private val udfpsOverlay: UdfpsOverlay
) : Command {

    /**
     * Set in [UdfpsController.java] constructor, used to show and hide the UDFPS overlay.
     * TODO: inject after b/229290039 is resolved
     */
    var udfpsOverlayController: IUdfpsOverlayController? = null
    var udfpsOverlayController: UdfpsController.UdfpsOverlayController? = null
    var context: Context? = null
    var inflater: LayoutInflater? = null

    init {
        commandRegistry.registerCommand("udfps") { this }
@@ -57,6 +61,11 @@ 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") {
            hideOverlay()
            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 {
@@ -104,6 +113,16 @@ 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)
    }
Loading