[multi-shade] Fix multi touch bug.

Dragging down with one finger, adding a second finger, then removing the
original finger was crashing because the pointer index for the saved off
pointer ID is missing.

This CL fixes it by seamlessly moving over to the next pointer when the
original pointer is lifted.

Bug: 274159734
Test: manually verified that (a) the crash doesn't happen and (b) the
shaded continues to drag down smoothly without a "jump".
Test: I considered a unit test but decided against it because it was going to be very hard to set up MotionEvent instances for different pointers based on the obtain API.

Change-Id: Idbc534660b947ce450aa819e1322eb934978de79
This commit is contained in:
Alejandro Nijamkin
2023-03-27 11:17:24 -07:00
parent f686be0b53
commit 17ced90e60

View File

@@ -182,6 +182,26 @@ constructor(
interactionState = null
true
}
MotionEvent.ACTION_POINTER_UP -> {
val removedPointerId = event.getPointerId(event.actionIndex)
if (removedPointerId == interactionState?.pointerId && event.pointerCount > 1) {
// We removed the original pointer but there must be another pointer because the
// gesture is still ongoing. Let's switch to that pointer.
interactionState =
event.firstUnremovedPointerId(removedPointerId)?.let { replacementPointerId
->
interactionState?.copy(
pointerId = replacementPointerId,
// We want to update the currentY of our state so that the
// transition to the next pointer doesn't report a big jump between
// the Y coordinate of the removed pointer and the Y coordinate of
// the replacement pointer.
currentY = event.getY(replacementPointerId),
)
}
}
true
}
MotionEvent.ACTION_CANCEL -> {
if (isDraggingShade()) {
// Our drag gesture was canceled by the system. This happens primarily in one of
@@ -219,4 +239,17 @@ constructor(
private fun isDraggingShade(): Boolean {
return interactionState?.isDraggingShade ?: false
}
/**
* Returns the index of the first pointer that is not [removedPointerId] or `null`, if there is
* no other pointer.
*/
private fun MotionEvent.firstUnremovedPointerId(removedPointerId: Int): Int? {
return (0 until pointerCount)
.firstOrNull { pointerIndex ->
val pointerId = getPointerId(pointerIndex)
pointerId != removedPointerId
}
?.let { pointerIndex -> getPointerId(pointerIndex) }
}
}