From 6b535f70cee3646a998bd439e6427724b0136bcc Mon Sep 17 00:00:00 2001 From: paczos Date: Thu, 14 Dec 2017 15:45:54 +0100 Subject: [android] added map touch listeners api based on lists --- .../mapbox/mapboxsdk/maps/MapGestureDetector.java | 139 +++++++++++++++++---- .../java/com/mapbox/mapboxsdk/maps/MapView.java | 48 ++++++- .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 126 +++++++++++++++++-- .../mapboxsdk/maps/MapTouchListenersTest.java | 95 ++++++++++++++ 4 files changed, 372 insertions(+), 36 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java (limited to 'platform') diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 489199e422..6424de342e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -26,6 +26,8 @@ import com.mapbox.services.android.telemetry.MapboxTelemetry; import com.mapbox.services.android.telemetry.utils.MathUtils; import com.mapbox.services.android.telemetry.utils.TelemetryUtils; +import java.util.concurrent.CopyOnWriteArrayList; + import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE; /** @@ -43,16 +45,30 @@ final class MapGestureDetector { private final AnnotationManager annotationManager; private final CameraChangeDispatcher cameraChangeDispatcher; - private final GestureDetectorCompat gestureDetector; - private final ScaleGestureDetector scaleGestureDetector; - private final RotateGestureDetector rotateGestureDetector; - private final ShoveGestureDetector shoveGestureDetector; + private GestureDetectorCompat gestureDetector; + private ScaleGestureDetector scaleGestureDetector; + private RotateGestureDetector rotateGestureDetector; + private ShoveGestureDetector shoveGestureDetector; + // deprecated map touch API private MapboxMap.OnMapClickListener onMapClickListener; private MapboxMap.OnMapLongClickListener onMapLongClickListener; private MapboxMap.OnFlingListener onFlingListener; private MapboxMap.OnScrollListener onScrollListener; + // new map touch API + private final CopyOnWriteArrayList onMapClickListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList onMapLongClickListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList onFlingListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList onScrollListenerList + = new CopyOnWriteArrayList<>(); + private PointF focalPoint; private boolean twoTap; @@ -81,12 +97,14 @@ final class MapGestureDetector { this.cameraChangeDispatcher = cameraChangeDispatcher; // Touch gesture detectors - gestureDetector = new GestureDetectorCompat(context, new GestureListener()); - gestureDetector.setIsLongpressEnabled(true); - scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener()); - ScaleGestureDetectorCompat.setQuickScaleEnabled(scaleGestureDetector, true); - rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener()); - shoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener()); + if (context != null) { + gestureDetector = new GestureDetectorCompat(context, new GestureListener()); + gestureDetector.setIsLongpressEnabled(true); + scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener()); + ScaleGestureDetectorCompat.setQuickScaleEnabled(scaleGestureDetector, true); + rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener()); + shoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener()); + } } /** @@ -287,7 +305,6 @@ final class MapGestureDetector { return false; } - /** * Responsible for handling one finger gestures. */ @@ -355,10 +372,7 @@ final class MapGestureDetector { annotationManager.deselectMarkers(); } - // notify app of map click - if (onMapClickListener != null) { - onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); - } + notifyOnMapClickListeners(tapPoint); } MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( @@ -370,9 +384,10 @@ final class MapGestureDetector { @Override public void onLongPress(MotionEvent motionEvent) { - if (onMapLongClickListener != null && !quickZoom) { - onMapLongClickListener.onMapLongClick( - projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY()))); + PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY()); + + if (!quickZoom) { + notifyOnMapLongClickListeners(longClickPoint); } } @@ -412,9 +427,7 @@ final class MapGestureDetector { // update transformation transform.moveBy(offsetX, offsetY, animationTime); - if (onFlingListener != null) { - onFlingListener.onFling(); - } + notifyOnFlingListeners(); return true; } @@ -449,13 +462,59 @@ final class MapGestureDetector { // Scroll the map transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); - if (onScrollListener != null) { - onScrollListener.onScroll(); - } + notifyOnScrollListeners(); return true; } } + void notifyOnMapClickListeners(PointF tapPoint) { + // deprecated API + if (onMapClickListener != null) { + onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); + } + + // new API + for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) { + listener.onMapClick(projection.fromScreenLocation(tapPoint)); + } + } + + void notifyOnMapLongClickListeners(PointF longClickPoint) { + // deprecated API + if (onMapLongClickListener != null) { + onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); + } + + // new API + for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) { + listener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); + } + } + + void notifyOnFlingListeners() { + // deprecated API + if (onFlingListener != null) { + onFlingListener.onFling(); + } + + // new API + for (MapboxMap.OnFlingListener listener : onFlingListenerList) { + listener.onFling(); + } + } + + void notifyOnScrollListeners() { + //deprecated API + if (onScrollListener != null) { + onScrollListener.onScroll(); + } + + // new API + for (MapboxMap.OnScrollListener listener : onScrollListenerList) { + listener.onScroll(); + } + } + /** * Responsible for handling two finger gestures and double-tap drag gestures. */ @@ -844,4 +903,36 @@ final class MapGestureDetector { void setOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { this.onScrollListener = onScrollListener; } + + void addOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) { + onMapClickListenerList.add(onMapClickListener); + } + + void removeOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) { + onMapClickListenerList.remove(onMapClickListener); + } + + void addOnMapLongClickListener(MapboxMap.OnMapLongClickListener onMapLongClickListener) { + onMapLongClickListenerList.add(onMapLongClickListener); + } + + void removeOnMapLongClickListener(MapboxMap.OnMapLongClickListener onMapLongClickListener) { + onMapLongClickListenerList.remove(onMapLongClickListener); + } + + void addOnFlingListener(MapboxMap.OnFlingListener onFlingListener) { + onFlingListenerList.add(onFlingListener); + } + + void removeOnFlingListener(MapboxMap.OnFlingListener onFlingListener) { + onFlingListenerList.remove(onFlingListener); + } + + void addOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { + onScrollListenerList.add(onScrollListener); + } + + void removeOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { + onScrollListenerList.remove(onScrollListener); + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 80a3ce5bb3..77d2ac0e28 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -908,24 +908,64 @@ public class MapView extends FrameLayout { private class RegisterTouchListener implements MapboxMap.OnRegisterTouchListener { @Override - public void onRegisterMapClickListener(MapboxMap.OnMapClickListener listener) { + public void onSetMapClickListener(MapboxMap.OnMapClickListener listener) { mapGestureDetector.setOnMapClickListener(listener); } @Override - public void onRegisterMapLongClickListener(MapboxMap.OnMapLongClickListener listener) { + public void onAddMapClickListener(MapboxMap.OnMapClickListener listener) { + mapGestureDetector.addOnMapClickListener(listener); + } + + @Override + public void onRemoveMapClickListener(MapboxMap.OnMapClickListener listener) { + mapGestureDetector.removeOnMapClickListener(listener); + } + + @Override + public void onSetMapLongClickListener(MapboxMap.OnMapLongClickListener listener) { mapGestureDetector.setOnMapLongClickListener(listener); } @Override - public void onRegisterScrollListener(MapboxMap.OnScrollListener listener) { + public void onAddMapLongClickListener(MapboxMap.OnMapLongClickListener listener) { + mapGestureDetector.addOnMapLongClickListener(listener); + } + + @Override + public void onRemoveMapLongClickListener(MapboxMap.OnMapLongClickListener listener) { + mapGestureDetector.removeOnMapLongClickListener(listener); + } + + @Override + public void onSetScrollListener(MapboxMap.OnScrollListener listener) { mapGestureDetector.setOnScrollListener(listener); } @Override - public void onRegisterFlingListener(MapboxMap.OnFlingListener listener) { + public void onAddScrollListener(MapboxMap.OnScrollListener listener) { + mapGestureDetector.addOnScrollListener(listener); + } + + @Override + public void onRemoveScrollListener(MapboxMap.OnScrollListener listener) { + mapGestureDetector.removeOnScrollListener(listener); + } + + @Override + public void onSetFlingListener(MapboxMap.OnFlingListener listener) { mapGestureDetector.setOnFlingListener(listener); } + + @Override + public void onAddFlingListener(MapboxMap.OnFlingListener listener) { + mapGestureDetector.addOnFlingListener(listener); + } + + @Override + public void onRemoveFlingListener(MapboxMap.OnFlingListener listener) { + mapGestureDetector.removeOnFlingListener(listener); + } } private static class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index ad5a5a4c4a..4da2f63eeb 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -1897,9 +1897,34 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. + * + * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} instead. */ + @Deprecated public void setOnScrollListener(@Nullable OnScrollListener listener) { - onRegisterTouchListener.onRegisterScrollListener(listener); + onRegisterTouchListener.onSetScrollListener(listener); + } + + /** + * Adds a callback that's invoked when the map is scrolled. + * + * @param listener The callback that's invoked when the map is scrolled. + * To unset the callback, use null. + * + */ + public void addOnScrollListener(@Nullable OnScrollListener listener) { + onRegisterTouchListener.onAddScrollListener(listener); + } + + /** + * Removes a callback that's invoked when the map is scrolled. + * + * @param listener The callback that's invoked when the map is scrolled. + * To unset the callback, use null. + * + */ + public void removeOnScrollListener(@Nullable OnScrollListener listener) { + onRegisterTouchListener.onRemoveScrollListener(listener); } /** @@ -1907,9 +1932,32 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is flinged. * To unset the callback, use null. + * + * @deprecated Use {@link #addOnFlingListener(OnFlingListener)} instead. */ + @Deprecated public void setOnFlingListener(@Nullable OnFlingListener listener) { - onRegisterTouchListener.onRegisterFlingListener(listener); + onRegisterTouchListener.onSetFlingListener(listener); + } + + /** + * Adds a callback that's invoked when the map is flinged. + * + * @param listener The callback that's invoked when the map is flinged. + * To unset the callback, use null. + */ + public void addOnFlingListener(@Nullable OnFlingListener listener) { + onRegisterTouchListener.onAddFlingListener(listener); + } + + /** + * Removes a callback that's invoked when the map is flinged. + * + * @param listener The callback that's invoked when the map is flinged. + * To unset the callback, use null. + */ + public void removeOnFlingListener(@Nullable OnFlingListener listener) { + onRegisterTouchListener.onRemoveFlingListener(listener); } /** @@ -1917,9 +1965,32 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the user clicks on the map view. * To unset the callback, use null. + * + * @deprecated Use {@link #addOnMapClickListener(OnMapClickListener)} instead. */ + @Deprecated public void setOnMapClickListener(@Nullable OnMapClickListener listener) { - onRegisterTouchListener.onRegisterMapClickListener(listener); + onRegisterTouchListener.onSetMapClickListener(listener); + } + + /** + * Adds a callback that's invoked when the user clicks on the map view. + * + * @param listener The callback that's invoked when the user clicks on the map view. + * To unset the callback, use null. + */ + public void addOnMapClickListener(@Nullable OnMapClickListener listener) { + onRegisterTouchListener.onAddMapClickListener(listener); + } + + /** + * Removes a callback that's invoked when the user clicks on the map view. + * + * @param listener The callback that's invoked when the user clicks on the map view. + * To unset the callback, use null. + */ + public void removeOnMapClickListener(@Nullable OnMapClickListener listener) { + onRegisterTouchListener.onRemoveMapClickListener(listener); } /** @@ -1927,9 +1998,32 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the user long clicks on the map view. * To unset the callback, use null. + * + * @deprecated Use {@link #addOnMapLongClickListener(OnMapLongClickListener)} instead. */ + @Deprecated public void setOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { - onRegisterTouchListener.onRegisterMapLongClickListener(listener); + onRegisterTouchListener.onSetMapLongClickListener(listener); + } + + /** + * Adds a callback that's invoked when the user long clicks on the map view. + * + * @param listener The callback that's invoked when the user long clicks on the map view. + * To unset the callback, use null. + */ + public void addOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { + onRegisterTouchListener.onAddMapLongClickListener(listener); + } + + /** + * Removes a callback that's invoked when the user long clicks on the map view. + * + * @param listener The callback that's invoked when the user long clicks on the map view. + * To unset the callback, use null. + */ + public void removeOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { + onRegisterTouchListener.onRemoveMapLongClickListener(listener); } /** @@ -2299,13 +2393,29 @@ public final class MapboxMap { * related to touch and click events. */ interface OnRegisterTouchListener { - void onRegisterMapClickListener(OnMapClickListener listener); + void onSetMapClickListener(OnMapClickListener listener); + + void onAddMapClickListener(OnMapClickListener listener); + + void onRemoveMapClickListener(OnMapClickListener listener); + + void onSetMapLongClickListener(OnMapLongClickListener listener); + + void onAddMapLongClickListener(OnMapLongClickListener listener); + + void onRemoveMapLongClickListener(OnMapLongClickListener listener); + + void onSetScrollListener(OnScrollListener listener); + + void onAddScrollListener(OnScrollListener listener); + + void onRemoveScrollListener(OnScrollListener listener); - void onRegisterMapLongClickListener(OnMapLongClickListener listener); + void onSetFlingListener(OnFlingListener listener); - void onRegisterScrollListener(OnScrollListener listener); + void onAddFlingListener(OnFlingListener listener); - void onRegisterFlingListener(OnFlingListener listener); + void onRemoveFlingListener(OnFlingListener listener); } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java new file mode 100644 index 0000000000..eeb00355bd --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java @@ -0,0 +1,95 @@ +package com.mapbox.mapboxsdk.maps; + +import android.graphics.PointF; + +import com.mapbox.mapboxsdk.geometry.LatLng; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class MapTouchListenersTest { + + @Test + public void onMapClickListenerTest() throws Exception { + LatLng latLng = new LatLng(); + PointF pointF = new PointF(); + + Projection projection = mock(Projection.class); + when(projection.fromScreenLocation(pointF)).thenReturn(latLng); + MapGestureDetector mapGestureDetector = new MapGestureDetector(null, + null, projection, null, null, null, null); + + MapboxMap.OnMapClickListener listener = mock(MapboxMap.OnMapClickListener.class); + mapGestureDetector.addOnMapClickListener(listener); + mapGestureDetector.notifyOnMapClickListeners(pointF); + verify(listener, times(1)).onMapClick(latLng); + + mapGestureDetector.removeOnMapClickListener(listener); + mapGestureDetector.notifyOnMapClickListeners(pointF); + verify(listener, times(1)).onMapClick(latLng); + } + + @Test + public void onMapLongClickListenerTest() throws Exception { + LatLng latLng = new LatLng(); + PointF pointF = new PointF(); + + Projection projection = mock(Projection.class); + when(projection.fromScreenLocation(pointF)).thenReturn(latLng); + MapGestureDetector mapGestureDetector = new MapGestureDetector(null, + null, projection, null, null, null, null); + + MapboxMap.OnMapLongClickListener listener = mock(MapboxMap.OnMapLongClickListener.class); + mapGestureDetector.addOnMapLongClickListener(listener); + mapGestureDetector.notifyOnMapLongClickListeners(pointF); + verify(listener, times(1)).onMapLongClick(latLng); + + mapGestureDetector.removeOnMapLongClickListener(listener); + mapGestureDetector.notifyOnMapLongClickListeners(pointF); + verify(listener, times(1)).onMapLongClick(latLng); + } + + @Test + public void onFlingListenerTest() throws Exception { + LatLng latLng = new LatLng(); + PointF pointF = new PointF(); + + Projection projection = mock(Projection.class); + when(projection.fromScreenLocation(pointF)).thenReturn(latLng); + MapGestureDetector mapGestureDetector = new MapGestureDetector(null, + null, projection, null, null, null, null); + + MapboxMap.OnFlingListener listener = mock(MapboxMap.OnFlingListener.class); + mapGestureDetector.addOnFlingListener(listener); + mapGestureDetector.notifyOnFlingListeners(); + verify(listener, times(1)).onFling(); + + mapGestureDetector.removeOnFlingListener(listener); + mapGestureDetector.notifyOnFlingListeners(); + verify(listener, times(1)).onFling(); + } + + @Test + public void onScrollListenerTest() throws Exception { + LatLng latLng = new LatLng(); + PointF pointF = new PointF(); + + Projection projection = mock(Projection.class); + when(projection.fromScreenLocation(pointF)).thenReturn(latLng); + MapGestureDetector mapGestureDetector = new MapGestureDetector(null, + null, projection, null, null, null, null); + + MapboxMap.OnScrollListener listener = mock(MapboxMap.OnScrollListener.class); + mapGestureDetector.addOnScrollListener(listener); + mapGestureDetector.notifyOnScrollListeners(); + verify(listener, times(1)).onScroll(); + + mapGestureDetector.removeOnScrollListener(listener); + mapGestureDetector.notifyOnScrollListeners(); + verify(listener, times(1)).onScroll(); + } +} -- cgit v1.2.1