Add multitouch to new touch architecture
Test: atest SystemUITests:com.android.systemui.biometrics Bug: 253085297 Change-Id: I05d7377921cd6675ebb1618b29e1b58dcf9ee4d6
This commit is contained in:
@@ -26,28 +26,28 @@ data class NormalizedTouchData(
|
||||
* Value obtained from [MotionEvent.getPointerId], or [MotionEvent.INVALID_POINTER_ID] if the ID
|
||||
* is not available.
|
||||
*/
|
||||
val pointerId: Int,
|
||||
val pointerId: Int = MotionEvent.INVALID_POINTER_ID,
|
||||
|
||||
/** [MotionEvent.getRawX] mapped to natural orientation and native resolution. */
|
||||
val x: Float,
|
||||
val x: Float = 0f,
|
||||
|
||||
/** [MotionEvent.getRawY] mapped to natural orientation and native resolution. */
|
||||
val y: Float,
|
||||
val y: Float = 0f,
|
||||
|
||||
/** [MotionEvent.getTouchMinor] mapped to natural orientation and native resolution. */
|
||||
val minor: Float,
|
||||
val minor: Float = 0f,
|
||||
|
||||
/** [MotionEvent.getTouchMajor] mapped to natural orientation and native resolution. */
|
||||
val major: Float,
|
||||
val major: Float = 0f,
|
||||
|
||||
/** [MotionEvent.getOrientation] mapped to natural orientation. */
|
||||
val orientation: Float,
|
||||
val orientation: Float = 0f,
|
||||
|
||||
/** [MotionEvent.getEventTime]. */
|
||||
val time: Long,
|
||||
val time: Long = 0,
|
||||
|
||||
/** [MotionEvent.getDownTime]. */
|
||||
val gestureStart: Long,
|
||||
val gestureStart: Long = 0,
|
||||
) {
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,74 +43,72 @@ class SinglePointerTouchProcessor @Inject constructor(val overlapDetector: Overl
|
||||
): TouchProcessorResult {
|
||||
|
||||
fun preprocess(): PreprocessedTouch {
|
||||
// TODO(b/253085297): Add multitouch support. pointerIndex can be > 0 for ACTION_MOVE.
|
||||
val pointerIndex = 0
|
||||
val touchData = event.normalize(pointerIndex, overlayParams)
|
||||
val isGoodOverlap =
|
||||
overlapDetector.isGoodOverlap(touchData, overlayParams.nativeSensorBounds)
|
||||
return PreprocessedTouch(touchData, previousPointerOnSensorId, isGoodOverlap)
|
||||
val touchData = List(event.pointerCount) { event.normalize(it, overlayParams) }
|
||||
val pointersOnSensor =
|
||||
touchData
|
||||
.filter { overlapDetector.isGoodOverlap(it, overlayParams.nativeSensorBounds) }
|
||||
.map { it.pointerId }
|
||||
return PreprocessedTouch(touchData, previousPointerOnSensorId, pointersOnSensor)
|
||||
}
|
||||
|
||||
return when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> processActionDown(preprocess())
|
||||
MotionEvent.ACTION_DOWN,
|
||||
MotionEvent.ACTION_POINTER_DOWN,
|
||||
MotionEvent.ACTION_MOVE -> processActionMove(preprocess())
|
||||
MotionEvent.ACTION_UP -> processActionUp(preprocess())
|
||||
MotionEvent.ACTION_CANCEL ->
|
||||
processActionCancel(event.normalize(pointerIndex = 0, overlayParams))
|
||||
MotionEvent.ACTION_UP,
|
||||
MotionEvent.ACTION_POINTER_UP ->
|
||||
processActionUp(preprocess(), event.getPointerId(event.actionIndex))
|
||||
MotionEvent.ACTION_CANCEL -> processActionCancel(NormalizedTouchData())
|
||||
else ->
|
||||
Failure("Unsupported MotionEvent." + MotionEvent.actionToString(event.actionMasked))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [data] contains a list of NormalizedTouchData for pointers in the motionEvent ordered by
|
||||
* pointerIndex
|
||||
*
|
||||
* [previousPointerOnSensorId] the pointerId of the previous pointer on the sensor,
|
||||
* [MotionEvent.INVALID_POINTER_ID] if none
|
||||
*
|
||||
* [pointersOnSensor] contains a list of ids of pointers on the sensor
|
||||
*/
|
||||
private data class PreprocessedTouch(
|
||||
val data: NormalizedTouchData,
|
||||
val data: List<NormalizedTouchData>,
|
||||
val previousPointerOnSensorId: Int,
|
||||
val isGoodOverlap: Boolean,
|
||||
val pointersOnSensor: List<Int>,
|
||||
)
|
||||
|
||||
private fun processActionDown(touch: PreprocessedTouch): TouchProcessorResult {
|
||||
return if (touch.isGoodOverlap) {
|
||||
ProcessedTouch(InteractionEvent.DOWN, pointerOnSensorId = touch.data.pointerId, touch.data)
|
||||
} else {
|
||||
val event =
|
||||
if (touch.data.pointerId == touch.previousPointerOnSensorId) {
|
||||
InteractionEvent.UP
|
||||
} else {
|
||||
InteractionEvent.UNCHANGED
|
||||
}
|
||||
ProcessedTouch(event, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processActionMove(touch: PreprocessedTouch): TouchProcessorResult {
|
||||
val hadPointerOnSensor = touch.previousPointerOnSensorId != INVALID_POINTER_ID
|
||||
val interactionEvent =
|
||||
when {
|
||||
touch.isGoodOverlap && !hadPointerOnSensor -> InteractionEvent.DOWN
|
||||
!touch.isGoodOverlap && hadPointerOnSensor -> InteractionEvent.UP
|
||||
else -> InteractionEvent.UNCHANGED
|
||||
}
|
||||
val pointerOnSensorId =
|
||||
when (interactionEvent) {
|
||||
InteractionEvent.UNCHANGED -> touch.previousPointerOnSensorId
|
||||
InteractionEvent.DOWN -> touch.data.pointerId
|
||||
else -> INVALID_POINTER_ID
|
||||
}
|
||||
return ProcessedTouch(interactionEvent, pointerOnSensorId, touch.data)
|
||||
val hasPointerOnSensor = touch.pointersOnSensor.isNotEmpty()
|
||||
val pointerOnSensorId = touch.pointersOnSensor.firstOrNull() ?: INVALID_POINTER_ID
|
||||
|
||||
return if (!hadPointerOnSensor && hasPointerOnSensor) {
|
||||
val data = touch.data.find { it.pointerId == pointerOnSensorId } ?: NormalizedTouchData()
|
||||
ProcessedTouch(InteractionEvent.DOWN, data.pointerId, data)
|
||||
} else if (hadPointerOnSensor && !hasPointerOnSensor) {
|
||||
ProcessedTouch(InteractionEvent.UP, INVALID_POINTER_ID, NormalizedTouchData())
|
||||
} else {
|
||||
val data = touch.data.find { it.pointerId == pointerOnSensorId } ?: NormalizedTouchData()
|
||||
ProcessedTouch(InteractionEvent.UNCHANGED, pointerOnSensorId, data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processActionUp(touch: PreprocessedTouch): TouchProcessorResult {
|
||||
return if (touch.isGoodOverlap) {
|
||||
ProcessedTouch(InteractionEvent.UP, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
|
||||
private fun processActionUp(touch: PreprocessedTouch, actionId: Int): TouchProcessorResult {
|
||||
// Finger lifted and it was the only finger on the sensor
|
||||
return if (touch.pointersOnSensor.size == 1 && touch.pointersOnSensor.contains(actionId)) {
|
||||
ProcessedTouch(
|
||||
InteractionEvent.UP,
|
||||
pointerOnSensorId = INVALID_POINTER_ID,
|
||||
NormalizedTouchData()
|
||||
)
|
||||
} else {
|
||||
val event =
|
||||
if (touch.previousPointerOnSensorId != INVALID_POINTER_ID) {
|
||||
InteractionEvent.UP
|
||||
} else {
|
||||
InteractionEvent.UNCHANGED
|
||||
}
|
||||
ProcessedTouch(event, pointerOnSensorId = INVALID_POINTER_ID, touch.data)
|
||||
// Pick new pointerOnSensor that's not the finger that was lifted
|
||||
val pointerOnSensorId = touch.pointersOnSensor.find { it != actionId } ?: INVALID_POINTER_ID
|
||||
val data = touch.data.find { it.pointerId == pointerOnSensorId } ?: NormalizedTouchData()
|
||||
ProcessedTouch(InteractionEvent.UNCHANGED, data.pointerId, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase()
|
||||
|
||||
@Test
|
||||
fun processTouch() {
|
||||
overlapDetector.shouldReturn = testCase.isGoodOverlap
|
||||
overlapDetector.shouldReturn =
|
||||
testCase.currentPointers.associate { pointer -> pointer.id to pointer.onSensor }
|
||||
|
||||
val actual =
|
||||
underTest.processTouch(
|
||||
@@ -56,7 +57,7 @@ class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase()
|
||||
|
||||
data class TestCase(
|
||||
val event: MotionEvent,
|
||||
val isGoodOverlap: Boolean,
|
||||
val currentPointers: List<TestPointer>,
|
||||
val previousPointerOnSensorId: Int,
|
||||
val overlayParams: UdfpsOverlayParams,
|
||||
val expected: TouchProcessorResult,
|
||||
@@ -91,28 +92,21 @@ class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase()
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_DOWN,
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
isGoodOverlap = true,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
|
||||
expectedInteractionEvent = InteractionEvent.DOWN,
|
||||
expectedPointerOnSensorId = POINTER_ID,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_DOWN,
|
||||
previousPointerOnSensorId = POINTER_ID,
|
||||
isGoodOverlap = true,
|
||||
expectedInteractionEvent = InteractionEvent.DOWN,
|
||||
expectedPointerOnSensorId = POINTER_ID,
|
||||
expectedPointerOnSensorId = POINTER_ID_1,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_DOWN,
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
isGoodOverlap = false,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
|
||||
expectedInteractionEvent = InteractionEvent.UNCHANGED,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_DOWN,
|
||||
previousPointerOnSensorId = POINTER_ID,
|
||||
isGoodOverlap = false,
|
||||
previousPointerOnSensorId = POINTER_ID_1,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
|
||||
expectedInteractionEvent = InteractionEvent.UP,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
@@ -120,109 +114,226 @@ class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase()
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_MOVE,
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
isGoodOverlap = true,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
|
||||
expectedInteractionEvent = InteractionEvent.DOWN,
|
||||
expectedPointerOnSensorId = POINTER_ID,
|
||||
expectedPointerOnSensorId = POINTER_ID_1,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_MOVE,
|
||||
previousPointerOnSensorId = POINTER_ID,
|
||||
isGoodOverlap = true,
|
||||
previousPointerOnSensorId = POINTER_ID_1,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
|
||||
expectedInteractionEvent = InteractionEvent.UNCHANGED,
|
||||
expectedPointerOnSensorId = POINTER_ID,
|
||||
expectedPointerOnSensorId = POINTER_ID_1,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_MOVE,
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
isGoodOverlap = false,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
|
||||
expectedInteractionEvent = InteractionEvent.UNCHANGED,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_MOVE,
|
||||
previousPointerOnSensorId = POINTER_ID,
|
||||
isGoodOverlap = false,
|
||||
previousPointerOnSensorId = POINTER_ID_1,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
|
||||
expectedInteractionEvent = InteractionEvent.UP,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_MOVE,
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
currentPointers =
|
||||
listOf(
|
||||
TestPointer(id = POINTER_ID_1, onSensor = false),
|
||||
TestPointer(id = POINTER_ID_2, onSensor = true)
|
||||
),
|
||||
expectedInteractionEvent = InteractionEvent.DOWN,
|
||||
expectedPointerOnSensorId = POINTER_ID_2,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_MOVE,
|
||||
previousPointerOnSensorId = POINTER_ID_1,
|
||||
currentPointers =
|
||||
listOf(
|
||||
TestPointer(id = POINTER_ID_1, onSensor = false),
|
||||
TestPointer(id = POINTER_ID_2, onSensor = true)
|
||||
),
|
||||
expectedInteractionEvent = InteractionEvent.UNCHANGED,
|
||||
expectedPointerOnSensorId = POINTER_ID_2,
|
||||
),
|
||||
// MotionEvent.ACTION_UP
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_UP,
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
isGoodOverlap = true,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
|
||||
expectedInteractionEvent = InteractionEvent.UP,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_UP,
|
||||
previousPointerOnSensorId = POINTER_ID,
|
||||
isGoodOverlap = true,
|
||||
previousPointerOnSensorId = POINTER_ID_1,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
|
||||
expectedInteractionEvent = InteractionEvent.UP,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_UP,
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
isGoodOverlap = false,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
|
||||
expectedInteractionEvent = InteractionEvent.UNCHANGED,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_UP,
|
||||
previousPointerOnSensorId = POINTER_ID,
|
||||
isGoodOverlap = false,
|
||||
expectedInteractionEvent = InteractionEvent.UP,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
// MotionEvent.ACTION_CANCEL
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_CANCEL,
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
isGoodOverlap = true,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
|
||||
expectedInteractionEvent = InteractionEvent.CANCEL,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_CANCEL,
|
||||
previousPointerOnSensorId = POINTER_ID,
|
||||
isGoodOverlap = true,
|
||||
previousPointerOnSensorId = POINTER_ID_1,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = true)),
|
||||
expectedInteractionEvent = InteractionEvent.CANCEL,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_CANCEL,
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
isGoodOverlap = false,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
|
||||
expectedInteractionEvent = InteractionEvent.CANCEL,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_CANCEL,
|
||||
previousPointerOnSensorId = POINTER_ID,
|
||||
isGoodOverlap = false,
|
||||
previousPointerOnSensorId = POINTER_ID_1,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = false)),
|
||||
expectedInteractionEvent = InteractionEvent.CANCEL,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID,
|
||||
),
|
||||
// MotionEvent.ACTION_POINTER_DOWN
|
||||
genPositiveTestCases(
|
||||
motionEventAction =
|
||||
MotionEvent.ACTION_POINTER_DOWN +
|
||||
(1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
currentPointers =
|
||||
listOf(
|
||||
TestPointer(id = POINTER_ID_1, onSensor = true),
|
||||
TestPointer(id = POINTER_ID_2, onSensor = false)
|
||||
),
|
||||
expectedInteractionEvent = InteractionEvent.DOWN,
|
||||
expectedPointerOnSensorId = POINTER_ID_1,
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction =
|
||||
MotionEvent.ACTION_POINTER_DOWN +
|
||||
(1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
currentPointers =
|
||||
listOf(
|
||||
TestPointer(id = POINTER_ID_1, onSensor = false),
|
||||
TestPointer(id = POINTER_ID_2, onSensor = true)
|
||||
),
|
||||
expectedInteractionEvent = InteractionEvent.DOWN,
|
||||
expectedPointerOnSensorId = POINTER_ID_2
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction =
|
||||
MotionEvent.ACTION_POINTER_DOWN +
|
||||
(1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
|
||||
previousPointerOnSensorId = POINTER_ID_1,
|
||||
currentPointers =
|
||||
listOf(
|
||||
TestPointer(id = POINTER_ID_1, onSensor = true),
|
||||
TestPointer(id = POINTER_ID_2, onSensor = false)
|
||||
),
|
||||
expectedInteractionEvent = InteractionEvent.UNCHANGED,
|
||||
expectedPointerOnSensorId = POINTER_ID_1,
|
||||
),
|
||||
// MotionEvent.ACTION_POINTER_UP
|
||||
genPositiveTestCases(
|
||||
motionEventAction =
|
||||
MotionEvent.ACTION_POINTER_UP +
|
||||
(1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
|
||||
previousPointerOnSensorId = INVALID_POINTER_ID,
|
||||
currentPointers =
|
||||
listOf(
|
||||
TestPointer(id = POINTER_ID_1, onSensor = false),
|
||||
TestPointer(id = POINTER_ID_2, onSensor = false)
|
||||
),
|
||||
expectedInteractionEvent = InteractionEvent.UNCHANGED,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction =
|
||||
MotionEvent.ACTION_POINTER_UP +
|
||||
(1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
|
||||
previousPointerOnSensorId = POINTER_ID_2,
|
||||
currentPointers =
|
||||
listOf(
|
||||
TestPointer(id = POINTER_ID_1, onSensor = false),
|
||||
TestPointer(id = POINTER_ID_2, onSensor = true)
|
||||
),
|
||||
expectedInteractionEvent = InteractionEvent.UP,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_POINTER_UP,
|
||||
previousPointerOnSensorId = POINTER_ID_1,
|
||||
currentPointers =
|
||||
listOf(
|
||||
TestPointer(id = POINTER_ID_1, onSensor = true),
|
||||
TestPointer(id = POINTER_ID_2, onSensor = false)
|
||||
),
|
||||
expectedInteractionEvent = InteractionEvent.UP,
|
||||
expectedPointerOnSensorId = INVALID_POINTER_ID
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction =
|
||||
MotionEvent.ACTION_POINTER_UP +
|
||||
(1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT),
|
||||
previousPointerOnSensorId = POINTER_ID_1,
|
||||
currentPointers =
|
||||
listOf(
|
||||
TestPointer(id = POINTER_ID_1, onSensor = true),
|
||||
TestPointer(id = POINTER_ID_2, onSensor = false)
|
||||
),
|
||||
expectedInteractionEvent = InteractionEvent.UNCHANGED,
|
||||
expectedPointerOnSensorId = POINTER_ID_1
|
||||
),
|
||||
genPositiveTestCases(
|
||||
motionEventAction = MotionEvent.ACTION_POINTER_UP,
|
||||
previousPointerOnSensorId = POINTER_ID_2,
|
||||
currentPointers =
|
||||
listOf(
|
||||
TestPointer(id = POINTER_ID_1, onSensor = false),
|
||||
TestPointer(id = POINTER_ID_2, onSensor = true)
|
||||
),
|
||||
expectedInteractionEvent = InteractionEvent.UNCHANGED,
|
||||
expectedPointerOnSensorId = POINTER_ID_2
|
||||
)
|
||||
)
|
||||
.flatten() +
|
||||
listOf(
|
||||
// Unsupported MotionEvent actions.
|
||||
genTestCasesForUnsupportedAction(MotionEvent.ACTION_POINTER_DOWN),
|
||||
genTestCasesForUnsupportedAction(MotionEvent.ACTION_POINTER_UP),
|
||||
genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_ENTER),
|
||||
genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_MOVE),
|
||||
genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_EXIT),
|
||||
genTestCasesForUnsupportedAction(MotionEvent.ACTION_HOVER_EXIT)
|
||||
)
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
data class TestPointer(val id: Int, val onSensor: Boolean)
|
||||
|
||||
/* Display dimensions in native resolution and natural orientation. */
|
||||
private const val ROTATION_0_NATIVE_DISPLAY_WIDTH = 400
|
||||
private const val ROTATION_0_NATIVE_DISPLAY_HEIGHT = 600
|
||||
|
||||
/* Placeholder touch parameters. */
|
||||
private const val POINTER_ID = 42
|
||||
private const val POINTER_ID_1 = 42
|
||||
private const val POINTER_ID_2 = 43
|
||||
private const val NATIVE_MINOR = 2.71828f
|
||||
private const val NATIVE_MAJOR = 3.14f
|
||||
private const val ORIENTATION = 1.2345f
|
||||
@@ -325,7 +436,7 @@ private val ROTATION_270_INPUTS =
|
||||
private val MOTION_EVENT =
|
||||
obtainMotionEvent(
|
||||
action = 0,
|
||||
pointerId = POINTER_ID,
|
||||
pointerId = POINTER_ID_1,
|
||||
x = 0f,
|
||||
y = 0f,
|
||||
minor = 0f,
|
||||
@@ -338,7 +449,7 @@ private val MOTION_EVENT =
|
||||
/* Template [NormalizedTouchData]. */
|
||||
private val NORMALIZED_TOUCH_DATA =
|
||||
NormalizedTouchData(
|
||||
POINTER_ID,
|
||||
POINTER_ID_1,
|
||||
x = 0f,
|
||||
y = 0f,
|
||||
NATIVE_MINOR,
|
||||
@@ -384,7 +495,7 @@ private data class OrientationBasedInputs(
|
||||
private fun genPositiveTestCases(
|
||||
motionEventAction: Int,
|
||||
previousPointerOnSensorId: Int,
|
||||
isGoodOverlap: Boolean,
|
||||
currentPointers: List<TestPointer>,
|
||||
expectedInteractionEvent: InteractionEvent,
|
||||
expectedPointerOnSensorId: Int
|
||||
): List<SinglePointerTouchProcessorTest.TestCase> {
|
||||
@@ -399,22 +510,47 @@ private fun genPositiveTestCases(
|
||||
return scaleFactors.flatMap { scaleFactor ->
|
||||
orientations.map { orientation ->
|
||||
val overlayParams = orientation.toOverlayParams(scaleFactor)
|
||||
val nativeX = orientation.getNativeX(isGoodOverlap)
|
||||
val nativeY = orientation.getNativeY(isGoodOverlap)
|
||||
|
||||
val pointerProperties =
|
||||
currentPointers
|
||||
.map { pointer ->
|
||||
val pp = MotionEvent.PointerProperties()
|
||||
pp.id = pointer.id
|
||||
pp
|
||||
}
|
||||
.toList()
|
||||
|
||||
val pointerCoords =
|
||||
currentPointers
|
||||
.map { pointer ->
|
||||
val pc = MotionEvent.PointerCoords()
|
||||
pc.x = orientation.getNativeX(pointer.onSensor) * scaleFactor
|
||||
pc.y = orientation.getNativeY(pointer.onSensor) * scaleFactor
|
||||
pc.touchMinor = NATIVE_MINOR * scaleFactor
|
||||
pc.touchMajor = NATIVE_MAJOR * scaleFactor
|
||||
pc.orientation = orientation.nativeOrientation
|
||||
pc
|
||||
}
|
||||
.toList()
|
||||
|
||||
val event =
|
||||
MOTION_EVENT.copy(
|
||||
action = motionEventAction,
|
||||
x = nativeX * scaleFactor,
|
||||
y = nativeY * scaleFactor,
|
||||
minor = NATIVE_MINOR * scaleFactor,
|
||||
major = NATIVE_MAJOR * scaleFactor,
|
||||
orientation = orientation.nativeOrientation
|
||||
pointerProperties = pointerProperties,
|
||||
pointerCoords = pointerCoords
|
||||
)
|
||||
|
||||
val expectedTouchData =
|
||||
NORMALIZED_TOUCH_DATA.copy(
|
||||
x = ROTATION_0_INPUTS.getNativeX(isGoodOverlap),
|
||||
y = ROTATION_0_INPUTS.getNativeY(isGoodOverlap),
|
||||
)
|
||||
if (expectedPointerOnSensorId != INVALID_POINTER_ID) {
|
||||
NORMALIZED_TOUCH_DATA.copy(
|
||||
pointerId = expectedPointerOnSensorId,
|
||||
x = ROTATION_0_INPUTS.getNativeX(isWithinSensor = true),
|
||||
y = ROTATION_0_INPUTS.getNativeY(isWithinSensor = true)
|
||||
)
|
||||
} else {
|
||||
NormalizedTouchData()
|
||||
}
|
||||
|
||||
val expected =
|
||||
TouchProcessorResult.ProcessedTouch(
|
||||
event = expectedInteractionEvent,
|
||||
@@ -423,7 +559,7 @@ private fun genPositiveTestCases(
|
||||
)
|
||||
SinglePointerTouchProcessorTest.TestCase(
|
||||
event = event,
|
||||
isGoodOverlap = isGoodOverlap,
|
||||
currentPointers = currentPointers,
|
||||
previousPointerOnSensorId = previousPointerOnSensorId,
|
||||
overlayParams = overlayParams,
|
||||
expected = expected,
|
||||
@@ -436,7 +572,7 @@ private fun genTestCasesForUnsupportedAction(
|
||||
motionEventAction: Int
|
||||
): List<SinglePointerTouchProcessorTest.TestCase> {
|
||||
val isGoodOverlap = true
|
||||
val previousPointerOnSensorIds = listOf(INVALID_POINTER_ID, POINTER_ID)
|
||||
val previousPointerOnSensorIds = listOf(INVALID_POINTER_ID, POINTER_ID_1)
|
||||
return previousPointerOnSensorIds.map { previousPointerOnSensorId ->
|
||||
val overlayParams = ROTATION_0_INPUTS.toOverlayParams(scaleFactor = 1f)
|
||||
val nativeX = ROTATION_0_INPUTS.getNativeX(isGoodOverlap)
|
||||
@@ -451,7 +587,7 @@ private fun genTestCasesForUnsupportedAction(
|
||||
)
|
||||
SinglePointerTouchProcessorTest.TestCase(
|
||||
event = event,
|
||||
isGoodOverlap = isGoodOverlap,
|
||||
currentPointers = listOf(TestPointer(id = POINTER_ID_1, onSensor = isGoodOverlap)),
|
||||
previousPointerOnSensorId = previousPointerOnSensorId,
|
||||
overlayParams = overlayParams,
|
||||
expected = TouchProcessorResult.Failure(),
|
||||
@@ -478,13 +614,23 @@ private fun obtainMotionEvent(
|
||||
pc.touchMinor = minor
|
||||
pc.touchMajor = major
|
||||
pc.orientation = orientation
|
||||
return obtainMotionEvent(action, arrayOf(pp), arrayOf(pc), time, gestureStart)
|
||||
}
|
||||
|
||||
private fun obtainMotionEvent(
|
||||
action: Int,
|
||||
pointerProperties: Array<MotionEvent.PointerProperties>,
|
||||
pointerCoords: Array<MotionEvent.PointerCoords>,
|
||||
time: Long,
|
||||
gestureStart: Long,
|
||||
): MotionEvent {
|
||||
return MotionEvent.obtain(
|
||||
gestureStart /* downTime */,
|
||||
time /* eventTime */,
|
||||
action /* action */,
|
||||
1 /* pointerCount */,
|
||||
arrayOf(pp) /* pointerProperties */,
|
||||
arrayOf(pc) /* pointerCoords */,
|
||||
pointerCoords.size /* pointerCount */,
|
||||
pointerProperties /* pointerProperties */,
|
||||
pointerCoords /* pointerCoords */,
|
||||
0 /* metaState */,
|
||||
0 /* buttonState */,
|
||||
1f /* xPrecision */,
|
||||
@@ -508,4 +654,19 @@ private fun MotionEvent.copy(
|
||||
gestureStart: Long = this.downTime,
|
||||
) = obtainMotionEvent(action, pointerId, x, y, minor, major, orientation, time, gestureStart)
|
||||
|
||||
private fun MotionEvent.copy(
|
||||
action: Int = this.action,
|
||||
pointerProperties: List<MotionEvent.PointerProperties>,
|
||||
pointerCoords: List<MotionEvent.PointerCoords>,
|
||||
time: Long = this.eventTime,
|
||||
gestureStart: Long = this.downTime
|
||||
) =
|
||||
obtainMotionEvent(
|
||||
action,
|
||||
pointerProperties.toTypedArray(),
|
||||
pointerCoords.toTypedArray(),
|
||||
time,
|
||||
gestureStart
|
||||
)
|
||||
|
||||
private fun Rect.scaled(scaleFactor: Float) = Rect(this).apply { scale(scaleFactor) }
|
||||
|
||||
@@ -19,9 +19,10 @@ package com.android.systemui.biometrics.udfps
|
||||
import android.graphics.Rect
|
||||
|
||||
class FakeOverlapDetector : OverlapDetector {
|
||||
var shouldReturn: Boolean = false
|
||||
var shouldReturn: Map<Int, Boolean> = mapOf()
|
||||
|
||||
override fun isGoodOverlap(touchData: NormalizedTouchData, nativeSensorBounds: Rect): Boolean {
|
||||
return shouldReturn
|
||||
return shouldReturn[touchData.pointerId]
|
||||
?: error("Unexpected PointerId not declared in TestCase currentPointers")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user