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

Commit 519cfdb3 authored by Austin Delgado's avatar Austin Delgado Committed by Android (Google) Code Review
Browse files

Merge changes from topic "EnableNewTouch"

* changes:
  Make EllipseOverlapDetector configurable
  Enable new touch detection by default on Master
parents c0bcd9af f28f635a
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -5001,6 +5001,26 @@
      -->
    </integer-array>

    <!-- The properties for handling UDFPS touch detection
    <string-array name="config_udfps_touch_detection_options">
        <item>[detector_type],[sensor_shape],[target_size],[min_ellipse_overlap_percentage]</item>
    </string-array>

        [detector_type]: 0 for bounding box detector, 1 for ellipse detector
        [sensor_shape]: 0 for square, 1 for circle
        [target_size]: percentage (defined as a float of 0-1) of sensor that is considered the target, expands outward from center
        [min_ellipse_overlap_percentage]: minimum percentage (float from 0-1) needed to be considered a valid overlap
        [step_size]: size of each step when iterating over sensor pixel grid
    -->
    <string-array name="config_udfps_touch_detection_options">
        <item>0,0,1.0,0,1</item>
        <item>1,1,1.0,0,1</item>
        <item>1,1,1.0,.4,1</item>
    </string-array>

    <!-- The integer index of the selected option in config_udfps_touch_detection_options -->
    <integer name="config_selected_udfps_touch_detection">2</integer>

    <!-- An array of arrays of side fingerprint sensor properties relative to each display.
         Note: this value is temporary and is expected to be queried directly
         from the HAL in the future. -->
+2 −0
Original line number Diff line number Diff line
@@ -2649,6 +2649,8 @@
  <java-symbol type="array" name="config_biometric_sensors" />
  <java-symbol type="bool" name="allow_test_udfps" />
  <java-symbol type="array" name="config_udfps_sensor_props" />
  <java-symbol type="array" name="config_udfps_touch_detection_options" />
  <java-symbol type="integer" name="config_selected_udfps_touch_detection" />
  <java-symbol type="array" name="config_sfps_sensor_props" />
  <java-symbol type="bool" name="config_is_powerbutton_fps" />
  <java-symbol type="array" name="config_udfps_enroll_stage_thresholds" />
+13 −0
Original line number Diff line number Diff line
package com.android.systemui.biometrics

/**
 * Collection of parameters used by EllipseOverlapDetector
 *
 * [minOverlap] minimum percentage (float from 0-1) needed to be considered a valid overlap
 *
 * [targetSize] percentage (defined as a float of 0-1) of sensor that is considered the target,
 * expands outward from center
 *
 * [stepSize] size of each step when iterating over sensor pixel grid
 */
class EllipseOverlapDetectorParams(val minOverlap: Float, val targetSize: Float, val stepSize: Int)
+26 −3
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.systemui.biometrics.dagger

import android.content.res.Resources
import com.android.internal.R
import com.android.systemui.biometrics.EllipseOverlapDetectorParams
import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector
import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
import com.android.systemui.biometrics.udfps.OverlapDetector
@@ -33,11 +36,31 @@ interface UdfpsModule {
        @Provides
        @SysUISingleton
        fun providesOverlapDetector(featureFlags: FeatureFlags): OverlapDetector {
            return if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
                EllipseOverlapDetector()
            if (featureFlags.isEnabled(Flags.UDFPS_ELLIPSE_DETECTION)) {
                val selectedOption =
                    Resources.getSystem()
                        .getInteger(R.integer.config_selected_udfps_touch_detection)
                val values =
                    Resources.getSystem()
                        .getStringArray(R.array.config_udfps_touch_detection_options)[
                            selectedOption]
                        .split(",")
                        .map { it.toFloat() }

                return if (values[0] == 1f) {
                    EllipseOverlapDetector(
                        EllipseOverlapDetectorParams(
                            minOverlap = values[3],
                            targetSize = values[2],
                            stepSize = values[4].toInt()
                        )
                    )
                } else {
                    BoundingBoxOverlapDetector()
                }
            } else {
                return BoundingBoxOverlapDetector()
            }
        }
    }
}
+70 −28
Original line number Diff line number Diff line
@@ -18,22 +18,84 @@ package com.android.systemui.biometrics.udfps

import android.graphics.Point
import android.graphics.Rect
import androidx.annotation.VisibleForTesting
import android.util.Log
import com.android.systemui.biometrics.EllipseOverlapDetectorParams
import com.android.systemui.dagger.SysUISingleton
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin

private enum class SensorPixelPosition {
    OUTSIDE, // Pixel that falls outside of sensor circle
    SENSOR, // Pixel within sensor circle
    TARGET // Pixel within sensor center target
}

private val isDebug = true
private val TAG = "EllipseOverlapDetector"

/**
 * Approximates the touch as an ellipse and determines whether the ellipse has a sufficient overlap
 * with the sensor.
 */
@SysUISingleton
class EllipseOverlapDetector(private val neededPoints: Int = 2) : OverlapDetector {

class EllipseOverlapDetector(private val params: EllipseOverlapDetectorParams) : OverlapDetector {
    override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
        val points = calculateSensorPoints(nativeSensorBounds)
        return points.count { checkPoint(it, touchData) } >= neededPoints
        var isTargetTouched = false
        var sensorPixels = 0
        var coveredPixels = 0
        for (y in nativeSensorBounds.top..nativeSensorBounds.bottom step params.stepSize) {
            for (x in nativeSensorBounds.left..nativeSensorBounds.right step params.stepSize) {
                // Check where pixel is within the sensor TODO: (b/265836919) This could be improved
                // by precomputing these points
                val pixelPosition =
                    isPartOfSensorArea(
                        x,
                        y,
                        nativeSensorBounds.centerX(),
                        nativeSensorBounds.centerY(),
                        nativeSensorBounds.width() / 2
                    )
                if (pixelPosition != SensorPixelPosition.OUTSIDE) {
                    sensorPixels++

                    // Check if this pixel falls within ellipse touch
                    if (checkPoint(Point(x, y), touchData)) {
                        coveredPixels++

                        // Check that at least one covered pixel is within sensor target
                        isTargetTouched =
                            isTargetTouched or (pixelPosition == SensorPixelPosition.TARGET)
                    }
                }
            }
        }

        val percentage: Float = coveredPixels.toFloat() / sensorPixels
        if (isDebug) {
            Log.v(
                TAG,
                "covered: $coveredPixels, sensor: $sensorPixels, " +
                    "percentage: $percentage, isCenterTouched: $isTargetTouched"
            )
        }

        return percentage >= params.minOverlap && isTargetTouched
    }

    /** Checks if point is in the sensor center target circle, outer circle, or outside of sensor */
    private fun isPartOfSensorArea(x: Int, y: Int, cX: Int, cY: Int, r: Int): SensorPixelPosition {
        val dx = cX - x
        val dy = cY - y

        val disSquared = dx * dx + dy * dy

        return if (disSquared <= (r * params.targetSize) * (r * params.targetSize)) {
            SensorPixelPosition.TARGET
        } else if (disSquared <= r * r) {
            SensorPixelPosition.SENSOR
        } else {
            SensorPixelPosition.OUTSIDE
        }
    }

    private fun checkPoint(point: Point, touchData: NormalizedTouchData): Boolean {
@@ -45,29 +107,9 @@ class EllipseOverlapDetector(private val neededPoints: Int = 2) : OverlapDetecto
        val c: Float = sin(touchData.orientation) * (point.x - touchData.x)
        val d: Float = cos(touchData.orientation) * (point.y - touchData.y)
        val result =
            (a + b).pow(2) / (touchData.minor / 2).pow(2) +
                (c - d).pow(2) / (touchData.major / 2).pow(2)
            (a + b) * (a + b) / ((touchData.minor / 2) * (touchData.minor / 2)) +
                (c - d) * (c - d) / ((touchData.major / 2) * (touchData.major / 2))

        return result <= 1
    }

    @VisibleForTesting
    fun calculateSensorPoints(sensorBounds: Rect): List<Point> {
        val sensorX = sensorBounds.centerX()
        val sensorY = sensorBounds.centerY()
        val cornerOffset: Int = sensorBounds.width() / 4
        val sideOffset: Int = sensorBounds.width() / 3

        return listOf(
            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)
        )
    }
}
Loading