Merge changes from topic "EnableNewTouch"
* changes: Make EllipseOverlapDetector configurable Enable new touch detection by default on Master
This commit is contained in:
committed by
Android (Google) Code Review
commit
519cfdb306
@@ -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. -->
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
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)
|
||||
@@ -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,10 +36,30 @@ 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 {
|
||||
BoundingBoxOverlapDetector()
|
||||
return BoundingBoxOverlapDetector()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,9 +572,8 @@ object Flags {
|
||||
|
||||
// 2200 - udfps
|
||||
// TODO(b/259264861): Tracking Bug
|
||||
@JvmField val UDFPS_NEW_TOUCH_DETECTION = unreleasedFlag(2200, "udfps_new_touch_detection")
|
||||
@JvmField val UDFPS_ELLIPSE_DEBUG_UI = unreleasedFlag(2201, "udfps_ellipse_debug")
|
||||
@JvmField val UDFPS_ELLIPSE_DETECTION = unreleasedFlag(2202, "udfps_ellipse_detection")
|
||||
@JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag(2200, "udfps_new_touch_detection")
|
||||
@JvmField val UDFPS_ELLIPSE_DETECTION = releasedFlag(2201, "udfps_ellipse_detection")
|
||||
|
||||
// 2300 - stylus
|
||||
@JvmField val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used")
|
||||
|
||||
@@ -16,30 +16,23 @@
|
||||
|
||||
package com.android.systemui.biometrics.udfps
|
||||
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.biometrics.EllipseOverlapDetectorParams
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import org.junit.runners.Parameterized.Parameters
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.`when` as whenEver
|
||||
|
||||
@SmallTest
|
||||
@RunWith(Parameterized::class)
|
||||
class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
|
||||
val underTest = spy(EllipseOverlapDetector(neededPoints = 1))
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
// Use one single center point for testing, required or total number of points may change
|
||||
whenEver(underTest.calculateSensorPoints(SENSOR))
|
||||
.thenReturn(listOf(Point(SENSOR.centerX(), SENSOR.centerY())))
|
||||
}
|
||||
val underTest =
|
||||
EllipseOverlapDetector(
|
||||
EllipseOverlapDetectorParams(minOverlap = .4f, targetSize = .2f, stepSize = 1)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun isGoodOverlap() {
|
||||
|
||||
Reference in New Issue
Block a user