Skip to content

Commit 682e710

Browse files
author
Łukasz Paczos
committed
expose a MoveGestureDetector#moveThresholdRect
When set, the defined screen area prohibits move gesture to be started. If the gesture is already in progress, this value is ignored.
1 parent ac3a194 commit 682e710

4 files changed

Lines changed: 167 additions & 14 deletions

File tree

app/src/androidTest/java/com/mapbox/android/gestures/GesturesUiTestUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ object GesturesUiTestUtils {
458458
pointerCoords, 0, 0, 1f, 1f, 0, 0, 0, 0)
459459
injectMotionEventToUiController(uiController, event)
460460
} catch (ex: InjectEventSecurityException) {
461-
throw RuntimeException("Could not perform quick scale", ex)
461+
throw RuntimeException("Could not perform move", ex)
462462
}
463463
}
464464

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package com.mapbox.android.gestures
2+
3+
import GesturesUiTestUtils.DEFAULT_GESTURE_DURATION
4+
import GesturesUiTestUtils.move
5+
import android.graphics.PointF
6+
import android.graphics.RectF
7+
import androidx.test.espresso.Espresso
8+
import androidx.test.espresso.matcher.ViewMatchers
9+
import androidx.test.ext.junit.runners.AndroidJUnit4
10+
import androidx.test.rule.ActivityTestRule
11+
import com.mapbox.android.gestures.testapp.R
12+
import com.mapbox.android.gestures.testapp.TestActivity
13+
import org.junit.Assert
14+
import org.junit.Before
15+
import org.junit.Rule
16+
import org.junit.Test
17+
import org.junit.runner.RunWith
18+
import java.util.concurrent.CountDownLatch
19+
import java.util.concurrent.TimeUnit
20+
21+
@RunWith(AndroidJUnit4::class)
22+
class MoveGestureDetectorTest {
23+
24+
@Rule
25+
@JvmField
26+
val activityTestRule = ActivityTestRule(TestActivity::class.java)
27+
28+
private lateinit var gesturesManager: AndroidGesturesManager
29+
30+
@Before
31+
fun setup() {
32+
gesturesManager = activityTestRule.activity.gesturesManager
33+
}
34+
35+
@Test
36+
fun move_ignoredWithRectThreshold() {
37+
val rect = RectF(400f, 400f, 600f, 600f)
38+
gesturesManager.setMoveGestureListener(object : MoveGestureDetector.OnMoveGestureListener {
39+
override fun onMoveBegin(detector: MoveGestureDetector) = true
40+
41+
override fun onMove(
42+
detector: MoveGestureDetector,
43+
distanceX: Float,
44+
distanceY: Float
45+
): Boolean = throw AssertionError("onMove shouldn't be called if threshold was not met")
46+
47+
override fun onMoveEnd(detector: MoveGestureDetector, velocityX: Float, velocityY: Float) = Unit
48+
49+
})
50+
gesturesManager.moveGestureDetector.moveThresholdRect = rect
51+
Espresso.onView(ViewMatchers.withId(R.id.content)).perform(
52+
move(
53+
deltaX = 50f,
54+
deltaY = 50f,
55+
startPoint = PointF(rect.right - 100f, rect.bottom - 100f)
56+
)
57+
)
58+
}
59+
60+
@Test
61+
fun move_executedWhenOutsideOfRect() {
62+
val latch = CountDownLatch(1)
63+
val rect = RectF(400f, 400f, 600f, 600f)
64+
gesturesManager.setMoveGestureListener(object : MoveGestureDetector.OnMoveGestureListener {
65+
override fun onMoveBegin(detector: MoveGestureDetector) = true
66+
67+
override fun onMove(
68+
detector: MoveGestureDetector,
69+
distanceX: Float,
70+
distanceY: Float
71+
): Boolean {
72+
Assert.assertFalse(rect.contains(detector.focalPoint.x, detector.focalPoint.y))
73+
latch.countDown()
74+
return true
75+
}
76+
77+
override fun onMoveEnd(detector: MoveGestureDetector, velocityX: Float, velocityY: Float) = Unit
78+
79+
})
80+
gesturesManager.moveGestureDetector.moveThresholdRect = rect
81+
Espresso.onView(ViewMatchers.withId(R.id.content)).perform(
82+
move(
83+
deltaX = 100f,
84+
deltaY = 100f,
85+
startPoint = PointF(rect.right + 50f, rect.bottom + 50f)
86+
)
87+
)
88+
if (!latch.await(DEFAULT_GESTURE_DURATION, TimeUnit.MILLISECONDS)) {
89+
Assert.fail("move was not called")
90+
}
91+
}
92+
93+
@Test
94+
fun move_executedWhenRectThresholdMet() {
95+
val latch = CountDownLatch(1)
96+
val rect = RectF(400f, 400f, 600f, 600f)
97+
gesturesManager.setMoveGestureListener(object : MoveGestureDetector.OnMoveGestureListener {
98+
override fun onMoveBegin(detector: MoveGestureDetector) = true
99+
100+
override fun onMove(
101+
detector: MoveGestureDetector,
102+
distanceX: Float,
103+
distanceY: Float
104+
): Boolean {
105+
Assert.assertFalse(rect.contains(detector.focalPoint.x, detector.focalPoint.y))
106+
latch.countDown()
107+
return true
108+
}
109+
110+
override fun onMoveEnd(detector: MoveGestureDetector, velocityX: Float, velocityY: Float) = Unit
111+
112+
})
113+
gesturesManager.moveGestureDetector.moveThresholdRect = rect
114+
Espresso.onView(ViewMatchers.withId(R.id.content)).perform(
115+
move(
116+
deltaX = -150f,
117+
deltaY = -150f,
118+
startPoint = PointF(500f, 500f)
119+
)
120+
)
121+
if (!latch.await(DEFAULT_GESTURE_DURATION, TimeUnit.MILLISECONDS)) {
122+
Assert.fail("move was not called")
123+
}
124+
}
125+
}

app/src/androidTest/java/com/mapbox/android/gestures/ScaleGestureDetectorTest.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import android.os.Build
88
import android.os.Handler
99
import androidx.test.espresso.Espresso.onView
1010
import androidx.test.espresso.matcher.ViewMatchers.withId
11-
import androidx.test.rule.ActivityTestRule
1211
import androidx.test.ext.junit.runners.AndroidJUnit4
12+
import androidx.test.rule.ActivityTestRule
1313
import com.mapbox.android.gestures.testapp.R
1414
import com.mapbox.android.gestures.testapp.TestActivity
1515
import org.junit.Assert
@@ -449,11 +449,9 @@ class ScaleGestureDetectorTest {
449449
override fun onScaleEnd(detector: StandardScaleGestureDetector, velocityX: Float, velocityY: Float) {
450450
endInvocations[0]++
451451
gesturesManager.setMoveGestureListener(object : MoveGestureDetector.OnMoveGestureListener {
452-
override fun onMoveBegin(detector: MoveGestureDetector?): Boolean {
453-
return true
454-
}
452+
override fun onMoveBegin(detector: MoveGestureDetector) = true
455453

456-
override fun onMove(detector: MoveGestureDetector?, distanceX: Float, distanceY: Float): Boolean {
454+
override fun onMove(detector: MoveGestureDetector, distanceX: Float, distanceY: Float): Boolean {
457455
if (endInvocations[0] == 1) {
458456
moveAfterInterruption = true
459457
} else {
@@ -462,8 +460,7 @@ class ScaleGestureDetectorTest {
462460
return true
463461
}
464462

465-
override fun onMoveEnd(detector: MoveGestureDetector?, velocityX: Float, velocityY: Float) {
466-
}
463+
override fun onMoveEnd(detector: MoveGestureDetector, velocityX: Float, velocityY: Float) {}
467464
})
468465
}
469466
})
@@ -615,7 +612,6 @@ class ScaleGestureDetectorTest {
615612
}
616613
})
617614

618-
onView(withId(R.id.content)).perform(quickScale(gesturesManager.standardScaleGestureDetector.spanSinceStartThreshold / 2, withVelocity = false, duration = 50L))
619615
onView(withId(R.id.content)).perform(move(300f, 300f, withVelocity = false))
620616
}
621617
}

library/src/main/java/com/mapbox/android/gestures/MoveGestureDetector.java

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
import android.content.Context;
44
import android.graphics.PointF;
5+
import android.graphics.RectF;
6+
import android.view.MotionEvent;
7+
58
import androidx.annotation.DimenRes;
69
import androidx.annotation.NonNull;
10+
import androidx.annotation.Nullable;
711
import androidx.annotation.UiThread;
8-
import android.view.MotionEvent;
912

1013
import java.util.HashMap;
1114
import java.util.HashSet;
@@ -38,7 +41,10 @@ public class MoveGestureDetector extends ProgressiveGesture<MoveGestureDetector.
3841
handledTypes.add(GESTURE_TYPE_MOVE);
3942
}
4043

44+
@Nullable
45+
private RectF moveThresholdRect;
4146
private float moveThreshold;
47+
4248
private final Map<Integer, MoveDistancesObject> moveDistancesObjectMap = new HashMap<>();
4349

4450
public MoveGestureDetector(Context context, AndroidGesturesManager gesturesManager) {
@@ -172,10 +178,11 @@ private void updateMoveDistancesObjects() {
172178

173179
boolean checkAnyMoveAboveThreshold() {
174180
for (MoveDistancesObject moveDistancesObject : moveDistancesObjectMap.values()) {
175-
if (Math.abs(moveDistancesObject.getDistanceXSinceStart()) >= moveThreshold
176-
|| Math.abs(moveDistancesObject.getDistanceYSinceStart()) >= moveThreshold) {
177-
return true;
178-
}
181+
boolean thresholdExceeded = Math.abs(moveDistancesObject.getDistanceXSinceStart()) >= moveThreshold
182+
|| Math.abs(moveDistancesObject.getDistanceYSinceStart()) >= moveThreshold;
183+
184+
boolean isInRect = moveThresholdRect != null && moveThresholdRect.contains(getFocalPoint().x, getFocalPoint().y);
185+
return !isInRect && thresholdExceeded;
179186
}
180187
return false;
181188
}
@@ -205,6 +212,7 @@ protected int getRequiredPointersCount() {
205212
* Get the delta pixel threshold required to qualify it as a move gesture.
206213
*
207214
* @return delta pixel threshold
215+
* @see #getMoveThresholdRect()
208216
*/
209217
public float getMoveThreshold() {
210218
return moveThreshold;
@@ -216,11 +224,35 @@ public float getMoveThreshold() {
216224
* We encourage to set those values from dimens to accommodate for various screen sizes.
217225
*
218226
* @param moveThreshold delta threshold
227+
* @see #setMoveThresholdRect(RectF)
219228
*/
220229
public void setMoveThreshold(float moveThreshold) {
221230
this.moveThreshold = moveThreshold;
222231
}
223232

233+
/**
234+
* Get the screen area in which the move gesture cannot be started.
235+
* If the gesture is already in progress, this value is ignored.
236+
* This condition is evaluated before {@link #setMoveThreshold(float)}.
237+
*
238+
* @return the screen area in which the gesture cannot be started
239+
*/
240+
@Nullable
241+
public RectF getMoveThresholdRect() {
242+
return moveThresholdRect;
243+
}
244+
245+
/**
246+
* Set the screen area in which the move gesture cannot be started.
247+
* If the gesture is already in progress, this value is ignored.
248+
* This condition is evaluated before {@link #setMoveThreshold(float)}.
249+
*
250+
* @param moveThresholdRect the screen area in which the gesture cannot be started
251+
*/
252+
public void setMoveThresholdRect(@Nullable RectF moveThresholdRect) {
253+
this.moveThresholdRect = moveThresholdRect;
254+
}
255+
224256
/**
225257
* Set the delta dp threshold required to qualify it as a move gesture.
226258
*

0 commit comments

Comments
 (0)