Skip to content

Commit d79a263

Browse files
authored
Fix map drag detection bug (#6)
* Fix map drag detection bug * Update the library version
1 parent f855842 commit d79a263

File tree

7 files changed

+117
-7
lines changed

7 files changed

+117
-7
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Add to your `build.gradle.kts`:
1414

1515
```kotlin
1616
dependencies {
17-
implementation("de.afarber:openmapview:0.9.0")
17+
implementation("de.afarber:openmapview:0.11.0")
1818
}
1919
```
2020

docs/PERFORMANCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ HttpClient(Android) {
269269
All requests include a user-agent header as required by OSM tile usage policy:
270270

271271
```kotlin
272-
header("User-Agent", "OpenMapView/0.1.0 (https://github.com/afarber/OpenMapView)")
272+
header("User-Agent", "OpenMapView/0.11.0 (https://github.com/afarber/OpenMapView)")
273273
```
274274

275275
### Coroutine-Based Downloads

docs/REPLACING_GOOGLE_MAPS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ dependencies {
107107
```kotlin
108108
// Add to build.gradle.kts
109109
dependencies {
110-
implementation("de.afarber:openmapview:0.6.0")
110+
implementation("de.afarber:openmapview:0.11.0")
111111
}
112112
```
113113

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright (c) 2025 Alexander Farber
3+
* SPDX-License-Identifier: MIT
4+
*
5+
* This file is part of the OpenMapView project (https://github.com/afarber/OpenMapView)
6+
*/
7+
8+
package de.afarber.openmapview
9+
10+
import android.view.MotionEvent
11+
import androidx.test.ext.junit.runners.AndroidJUnit4
12+
import androidx.test.platform.app.InstrumentationRegistry
13+
import org.junit.Assert.assertFalse
14+
import org.junit.Assert.assertTrue
15+
import org.junit.Before
16+
import org.junit.Test
17+
import org.junit.runner.RunWith
18+
19+
@RunWith(AndroidJUnit4::class)
20+
class TouchGestureInstrumentationTest {
21+
private lateinit var openMapView: OpenMapView
22+
private lateinit var controller: MapController
23+
24+
@Before
25+
fun setUp() {
26+
val context = InstrumentationRegistry.getInstrumentation().targetContext
27+
28+
// Create and setup view on main thread to avoid Handler creation issues
29+
InstrumentationRegistry.getInstrumentation().runOnMainSync {
30+
openMapView = OpenMapView(context)
31+
openMapView.layout(0, 0, 1080, 1920)
32+
}
33+
34+
controller = openMapView.getMapControllerForTesting()
35+
controller.setCenter(LatLng(51.4661, 7.2491))
36+
controller.setZoom(10.0f)
37+
}
38+
39+
@Test
40+
fun testLongDrag_ShouldNotRegisterAsClick() {
41+
var clickReceived = false
42+
openMapView.setOnMapClickListener { clickReceived = true }
43+
44+
val downTime = System.currentTimeMillis()
45+
46+
// ACTION_DOWN at (100, 100)
47+
val down = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 100f, 100f, 0)
48+
openMapView.onTouchEvent(down)
49+
down.recycle()
50+
51+
// Simulate multiple MOVE events (drag 200 pixels)
52+
for (i in 1..10) {
53+
val move =
54+
MotionEvent.obtain(
55+
downTime,
56+
downTime + i * 10L,
57+
MotionEvent.ACTION_MOVE,
58+
100f + i * 20f,
59+
100f + i * 20f,
60+
0,
61+
)
62+
openMapView.onTouchEvent(move)
63+
move.recycle()
64+
}
65+
66+
// ACTION_UP at (300, 300) - 200 pixels away from initial position
67+
val up = MotionEvent.obtain(downTime, downTime + 150, MotionEvent.ACTION_UP, 300f, 300f, 0)
68+
openMapView.onTouchEvent(up)
69+
up.recycle()
70+
71+
// Should NOT have registered as a click (distance 200px > 10px threshold)
72+
assertFalse("Long drag should not register as click", clickReceived)
73+
}
74+
75+
@Test
76+
fun testSmallMovement_ShouldRegisterAsClick() {
77+
var clickReceived = false
78+
openMapView.setOnMapClickListener { clickReceived = true }
79+
80+
val downTime = System.currentTimeMillis()
81+
82+
// ACTION_DOWN at (100, 100)
83+
val down = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 100f, 100f, 0)
84+
openMapView.onTouchEvent(down)
85+
down.recycle()
86+
87+
// Small movement (3 pixels)
88+
val move = MotionEvent.obtain(downTime, downTime + 50, MotionEvent.ACTION_MOVE, 103f, 103f, 0)
89+
openMapView.onTouchEvent(move)
90+
move.recycle()
91+
92+
// ACTION_UP at (103, 103) - only 3 pixels away
93+
val up = MotionEvent.obtain(downTime, downTime + 100, MotionEvent.ACTION_UP, 103f, 103f, 0)
94+
openMapView.onTouchEvent(up)
95+
up.recycle()
96+
97+
// Should have registered as a click (distance 3px < 10px threshold)
98+
assertTrue("Small movement should register as click", clickReceived)
99+
}
100+
101+
private fun OpenMapView.getMapControllerForTesting(): MapController {
102+
val field = OpenMapView::class.java.getDeclaredField("controller")
103+
field.isAccessible = true
104+
return field.get(this) as MapController
105+
}
106+
}

openmapview/src/main/kotlin/de/afarber/openmapview/OpenMapView.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class OpenMapView
6868
private var paddingBottom = 0
6969
private var lastTouchX = 0f
7070
private var lastTouchY = 0f
71+
private var initialTouchX = 0f
72+
private var initialTouchY = 0f
7173
private var onMapClickListener: OnMapClickListener? = null
7274
private var onMapLongClickListener: OnMapLongClickListener? = null
7375
private var onMarkerDragListener: OnMarkerDragListener? = null
@@ -154,6 +156,8 @@ class OpenMapView
154156
MotionEvent.ACTION_DOWN -> {
155157
lastTouchX = event.x
156158
lastTouchY = event.y
159+
initialTouchX = event.x
160+
initialTouchY = event.y
157161
// Check if touch is on a draggable marker
158162
val touchedMarker = controller.handleMarkerTouch(event.x, event.y)
159163
if (touchedMarker != null && touchedMarker.draggable) {
@@ -235,8 +239,8 @@ class OpenMapView
235239
isDragging = false
236240

237241
// Check if touch is on a marker (only if there was minimal movement)
238-
val dx = event.x - lastTouchX
239-
val dy = event.y - lastTouchY
242+
val dx = event.x - initialTouchX
243+
val dy = event.y - initialTouchY
240244
val movementDistance = kotlin.math.sqrt((dx * dx + dy * dy).toDouble())
241245

242246
if (movementDistance < 10) {

openmapview/src/main/kotlin/de/afarber/openmapview/TileDownloader.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class TileDownloader {
3333
try {
3434
val response =
3535
client.get(url) {
36-
header("User-Agent", "OpenMapView/0.9.0 (https://github.com/afarber/OpenMapView)")
36+
header("User-Agent", "OpenMapView/0.11.0 (https://github.com/afarber/OpenMapView)")
3737
}
3838
val bytes = response.readRawBytes()
3939
// Decode with RGB_565 to reduce memory usage (2 bytes per pixel vs 4 bytes for ARGB_8888)

openmapview/src/main/kotlin/de/afarber/openmapview/UrlTileProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ abstract class UrlTileProvider(
6262
*
6363
* @return User-Agent string
6464
*/
65-
protected open fun getUserAgent(): String = "OpenMapView/0.1.0 (https://github.com/afarber/OpenMapView)"
65+
protected open fun getUserAgent(): String = "OpenMapView/0.11.0 (https://github.com/afarber/OpenMapView)"
6666

6767
/**
6868
* Builds the URL for the specified tile coordinates by replacing placeholders

0 commit comments

Comments
 (0)