55import org .junit .Test ;
66
77import static org .mockito .Mockito .doReturn ;
8+ import static org .mockito .Mockito .never ;
89import static org .mockito .Mockito .spy ;
910import static org .mockito .Mockito .times ;
1011import static org .mockito .Mockito .verify ;
12+ import static org .junit .Assert .assertFalse ;
13+ import static org .junit .Assert .assertTrue ;
1114
1215public class MoveGestureDetectorTest extends
1316 AbstractGestureDetectorTest <MoveGestureDetector , MoveGestureDetector .OnMoveGestureListener > {
@@ -66,4 +69,158 @@ public void analyzeEventTest() {
6669 gestureDetector , gestureDetector .velocityX , gestureDetector .velocityY
6770 );
6871 }
72+
73+ @ Test
74+ public void multiFingerMoveThreshold_fallsBackToMoveThreshold () {
75+ doReturn (true ).when (listener ).onMoveBegin (gestureDetector );
76+ doReturn (true ).when (gestureDetector ).checkPressure ();
77+ doReturn (false ).when (gestureDetector ).isSloppyGesture ();
78+ gestureDetector .setMoveThreshold (10f );
79+
80+ MotionEvent downEvent = TestUtils .INSTANCE .getMotionEvent (MotionEvent .ACTION_DOWN , 100 , 100 , null );
81+ gestureDetector .onTouchEvent (downEvent );
82+ MotionEvent pointerDownEvent = TestUtils .INSTANCE .getMotionEvent (
83+ MotionEvent .ACTION_POINTER_DOWN , 110 , 100 , downEvent );
84+ gestureDetector .onTouchEvent (pointerDownEvent );
85+ MotionEvent moveEvent = TestUtils .INSTANCE .getMotionEvent (MotionEvent .ACTION_MOVE , 122 , 100 , pointerDownEvent );
86+ gestureDetector .onTouchEvent (moveEvent );
87+
88+ verify (listener , times (1 )).onMoveBegin (gestureDetector );
89+ }
90+
91+ @ Test
92+ public void multiFingerMoveThreshold_overridesMoveThresholdForMultiFingerMoves () {
93+ doReturn (true ).when (listener ).onMoveBegin (gestureDetector );
94+ doReturn (true ).when (gestureDetector ).checkPressure ();
95+ doReturn (false ).when (gestureDetector ).isSloppyGesture ();
96+ gestureDetector .setMoveThreshold (10f );
97+ gestureDetector .setMultiFingerMoveThreshold (30f );
98+
99+ MotionEvent downEvent = TestUtils .INSTANCE .getMotionEvent (MotionEvent .ACTION_DOWN , 100 , 100 , null );
100+ gestureDetector .onTouchEvent (downEvent );
101+ MotionEvent pointerDownEvent = TestUtils .INSTANCE .getMotionEvent (
102+ MotionEvent .ACTION_POINTER_DOWN , 110 , 100 , downEvent );
103+ gestureDetector .onTouchEvent (pointerDownEvent );
104+
105+ MotionEvent moveBelowMultiThreshold = TestUtils .INSTANCE .getMotionEvent (
106+ MotionEvent .ACTION_MOVE , 125 , 100 , pointerDownEvent );
107+ gestureDetector .onTouchEvent (moveBelowMultiThreshold );
108+ verify (listener , never ()).onMoveBegin (gestureDetector );
109+
110+ MotionEvent moveAboveMultiThreshold = TestUtils .INSTANCE .getMotionEvent (
111+ MotionEvent .ACTION_MOVE , 145 , 100 , moveBelowMultiThreshold );
112+ gestureDetector .onTouchEvent (moveAboveMultiThreshold );
113+ verify (listener , times (1 )).onMoveBegin (gestureDetector );
114+ }
115+
116+ @ Test
117+ public void checkAnyMoveAboveThreshold_checksAllPointers () {
118+ doReturn (true ).when (gestureDetector ).checkPressure ();
119+ doReturn (false ).when (gestureDetector ).isSloppyGesture ();
120+ gestureDetector .setMoveThreshold (10f );
121+
122+ MotionEvent downEvent = TestUtils .INSTANCE .getMotionEvent (
123+ MotionEvent .ACTION_DOWN ,
124+ new float []{0f },
125+ new float []{0f },
126+ 0 ,
127+ null
128+ );
129+ gestureDetector .onTouchEvent (downEvent );
130+
131+ MotionEvent pointerDownEvent = TestUtils .INSTANCE .getMotionEvent (
132+ MotionEvent .ACTION_POINTER_DOWN ,
133+ new float []{0f , 100f },
134+ new float []{0f , 0f },
135+ 1 ,
136+ downEvent
137+ );
138+ gestureDetector .onTouchEvent (pointerDownEvent );
139+
140+ MotionEvent moveEvent = TestUtils .INSTANCE .getMotionEvent (
141+ MotionEvent .ACTION_MOVE ,
142+ new float []{5f , 120f },
143+ new float []{0f , 0f },
144+ 0 ,
145+ pointerDownEvent
146+ );
147+ gestureDetector .onTouchEvent (moveEvent );
148+
149+ assertTrue ("Any pointer above threshold should start move gesture" ,
150+ gestureDetector .checkAnyMoveAboveThreshold ());
151+ }
152+
153+ /**
154+ * Regression test: moving 400px with 2 fingers should NOT trigger onMoveBegin for single-finger
155+ * after one finger is lifted, even though 400px exceeds the single-finger threshold (100px).
156+ * The distances must be reset when the pointer configuration changes.
157+ */
158+ @ Test
159+ public void noFalseBeginOnPointerUp_distancesResetAfterLiftingFinger () {
160+ doReturn (true ).when (listener ).onMoveBegin (gestureDetector );
161+ doReturn (true ).when (gestureDetector ).checkPressure ();
162+ doReturn (false ).when (gestureDetector ).isSloppyGesture ();
163+
164+ // single-finger threshold: 100px, multi-finger threshold: 500px
165+ gestureDetector .setMoveThreshold (100f );
166+ gestureDetector .setMultiFingerMoveThreshold (500f );
167+
168+ // Put first finger down
169+ MotionEvent downEvent = TestUtils .INSTANCE .getMotionEvent (MotionEvent .ACTION_DOWN , 100 , 100 , null );
170+ gestureDetector .onTouchEvent (downEvent );
171+
172+ // Put second finger down
173+ MotionEvent pointerDownEvent = TestUtils .INSTANCE .getMotionEvent (
174+ MotionEvent .ACTION_POINTER_DOWN , 200 , 100 , downEvent );
175+ gestureDetector .onTouchEvent (pointerDownEvent );
176+
177+ // Move 400px — below multi-finger threshold (500px), so no onMoveBegin yet
178+ MotionEvent moveEvent = TestUtils .INSTANCE .getMotionEvent (MotionEvent .ACTION_MOVE , 500 , 100 , pointerDownEvent );
179+ gestureDetector .onTouchEvent (moveEvent );
180+ verify (listener , never ()).onMoveBegin (gestureDetector );
181+
182+ // Lift one finger — the remaining pointer's accumulated distance must be reset.
183+ // After the pointer-up, checkAnyMoveAboveThreshold() must return false because distances
184+ // were reset, so a subsequent MOVE event does NOT cross the single-finger threshold.
185+ MotionEvent pointerUpEvent = TestUtils .INSTANCE .getMotionEvent (MotionEvent .ACTION_POINTER_UP , 500 , 100 , moveEvent );
186+ gestureDetector .onTouchEvent (pointerUpEvent );
187+
188+ // checkAnyMoveAboveThreshold() should now return false since distances were reset
189+ assertFalse ("Distances should be reset after lifting a finger" ,
190+ gestureDetector .checkAnyMoveAboveThreshold ());
191+ }
192+
193+ /**
194+ * Regression test: moving 400px with 1 finger should NOT trigger onMoveBegin for multi-finger
195+ * right after a second finger is added, even though 400px exceeds the single-finger threshold.
196+ * The distances of existing pointers must be reset when a new pointer is added.
197+ */
198+ @ Test
199+ public void noFalseBeginOnPointerDown_distancesResetAfterAddingFinger () {
200+ doReturn (true ).when (listener ).onMoveBegin (gestureDetector );
201+ doReturn (true ).when (gestureDetector ).checkPressure ();
202+ doReturn (false ).when (gestureDetector ).isSloppyGesture ();
203+
204+ // single-finger threshold: 500px, multi-finger threshold: 100px
205+ gestureDetector .setMoveThreshold (500f );
206+ gestureDetector .setMultiFingerMoveThreshold (100f );
207+
208+ // Put first finger down and move 400px — below single-finger threshold (500px), no begin yet
209+ MotionEvent downEvent = TestUtils .INSTANCE .getMotionEvent (MotionEvent .ACTION_DOWN , 100 , 100 , null );
210+ gestureDetector .onTouchEvent (downEvent );
211+ MotionEvent moveEvent = TestUtils .INSTANCE .getMotionEvent (MotionEvent .ACTION_MOVE , 500 , 100 , downEvent );
212+ gestureDetector .onTouchEvent (moveEvent );
213+ verify (listener , never ()).onMoveBegin (gestureDetector );
214+
215+ // Add a second finger — existing pointer's accumulated distance must be reset.
216+ // After the pointer-down, checkAnyMoveAboveThreshold() must return false because distances
217+ // were reset, so a subsequent MOVE event does NOT immediately cross the multi-finger threshold.
218+ MotionEvent pointerDownEvent = TestUtils .INSTANCE .getMotionEvent (
219+ MotionEvent .ACTION_POINTER_DOWN , 500 , 100 , moveEvent );
220+ gestureDetector .onTouchEvent (pointerDownEvent );
221+
222+ // checkAnyMoveAboveThreshold() should now return false since distances were reset
223+ assertFalse ("Distances should be reset after adding a finger" ,
224+ gestureDetector .checkAnyMoveAboveThreshold ());
225+ }
69226}
0 commit comments