Skip to content

Commit 0d35644

Browse files
authored
Fix ConcurrentModificationException for detectors (#111)
* Fix concurrent modification exception for detectors * Construct detectors with pre-defined detectors * Add version to CHANGELOG.md * Fix codestyle * Convert CircleCI build job to GHA * Remove release from GHA main.yml and fix circle.yml
1 parent 60bcff9 commit 0d35644

6 files changed

Lines changed: 80 additions & 62 deletions

File tree

.github/workflows/main.yml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ '**' ]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
container:
13+
image: mbgl/android-sdk:latest
14+
steps:
15+
- name: Checkout repository
16+
uses: actions/checkout@v4
17+
18+
- name: Check code style
19+
run: make checkstyle
20+
21+
- name: Run Lint
22+
run: ./gradlew lint
23+
24+
- name: Run unit-test in Android libraries
25+
run: make test
26+
27+
- name: Upload reports
28+
uses: actions/upload-artifact@v4
29+
with:
30+
name: reports
31+
path: app/build/reports
32+
33+
- name: Upload test results
34+
uses: actions/upload-artifact@v4
35+
with:
36+
name: test-results
37+
path: app/build/test-results

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog for the Mapbox Gestures for Android
22

3+
## 0.9.2 - September 29, 2025
4+
* Fix concurrent modification exception for detectors
5+
36
## 0.9.1 - November 27, 2023
47
Minor release with internal fixes for publishing library
58

build.gradle

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@ buildscript {
55
mavenCentral()
66
maven {
77
url 'https://api.mapbox.com/downloads/v2/releases/maven'
8-
authentication {
9-
basic(BasicAuthentication)
10-
}
11-
credentials {
12-
username = "mapbox"
13-
password = System.getenv("SDK_REGISTRY_TOKEN") ?: project.property("SDK_REGISTRY_TOKEN") as String
14-
}
158
}
169
}
1710
dependencies {
@@ -28,13 +21,6 @@ allprojects {
2821
mavenCentral()
2922
maven {
3023
url 'https://api.mapbox.com/downloads/v2/releases/maven'
31-
authentication {
32-
basic(BasicAuthentication)
33-
}
34-
credentials {
35-
username = "mapbox"
36-
password = password = System.getenv("SDK_REGISTRY_TOKEN") ?: project.property("SDK_REGISTRY_TOKEN") as String
37-
}
3824
}
3925
}
4026
}

circle.yml

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ workflows:
44
version: 2
55
default:
66
jobs:
7-
- build
87
- release:
98
filters:
109
branches:
@@ -13,39 +12,6 @@ workflows:
1312
only: /^v.*/
1413

1514
jobs:
16-
build:
17-
working_directory: ~/code
18-
docker:
19-
- image: mbgl/android-ndk-r21e:latest
20-
environment:
21-
JVM_OPTS: -Xmx3200m
22-
steps:
23-
- checkout
24-
- restore_cache:
25-
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}-{{ checksum "library/build.gradle" }}-{{ checksum "gradle/dependencies.gradle" }}
26-
- run:
27-
name: Download Dependencies
28-
command: ./gradlew androidDependencies
29-
- save_cache:
30-
paths:
31-
- ~/.gradle
32-
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}-{{ checksum "library/build.gradle" }}-{{ checksum "gradle/dependencies.gradle" }}
33-
- run:
34-
name: Check code style
35-
command: make checkstyle
36-
- run:
37-
name: Run Lint
38-
command: ./gradlew lint
39-
- run:
40-
name: Run unit-test in Android libraries
41-
command: make test
42-
- store_artifacts:
43-
path: app/build/reports
44-
destination: reports
45-
- store_test_results:
46-
path: app/build/test-results
47-
48-
# ------------------------------------------------------------------------------
4915
release:
5016
docker:
5117
- image: mbgl/android-ndk-r21e:latest

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.Arrays;
1313
import java.util.List;
1414
import java.util.Set;
15+
import java.util.concurrent.CopyOnWriteArrayList;
1516

1617
/**
1718
* Entry point for all of the detectors. Set listener for gestures you'd like to be notified about
@@ -58,7 +59,7 @@ public class AndroidGesturesManager {
5859
public static final int GESTURE_TYPE_QUICK_SCALE = 15;
5960

6061
private final List<Set<Integer>> mutuallyExclusiveGestures = new ArrayList<>();
61-
private final List<BaseGesture> detectors = new ArrayList<>();
62+
private final List<BaseGesture> detectors;
6263

6364
private final StandardGestureDetector standardGestureDetector;
6465
private final StandardScaleGestureDetector standardScaleGestureDetector;
@@ -128,13 +129,15 @@ public AndroidGesturesManager(Context context, List<Set<Integer>> exclusiveGestu
128129
moveGestureDetector = new MoveGestureDetector(context, this);
129130
standardGestureDetector = new StandardGestureDetector(context, this);
130131

131-
detectors.add(rotateGestureDetector);
132-
detectors.add(standardScaleGestureDetector);
133-
detectors.add(shoveGestureDetector);
134-
detectors.add(sidewaysShoveGestureDetector);
135-
detectors.add(multiFingerTapGestureDetector);
136-
detectors.add(moveGestureDetector);
137-
detectors.add(standardGestureDetector);
132+
detectors = new CopyOnWriteArrayList<BaseGesture>(Arrays.asList(
133+
rotateGestureDetector,
134+
standardScaleGestureDetector,
135+
shoveGestureDetector,
136+
sidewaysShoveGestureDetector,
137+
multiFingerTapGestureDetector,
138+
moveGestureDetector,
139+
standardGestureDetector
140+
));
138141

139142
if (applyDefaultThresholds) {
140143
initDefaultThresholds();

library/src/test/java/com/mapbox/android/gestures/AndroidGesturesManagerTest.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package com.mapbox.android.gestures;
22

3+
import static junit.framework.Assert.assertEquals;
4+
import static junit.framework.Assert.assertNotNull;
5+
import static org.junit.Assert.assertTrue;
6+
7+
import android.view.MotionEvent;
8+
39
import org.junit.Before;
410
import org.junit.Test;
511
import org.junit.runner.RunWith;
@@ -11,9 +17,6 @@
1117
import java.util.List;
1218
import java.util.Set;
1319

14-
import static junit.framework.Assert.assertEquals;
15-
import static junit.framework.Assert.assertNotNull;
16-
1720
@RunWith(RobolectricTestRunner.class)
1821
public class AndroidGesturesManagerTest {
1922
private AndroidGesturesManager androidGesturesManager;
@@ -44,9 +47,9 @@ public void setUp() throws Exception {
4447
mutuallyExclusivesList.add(set3);
4548

4649
androidGesturesManager =
47-
new AndroidGesturesManager(
48-
RuntimeEnvironment.application.getApplicationContext(),
49-
mutuallyExclusivesList, true);
50+
new AndroidGesturesManager(
51+
RuntimeEnvironment.application.getApplicationContext(),
52+
mutuallyExclusivesList, true);
5053
}
5154

5255
@Test
@@ -69,4 +72,24 @@ public void setMutuallyExclusivesTest() throws Exception {
6972
androidGesturesManager.setMutuallyExclusiveGestures(mutuallyExclusivesList);
7073
assertEquals(androidGesturesManager.getMutuallyExclusiveGestures(), mutuallyExclusivesList);
7174
}
75+
76+
@Test
77+
public void onSingleTapModifyDetectorsTest() {
78+
final StandardGestureDetector standardGestureDetector = new StandardGestureDetector(
79+
RuntimeEnvironment.getApplication().getApplicationContext(),
80+
androidGesturesManager
81+
);
82+
83+
androidGesturesManager.setStandardGestureListener(new StandardGestureDetector.SimpleStandardOnGestureListener() {
84+
@Override
85+
public boolean onDown(MotionEvent e) {
86+
androidGesturesManager.getDetectors().add(standardGestureDetector);
87+
return true;
88+
}
89+
});
90+
91+
MotionEvent downEvent = TestUtils.INSTANCE.getMotionEvent(MotionEvent.ACTION_DOWN, 0, 0, null);
92+
androidGesturesManager.onTouchEvent(downEvent);
93+
assertTrue(androidGesturesManager.getDetectors().contains(standardGestureDetector));
94+
}
7295
}

0 commit comments

Comments
 (0)