diff --git a/app/src/main/java/com/droiddeck/launcher/input/TwoFingerGestures.kt b/app/src/main/java/com/droiddeck/launcher/input/TwoFingerGestures.kt index 492713644..158b7a79e 100644 --- a/app/src/main/java/com/droiddeck/launcher/input/TwoFingerGestures.kt +++ b/app/src/main/java/com/droiddeck/launcher/input/TwoFingerGestures.kt @@ -26,7 +26,6 @@ class TwoFingerGestures(private val slopPx: Float, private val listener: Listene private var startSpan = 0f private var lastX = 0f private var lastY = 0f - private var anchored = false private val starts = HashMap>() private val held = ArrayList() private val hold = Runnable { @@ -85,7 +84,7 @@ class TwoFingerGestures(private val slopPx: Float, private val listener: Listene return true } State.SCROLLING -> { - anchored = false + anchor(event, -1) return true } State.HELD, State.TAPPED -> return true @@ -111,7 +110,6 @@ class TwoFingerGestures(private val slopPx: Float, private val listener: Listene listener.cancelTouch() lastX = midX(event) lastY = midY(event) - anchored = true listener.scrollStart(lastX, lastY) return true } @@ -122,8 +120,7 @@ class TwoFingerGestures(private val slopPx: Float, private val listener: Listene if (event.pointerCount >= 2) { val mx = midX(event) val my = midY(event) - if (anchored) listener.scroll(lastX - mx, lastY - my) - anchored = true + listener.scroll(lastX - mx, lastY - my) lastX = mx lastY = my } @@ -145,7 +142,7 @@ class TwoFingerGestures(private val slopPx: Float, private val listener: Listene return true } State.SCROLLING -> { - anchored = false + anchor(event, event.actionIndex) return true } State.HELD, State.TAPPED -> return true @@ -204,6 +201,13 @@ class TwoFingerGestures(private val slopPx: Float, private val listener: Listene hypot(event.getX(i) - start.first, event.getY(i) - start.second) > slopPx } + private fun anchor(event: MotionEvent, lifted: Int) { + val kept = (0 until event.pointerCount).filter { it != lifted } + if (kept.size < 2) return + lastX = (event.getX(kept[0]) + event.getX(kept[1])) / 2f + lastY = (event.getY(kept[0]) + event.getY(kept[1])) / 2f + } + private fun span(event: MotionEvent) = hypot(event.getX(1) - event.getX(0), event.getY(1) - event.getY(0)) private fun midX(event: MotionEvent) = (event.getX(0) + event.getX(1)) / 2f private fun midY(event: MotionEvent) = (event.getY(0) + event.getY(1)) / 2f diff --git a/app/src/test/java/com/droiddeck/launcher/input/TwoFingerGesturesTest.kt b/app/src/test/java/com/droiddeck/launcher/input/TwoFingerGesturesTest.kt index c55f6df49..4643f5e69 100644 --- a/app/src/test/java/com/droiddeck/launcher/input/TwoFingerGesturesTest.kt +++ b/app/src/test/java/com/droiddeck/launcher/input/TwoFingerGesturesTest.kt @@ -71,11 +71,23 @@ class TwoFingerGesturesTest { assertTrue(touch(MotionEvent.ACTION_POINTER_UP, 1, 1 to (400f to 260f), 2 to (500f to 260f))) assertTrue(touch(MotionEvent.ACTION_MOVE, 0, 1 to (400f to 250f))) assertTrue(touch(MotionEvent.ACTION_POINTER_DOWN, 1, 1 to (400f to 250f), 3 to (800f to 600f))) - assertTrue(touch(MotionEvent.ACTION_MOVE, 0, 1 to (400f to 250f), 3 to (800f to 600f))) assertTrue(touch(MotionEvent.ACTION_MOVE, 0, 1 to (400f to 230f), 3 to (800f to 580f))) assertEquals(listOf("cancel", "start 450.0,260.0", "scroll 0.0,20.0"), calls) } + @Test fun aThirdFingerLiftingDuringAScrollKeepsTheRemainingMidpoint() { + val c = 3 to (450f to 500f) + touch(MotionEvent.ACTION_DOWN, 0, a) + time += 20 + touch(MotionEvent.ACTION_POINTER_DOWN, 1, a, b) + time += 20 + touch(MotionEvent.ACTION_MOVE, 0, 1 to (400f to 260f), 2 to (500f to 260f)) + assertTrue(touch(MotionEvent.ACTION_POINTER_DOWN, 2, 1 to (400f to 260f), 2 to (500f to 260f), c)) + assertTrue(touch(MotionEvent.ACTION_POINTER_UP, 0, 1 to (400f to 260f), 2 to (500f to 260f), c)) + assertTrue(touch(MotionEvent.ACTION_MOVE, 0, 2 to (500f to 250f), c)) + assertEquals(listOf("cancel", "start 450.0,260.0", "scroll 0.0,5.0"), calls) + } + @Test fun pinchPassesThroughForZoom() { touch(MotionEvent.ACTION_DOWN, 0, a) time += 20