Merge "Ignore touches outside of Udfps Overlay bounds" into udc-dev

This commit is contained in:
Austin Delgado
2023-05-11 16:29:04 +00:00
committed by Android (Google) Code Review
11 changed files with 64 additions and 27 deletions

View File

@@ -36,6 +36,9 @@ data class UdfpsOverlayParams(
/** Same as [sensorBounds], but in native resolution. */ /** Same as [sensorBounds], but in native resolution. */
val nativeSensorBounds = Rect(sensorBounds).apply { scale(1f / scaleFactor) } 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] */ /** See [android.view.DisplayInfo.logicalWidth] */
val logicalDisplayWidth = val logicalDisplayWidth =
if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {

View File

@@ -636,7 +636,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mOverlay.getOverlayView().getViewRootImpl().getInputToken()); 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) { private boolean oldOnTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {

View File

@@ -22,6 +22,11 @@ import com.android.systemui.dagger.SysUISingleton
/** Returns whether the touch coordinates are within the sensor's bounding box. */ /** Returns whether the touch coordinates are within the sensor's bounding box. */
@SysUISingleton @SysUISingleton
class BoundingBoxOverlapDetector : OverlapDetector { class BoundingBoxOverlapDetector : OverlapDetector {
override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean = override fun isGoodOverlap(
touchData.isWithinSensor(nativeSensorBounds) touchData: NormalizedTouchData,
nativeSensorBounds: Rect,
nativeOverlayBounds: Rect,
): Boolean =
touchData.isWithinBounds(nativeOverlayBounds) &&
touchData.isWithinBounds(nativeSensorBounds)
} }

View File

@@ -30,8 +30,8 @@ private enum class SensorPixelPosition {
TARGET // Pixel within sensor center target TARGET // Pixel within sensor center target
} }
private val isDebug = true private const val isDebug = false
private val TAG = "EllipseOverlapDetector" private const val TAG = "EllipseOverlapDetector"
/** /**
* Approximates the touch as an ellipse and determines whether the ellipse has a sufficient overlap * Approximates the touch as an ellipse and determines whether the ellipse has a sufficient overlap
@@ -39,12 +39,21 @@ private val TAG = "EllipseOverlapDetector"
*/ */
@SysUISingleton @SysUISingleton
class EllipseOverlapDetector(private val params: EllipseOverlapDetectorParams) : OverlapDetector { class EllipseOverlapDetector(private val params: EllipseOverlapDetectorParams) : OverlapDetector {
override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean { override fun isGoodOverlap(
// First, check if touch is within bounding box, touchData: NormalizedTouchData,
if (nativeSensorBounds.contains(touchData.x.toInt(), touchData.y.toInt())) { nativeSensorBounds: Rect,
nativeOverlayBounds: Rect,
): Boolean {
// First, check if touch is within bounding box to exit early
if (touchData.isWithinBounds(nativeSensorBounds)) {
return true return true
} }
// Check touch is within overlay bounds, not worth checking if outside
if (!touchData.isWithinBounds(nativeOverlayBounds)) {
return false
}
var isTargetTouched = false var isTargetTouched = false
var sensorPixels = 0 var sensorPixels = 0
var coveredPixels = 0 var coveredPixels = 0
@@ -77,7 +86,7 @@ class EllipseOverlapDetector(private val params: EllipseOverlapDetectorParams) :
val percentage: Float = coveredPixels.toFloat() / sensorPixels val percentage: Float = coveredPixels.toFloat() / sensorPixels
if (isDebug) { if (isDebug) {
Log.v( Log.d(
TAG, TAG,
"covered: $coveredPixels, sensor: $sensorPixels, " + "covered: $coveredPixels, sensor: $sensorPixels, " +
"percentage: $percentage, isCenterTouched: $isTargetTouched" "percentage: $percentage, isCenterTouched: $isTargetTouched"

View File

@@ -51,16 +51,16 @@ data class NormalizedTouchData(
) { ) {
/** /**
* [nativeSensorBounds] contains the location and dimensions of the sensor area in native * [nativeBounds] contains the location and dimensions of the area in native resolution and
* resolution and natural orientation. * 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 { fun isWithinBounds(nativeBounds: Rect): Boolean {
return nativeSensorBounds.left <= x && return nativeBounds.left <= x &&
nativeSensorBounds.right >= x && nativeBounds.right >= x &&
nativeSensorBounds.top <= y && nativeBounds.top <= y &&
nativeSensorBounds.bottom >= y nativeBounds.bottom >= y
} }
@JvmOverloads @JvmOverloads

View File

@@ -20,5 +20,9 @@ import android.graphics.Rect
/** Determines whether the touch has a sufficient overlap with the sensor. */ /** Determines whether the touch has a sufficient overlap with the sensor. */
interface OverlapDetector { interface OverlapDetector {
fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean fun isGoodOverlap(
touchData: NormalizedTouchData,
nativeSensorBounds: Rect,
nativeOverlayBounds: Rect
): Boolean
} }

View File

@@ -46,7 +46,13 @@ class SinglePointerTouchProcessor @Inject constructor(val overlapDetector: Overl
val touchData = List(event.pointerCount) { event.normalize(it, overlayParams) } val touchData = List(event.pointerCount) { event.normalize(it, overlayParams) }
val pointersOnSensor = val pointersOnSensor =
touchData touchData
.filter { overlapDetector.isGoodOverlap(it, overlayParams.nativeSensorBounds) } .filter {
overlapDetector.isGoodOverlap(
it,
overlayParams.nativeSensorBounds,
overlayParams.nativeOverlayBounds
)
}
.map { it.pointerId } .map { it.pointerId }
return PreprocessedTouch(touchData, previousPointerOnSensorId, pointersOnSensor) return PreprocessedTouch(touchData, previousPointerOnSensorId, pointersOnSensor)
} }

View File

@@ -33,7 +33,7 @@ class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
@Test @Test
fun isGoodOverlap() { fun isGoodOverlap() {
val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat()) 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) assertThat(actual).isEqualTo(testCase.expected)
} }
@@ -50,8 +50,10 @@ class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY()) validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY())
), ),
genNegativeTestCases( genNegativeTestCases(
invalidXs = listOf(SENSOR.left - 1, SENSOR.right + 1), invalidXs =
invalidYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1), 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()), validXs = listOf(SENSOR.left, SENSOR.right, SENSOR.centerX()),
validYs = listOf(SENSOR.top, SENSOR.bottom, SENSOR.centerY()) 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 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( private fun genTestCases(
xs: List<Int>, xs: List<Int>,

View File

@@ -43,7 +43,7 @@ class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
minor = testCase.minor, minor = testCase.minor,
major = testCase.major major = testCase.major
) )
val actual = underTest.isGoodOverlap(touchData, SENSOR) val actual = underTest.isGoodOverlap(touchData, SENSOR, OVERLAY)
assertThat(actual).isEqualTo(testCase.expected) assertThat(actual).isEqualTo(testCase.expected)
} }
@@ -71,8 +71,10 @@ class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
expected = true expected = true
), ),
genNegativeTestCase( genNegativeTestCase(
outerXs = listOf(SENSOR.left - 1, SENSOR.right + 1), outerXs =
outerYs = listOf(SENSOR.top - 1, SENSOR.bottom + 1), 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, minor = 100f,
major = 100f, major = 100f,
expected = false expected = false
@@ -104,6 +106,7 @@ private val TOUCH_DATA =
) )
private val SENSOR = Rect(100 /* left */, 200 /* top */, 300 /* right */, 400 /* bottom */) 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( private fun genPositiveTestCases(
innerXs: List<Int>, innerXs: List<Int>,

View File

@@ -16,7 +16,7 @@ class NormalizedTouchDataTest(val testCase: TestCase) : SysuiTestCase() {
@Test @Test
fun isWithinSensor() { fun isWithinSensor() {
val touchData = TOUCH_DATA.copy(x = testCase.x.toFloat(), y = testCase.y.toFloat()) 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) assertThat(actual).isEqualTo(testCase.expected)
} }

View File

@@ -21,7 +21,11 @@ import android.graphics.Rect
class FakeOverlapDetector : OverlapDetector { class FakeOverlapDetector : OverlapDetector {
var shouldReturn: Map<Int, Boolean> = mapOf() 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] return shouldReturn[touchData.pointerId]
?: error("Unexpected PointerId not declared in TestCase currentPointers") ?: error("Unexpected PointerId not declared in TestCase currentPointers")
} }