Ignore touches outside of Udfps Overlay bounds
Udfps overlay is fullscreen so unnecessary ellipse checks can be removed by only checking touches that are within the overlay bounds (bottom half of screen). These touches are likely way off from the sensor. Bug: 274162395 Test: atest SystemUITests:com.android.systemui.biometrics Change-Id: I56af14d9817b5b4719a20d40dcc69f961003b1be
This commit is contained in:
@@ -36,6 +36,9 @@ data class UdfpsOverlayParams(
|
||||
/** Same as [sensorBounds], but in native resolution. */
|
||||
val nativeSensorBounds = Rect(sensorBounds).apply { scale(1f / scaleFactor) }
|
||||
|
||||
/** Same as [overlayBounds], but in native resolution. */
|
||||
val nativeOverlayBounds = Rect(overlayBounds).apply { scale(1f / scaleFactor) }
|
||||
|
||||
/** See [android.view.DisplayInfo.logicalWidth] */
|
||||
val logicalDisplayWidth =
|
||||
if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
|
||||
|
||||
@@ -636,7 +636,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
|
||||
mOverlay.getOverlayView().getViewRootImpl().getInputToken());
|
||||
}
|
||||
|
||||
return processedTouch.getTouchData().isWithinSensor(mOverlayParams.getNativeSensorBounds());
|
||||
return processedTouch.getTouchData().isWithinBounds(mOverlayParams.getNativeSensorBounds());
|
||||
}
|
||||
|
||||
private boolean oldOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
|
||||
|
||||
@@ -22,6 +22,11 @@ import com.android.systemui.dagger.SysUISingleton
|
||||
/** Returns whether the touch coordinates are within the sensor's bounding box. */
|
||||
@SysUISingleton
|
||||
class BoundingBoxOverlapDetector : OverlapDetector {
|
||||
override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean =
|
||||
touchData.isWithinSensor(nativeSensorBounds)
|
||||
override fun isGoodOverlap(
|
||||
touchData: NormalizedTouchData,
|
||||
nativeSensorBounds: Rect,
|
||||
nativeOverlayBounds: Rect,
|
||||
): Boolean =
|
||||
touchData.isWithinBounds(nativeOverlayBounds) &&
|
||||
touchData.isWithinBounds(nativeSensorBounds)
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ private enum class SensorPixelPosition {
|
||||
TARGET // Pixel within sensor center target
|
||||
}
|
||||
|
||||
private val isDebug = true
|
||||
private val TAG = "EllipseOverlapDetector"
|
||||
private const val isDebug = false
|
||||
private const val TAG = "EllipseOverlapDetector"
|
||||
|
||||
/**
|
||||
* Approximates the touch as an ellipse and determines whether the ellipse has a sufficient overlap
|
||||
@@ -39,12 +39,21 @@ private val TAG = "EllipseOverlapDetector"
|
||||
*/
|
||||
@SysUISingleton
|
||||
class EllipseOverlapDetector(private val params: EllipseOverlapDetectorParams) : OverlapDetector {
|
||||
override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
|
||||
// First, check if touch is within bounding box,
|
||||
if (nativeSensorBounds.contains(touchData.x.toInt(), touchData.y.toInt())) {
|
||||
override fun isGoodOverlap(
|
||||
touchData: NormalizedTouchData,
|
||||
nativeSensorBounds: Rect,
|
||||
nativeOverlayBounds: Rect,
|
||||
): Boolean {
|
||||
// First, check if touch is within bounding box to exit early
|
||||
if (touchData.isWithinBounds(nativeSensorBounds)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check touch is within overlay bounds, not worth checking if outside
|
||||
if (!touchData.isWithinBounds(nativeOverlayBounds)) {
|
||||
return false
|
||||
}
|
||||
|
||||
var isTargetTouched = false
|
||||
var sensorPixels = 0
|
||||
var coveredPixels = 0
|
||||
@@ -77,7 +86,7 @@ class EllipseOverlapDetector(private val params: EllipseOverlapDetectorParams) :
|
||||
|
||||
val percentage: Float = coveredPixels.toFloat() / sensorPixels
|
||||
if (isDebug) {
|
||||
Log.v(
|
||||
Log.d(
|
||||
TAG,
|
||||
"covered: $coveredPixels, sensor: $sensorPixels, " +
|
||||
"percentage: $percentage, isCenterTouched: $isTargetTouched"
|
||||
|
||||
@@ -51,16 +51,16 @@ data class NormalizedTouchData(
|
||||
) {
|
||||
|
||||
/**
|
||||
* [nativeSensorBounds] contains the location and dimensions of the sensor area in native
|
||||
* resolution and natural orientation.
|
||||
* [nativeBounds] contains the location and dimensions of the area in native resolution and
|
||||
* natural orientation.
|
||||
*
|
||||
* Returns whether the coordinates of the given pointer are within the sensor's bounding box.
|
||||
* Returns whether the coordinates of the given pointer are within the bounding box.
|
||||
*/
|
||||
fun isWithinSensor(nativeSensorBounds: Rect): Boolean {
|
||||
return nativeSensorBounds.left <= x &&
|
||||
nativeSensorBounds.right >= x &&
|
||||
nativeSensorBounds.top <= y &&
|
||||
nativeSensorBounds.bottom >= y
|
||||
fun isWithinBounds(nativeBounds: Rect): Boolean {
|
||||
return nativeBounds.left <= x &&
|
||||
nativeBounds.right >= x &&
|
||||
nativeBounds.top <= y &&
|
||||
nativeBounds.bottom >= y
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
|
||||
@@ -20,5 +20,9 @@ import android.graphics.Rect
|
||||
|
||||
/** Determines whether the touch has a sufficient overlap with the sensor. */
|
||||
interface OverlapDetector {
|
||||
fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean
|
||||
fun isGoodOverlap(
|
||||
touchData: NormalizedTouchData,
|
||||
nativeSensorBounds: Rect,
|
||||
nativeOverlayBounds: Rect
|
||||
): Boolean
|
||||
}
|
||||
|
||||
@@ -46,7 +46,13 @@ class SinglePointerTouchProcessor @Inject constructor(val overlapDetector: Overl
|
||||
val touchData = List(event.pointerCount) { event.normalize(it, overlayParams) }
|
||||
val pointersOnSensor =
|
||||
touchData
|
||||
.filter { overlapDetector.isGoodOverlap(it, overlayParams.nativeSensorBounds) }
|
||||
.filter {
|
||||
overlapDetector.isGoodOverlap(
|
||||
it,
|
||||
overlayParams.nativeSensorBounds,
|
||||
overlayParams.nativeOverlayBounds
|
||||
)
|
||||
}
|
||||
.map { it.pointerId }
|
||||
return PreprocessedTouch(touchData, previousPointerOnSensorId, pointersOnSensor)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
|
||||
@Test
|
||||
fun isGoodOverlap() {
|
||||
val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat())
|
||||
val actual = underTest.isGoodOverlap(touchData, SENSOR)
|
||||
val actual = underTest.isGoodOverlap(touchData, SENSOR, OVERLAY)
|
||||
|
||||
assertThat(actual).isEqualTo(testCase.expected)
|
||||
}
|
||||
@@ -50,8 +50,10 @@ class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
|
||||
validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
|
||||
),
|
||||
genNegativeTestCases(
|
||||
invalidXs = listOf(SENSOR.left - 1, SENSOR.right + 1),
|
||||
invalidYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1),
|
||||
invalidXs =
|
||||
listOf(SENSOR.left - 1, SENSOR.right + 1, OVERLAY.left, OVERLAY.right),
|
||||
invalidYs =
|
||||
listOf(SENSOR.top - 1, SENSOR.bottom + 1, OVERLAY.top, OVERLAY.bottom),
|
||||
validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
|
||||
validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
|
||||
)
|
||||
@@ -82,6 +84,7 @@ private val TOUCH_DATA =
|
||||
)
|
||||
|
||||
private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 500 /* bottom */)
|
||||
private val OVERLAY = Rect(0 /* left */, 100 /* top */, 400 /* right */, 600 /* bottom */)
|
||||
|
||||
private fun genTestCases(
|
||||
xs: List<Int>,
|
||||
|
||||
@@ -43,7 +43,7 @@ class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
|
||||
minor = testCase.minor,
|
||||
major = testCase.major
|
||||
)
|
||||
val actual = underTest.isGoodOverlap(touchData, SENSOR)
|
||||
val actual = underTest.isGoodOverlap(touchData, SENSOR, OVERLAY)
|
||||
|
||||
assertThat(actual).isEqualTo(testCase.expected)
|
||||
}
|
||||
@@ -71,8 +71,10 @@ class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
|
||||
expected = true
|
||||
),
|
||||
genNegativeTestCase(
|
||||
outerXs = listOf(SENSOR.left - 1, SENSOR.right + 1),
|
||||
outerYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1),
|
||||
outerXs =
|
||||
listOf(SENSOR.left - 1, SENSOR.right + 1, OVERLAY.left, OVERLAY.right),
|
||||
outerYs =
|
||||
listOf(SENSOR.top - 1, SENSOR.bottom + 1, OVERLAY.top, OVERLAY.bottom),
|
||||
minor = 100f,
|
||||
major = 100f,
|
||||
expected = false
|
||||
@@ -104,6 +106,7 @@ private val TOUCH_DATA =
|
||||
)
|
||||
|
||||
private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 400 /* bottom */)
|
||||
private val OVERLAY = Rect(0 /* left */, 100 /* top */, 400 /* right */, 600 /* bottom */)
|
||||
|
||||
private fun genPositiveTestCases(
|
||||
innerXs: List<Int>,
|
||||
|
||||
@@ -16,7 +16,7 @@ class NormalizedTouchDataTest(val testCase: TestCase) : SysuiTestCase() {
|
||||
@Test
|
||||
fun isWithinSensor() {
|
||||
val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat())
|
||||
val actual = touchData.isWithinSensor(SENSOR)
|
||||
val actual = touchData.isWithinBounds(SENSOR)
|
||||
|
||||
assertThat(actual).isEqualTo(testCase.expected)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,11 @@ import android.graphics.Rect
|
||||
class FakeOverlapDetector : OverlapDetector {
|
||||
var shouldReturn: Map<Int, Boolean> = mapOf()
|
||||
|
||||
override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
|
||||
override fun isGoodOverlap(
|
||||
touchData: NormalizedTouchData,
|
||||
nativeSensorBounds: Rect,
|
||||
nativeOverlayBounds: Rect
|
||||
): Boolean {
|
||||
return shouldReturn[touchData.pointerId]
|
||||
?: error("Unexpected PointerId not declared in TestCase currentPointers")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user