summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps')
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java87
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java88
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java17
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java234
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java66
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java96
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java9
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java83
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java38
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java18
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java4
14 files changed, 633 insertions, 139 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
index c09c926eb5..9f256c341b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
@@ -21,7 +21,6 @@ import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
-import com.mapbox.services.commons.geojson.Feature;
import java.util.ArrayList;
import java.util.List;
@@ -41,7 +40,6 @@ import timber.log.Timber;
*/
class AnnotationManager {
- private static final String LAYER_ID_SHAPE_ANNOTATIONS = "com.mapbox.annotations.shape.";
private static final long NO_ANNOTATION_ID = -1;
private final MapView mapView;
@@ -50,7 +48,6 @@ class AnnotationManager {
private final MarkerViewManager markerViewManager;
private final LongSparseArray<Annotation> annotationsArray;
private final List<Marker> selectedMarkers = new ArrayList<>();
- private final List<String> shapeAnnotationIds = new ArrayList<>();
private MapboxMap mapboxMap;
private MapboxMap.OnMarkerClickListener onMarkerClickListener;
@@ -58,13 +55,14 @@ class AnnotationManager {
private MapboxMap.OnPolylineClickListener onPolylineClickListener;
private Annotations annotations;
+ private ShapeAnnotations shapeAnnotations;
private Markers markers;
private Polygons polygons;
private Polylines polylines;
AnnotationManager(NativeMapView view, MapView mapView, LongSparseArray<Annotation> annotationsArray,
MarkerViewManager markerViewManager, IconManager iconManager, Annotations annotations,
- Markers markers, Polygons polygons, Polylines polylines) {
+ Markers markers, Polygons polygons, Polylines polylines, ShapeAnnotations shapeAnnotations) {
this.mapView = mapView;
this.annotationsArray = annotationsArray;
this.markerViewManager = markerViewManager;
@@ -73,6 +71,7 @@ class AnnotationManager {
this.markers = markers;
this.polygons = polygons;
this.polylines = polylines;
+ this.shapeAnnotations = shapeAnnotations;
if (view != null) {
// null checking needed for unit tests
view.addOnMapChangedListener(markerViewManager);
@@ -122,9 +121,6 @@ class AnnotationManager {
// do icon cleanup
iconManager.iconCleanup(marker.getIcon());
}
- } else {
- // instanceOf Polygon/Polyline
- shapeAnnotationIds.remove(annotation.getId());
}
annotations.removeBy(annotation);
}
@@ -143,9 +139,6 @@ class AnnotationManager {
} else {
iconManager.iconCleanup(marker.getIcon());
}
- } else {
- // instanceOf Polygon/Polyline
- shapeAnnotationIds.remove(annotation.getId());
}
}
annotations.removeBy(annotationList);
@@ -167,9 +160,6 @@ class AnnotationManager {
} else {
iconManager.iconCleanup(marker.getIcon());
}
- } else {
- // instanceOf Polygon/Polyline
- shapeAnnotationIds.remove(annotation.getId());
}
}
annotations.removeAll();
@@ -227,17 +217,11 @@ class AnnotationManager {
//
Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) {
- Polygon polygon = polygons.addBy(polygonOptions, mapboxMap);
- shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId());
- return polygon;
+ return polygons.addBy(polygonOptions, mapboxMap);
}
List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) {
- List<Polygon> polygonList = polygons.addBy(polygonOptionsList, mapboxMap);
- for (Polygon polygon : polygonList) {
- shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId());
- }
- return polygonList;
+ return polygons.addBy(polygonOptionsList, mapboxMap);
}
void updatePolygon(Polygon polygon) {
@@ -257,17 +241,11 @@ class AnnotationManager {
//
Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) {
- Polyline polyline = polylines.addBy(polylineOptions, mapboxMap);
- shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId());
- return polyline;
+ return polylines.addBy(polylineOptions, mapboxMap);
}
List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) {
- List<Polyline> polylineList = polylines.addBy(polylineOptionsList, mapboxMap);
- for (Polyline polyline : polylineList) {
- shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId());
- }
- return polylineList;
+ return polylines.addBy(polylineOptionsList, mapboxMap);
}
void updatePolyline(Polyline polyline) {
@@ -397,11 +375,11 @@ class AnnotationManager {
//
boolean onTap(PointF tapPoint) {
- if (!shapeAnnotationIds.isEmpty()) {
- ShapeAnnotationHit shapeAnnotationHit = getShapeAnnotationHitFromTap(tapPoint);
- long shapeAnnotationId = new ShapeAnnotationHitResolver(mapboxMap).execute(shapeAnnotationHit);
- if (shapeAnnotationId != NO_ANNOTATION_ID) {
- handleClickForShapeAnnotation(shapeAnnotationId);
+ ShapeAnnotationHit shapeAnnotationHit = getShapeAnnotationHitFromTap(tapPoint);
+ Annotation annotation = new ShapeAnnotationHitResolver(shapeAnnotations).execute(shapeAnnotationHit);
+ if (annotation != null) {
+ if (handleClickForShapeAnnotation(annotation)) {
+ return true;
}
}
@@ -418,16 +396,18 @@ class AnnotationManager {
tapPoint.x + touchTargetSide,
tapPoint.y + touchTargetSide
);
- return new ShapeAnnotationHit(tapRect, shapeAnnotationIds.toArray(new String[shapeAnnotationIds.size()]));
+ return new ShapeAnnotationHit(tapRect);
}
- private void handleClickForShapeAnnotation(long shapeAnnotationId) {
- Annotation annotation = getAnnotation(shapeAnnotationId);
+ private boolean handleClickForShapeAnnotation(Annotation annotation) {
if (annotation instanceof Polygon && onPolygonClickListener != null) {
onPolygonClickListener.onPolygonClick((Polygon) annotation);
+ return true;
} else if (annotation instanceof Polyline && onPolylineClickListener != null) {
onPolylineClickListener.onPolylineClick((Polyline) annotation);
+ return true;
}
+ return false;
}
private MarkerHit getMarkerHitFromTouchArea(PointF tapPoint) {
@@ -470,28 +450,19 @@ class AnnotationManager {
private static class ShapeAnnotationHitResolver {
- private MapboxMap mapboxMap;
-
- ShapeAnnotationHitResolver(MapboxMap mapboxMap) {
- this.mapboxMap = mapboxMap;
- }
+ private ShapeAnnotations shapeAnnotations;
- public long execute(ShapeAnnotationHit shapeHit) {
- long foundAnnotationId = NO_ANNOTATION_ID;
- List<Feature> features = mapboxMap.queryRenderedFeatures(shapeHit.tapPoint, shapeHit.layerIds);
- if (!features.isEmpty()) {
- foundAnnotationId = getIdFromFeature(features.get(0));
- }
- return foundAnnotationId;
+ ShapeAnnotationHitResolver(ShapeAnnotations shapeAnnotations) {
+ this.shapeAnnotations = shapeAnnotations;
}
- private long getIdFromFeature(Feature feature) {
- try {
- return Long.valueOf(feature.getId());
- } catch (NumberFormatException exception) {
- Timber.e(exception, "Couldn't parse feature id to a long, with id: %s", feature.getId());
- return NO_ANNOTATION_ID;
+ public Annotation execute(ShapeAnnotationHit shapeHit) {
+ Annotation foundAnnotation = null;
+ List<Annotation> annotations = shapeAnnotations.obtainAllIn(shapeHit.tapPoint);
+ if (annotations.size() > 0) {
+ foundAnnotation = annotations.get(0);
}
+ return foundAnnotation;
}
}
@@ -567,11 +538,9 @@ class AnnotationManager {
private static class ShapeAnnotationHit {
private final RectF tapPoint;
- private final String[] layerIds;
- ShapeAnnotationHit(RectF tapRect, String[] layerIds) {
- this.tapPoint = tapRect;
- this.layerIds = layerIds;
+ ShapeAnnotationHit(RectF tapPoint) {
+ this.tapPoint = tapPoint;
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
index 5113a0cc73..9ccff387f5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
@@ -1,12 +1,12 @@
package com.mapbox.mapboxsdk.maps;
+import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
-import android.support.v7.app.AlertDialog;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -56,7 +56,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
private void showAttributionDialog() {
attributionKeys = attributionMap.keySet().toArray(new String[attributionMap.size()]);
- AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.mapbox_attributionsDialogTitle);
builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionKeys), this);
builder.show();
@@ -77,7 +77,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
}
private void showTelemetryDialog() {
- AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.mapbox_AlertDialogStyle);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.mapbox_attributionTelemetryTitle);
builder.setMessage(R.string.mapbox_attributionTelemetryMessage);
builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java
index 6f7d7c0080..cf780dcc3f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java
@@ -1,5 +1,10 @@
package com.mapbox.mapboxsdk.maps;
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveCanceledListener;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener;
@@ -10,23 +15,32 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
private boolean idle = true;
+ private final List<OnCameraMoveStartedListener> onCameraMoveStartedListenerList = new ArrayList<>();
+ private final List<OnCameraMoveCanceledListener> onCameraMoveCanceledListenerList = new ArrayList<>();
+ private final List<OnCameraMoveListener> onCameraMoveListenerList = new ArrayList<>();
+ private final List<OnCameraIdleListener> onCameraIdleListenerList = new ArrayList<>();
+
private OnCameraMoveStartedListener onCameraMoveStartedListener;
private OnCameraMoveCanceledListener onCameraMoveCanceledListener;
private OnCameraMoveListener onCameraMoveListener;
private OnCameraIdleListener onCameraIdleListener;
+ @Deprecated
void setOnCameraMoveStartedListener(OnCameraMoveStartedListener onCameraMoveStartedListener) {
this.onCameraMoveStartedListener = onCameraMoveStartedListener;
}
+ @Deprecated
void setOnCameraMoveCanceledListener(OnCameraMoveCanceledListener onCameraMoveCanceledListener) {
this.onCameraMoveCanceledListener = onCameraMoveCanceledListener;
}
+ @Deprecated
void setOnCameraMoveListener(OnCameraMoveListener onCameraMoveListener) {
this.onCameraMoveListener = onCameraMoveListener;
}
+ @Deprecated
void setOnCameraIdleListener(OnCameraIdleListener onCameraIdleListener) {
this.onCameraIdleListener = onCameraIdleListener;
}
@@ -36,34 +50,106 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
if (!idle) {
return;
}
-
idle = false;
+
+ // deprecated API
if (onCameraMoveStartedListener != null) {
onCameraMoveStartedListener.onCameraMoveStarted(reason);
}
+
+ // new API
+ if (!onCameraMoveStartedListenerList.isEmpty()) {
+ for (OnCameraMoveStartedListener cameraMoveStartedListener : onCameraMoveStartedListenerList) {
+ cameraMoveStartedListener.onCameraMoveStarted(reason);
+ }
+ }
}
@Override
public void onCameraMove() {
+ // deprecated API
if (onCameraMoveListener != null && !idle) {
onCameraMoveListener.onCameraMove();
}
+
+ // new API
+ if (!onCameraMoveListenerList.isEmpty() && !idle) {
+ for (OnCameraMoveListener cameraMoveListener : onCameraMoveListenerList) {
+ cameraMoveListener.onCameraMove();
+ }
+ }
}
@Override
public void onCameraMoveCanceled() {
+ // deprecated API
if (onCameraMoveCanceledListener != null && !idle) {
onCameraMoveCanceledListener.onCameraMoveCanceled();
}
+
+ // new API
+ if (!onCameraMoveCanceledListenerList.isEmpty() && !idle) {
+ for (OnCameraMoveCanceledListener cameraMoveCanceledListener : onCameraMoveCanceledListenerList) {
+ cameraMoveCanceledListener.onCameraMoveCanceled();
+ }
+ }
}
@Override
public void onCameraIdle() {
if (!idle) {
idle = true;
+ // deprecated API
if (onCameraIdleListener != null) {
onCameraIdleListener.onCameraIdle();
}
+
+ // new API
+ if (!onCameraIdleListenerList.isEmpty()) {
+ for (OnCameraIdleListener cameraIdleListener : onCameraIdleListenerList) {
+ cameraIdleListener.onCameraIdle();
+ }
+ }
+ }
+ }
+
+ void addOnCameraIdleListener(@NonNull OnCameraIdleListener listener) {
+ onCameraIdleListenerList.add(listener);
+ }
+
+ void removeOnCameraIdleListener(@NonNull OnCameraIdleListener listener) {
+ if (onCameraIdleListenerList.contains(listener)) {
+ onCameraIdleListenerList.remove(listener);
+ }
+ }
+
+ void addOnCameraMoveCancelListener(OnCameraMoveCanceledListener listener) {
+ onCameraMoveCanceledListenerList.add(listener);
+ }
+
+ void removeOnCameraMoveCancelListener(OnCameraMoveCanceledListener listener) {
+ if (onCameraMoveCanceledListenerList.contains(listener)) {
+ onCameraMoveCanceledListenerList.remove(listener);
+ }
+ }
+
+ void addOnCameraMoveStartedListener(OnCameraMoveStartedListener listener) {
+ onCameraMoveStartedListenerList.add(listener);
+ }
+
+ void removeOnCameraMoveStartedListener(OnCameraMoveStartedListener listener) {
+ if (onCameraMoveStartedListenerList.contains(listener)) {
+ onCameraMoveStartedListenerList.remove(listener);
+ }
+ }
+
+ void addOnCameraMoveListener(OnCameraMoveListener listener) {
+ onCameraMoveListenerList.add(listener);
+ }
+
+ void removeOnCameraMoveListener(OnCameraMoveListener listener) {
+ if (onCameraMoveListenerList.contains(listener)) {
+ onCameraMoveListenerList.remove(listener);
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java
index b1d6df2103..80ffa973e7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java
@@ -143,11 +143,14 @@ class IconManager {
}
void iconCleanup(Icon icon) {
- int refCounter = iconMap.get(icon) - 1;
- if (refCounter == 0) {
- remove(icon);
- } else {
- updateIconRefCounter(icon, refCounter);
+ Integer refCounter = iconMap.get(icon);
+ if (refCounter != null) {
+ refCounter--;
+ if (refCounter == 0) {
+ remove(icon);
+ } else {
+ updateIconRefCounter(icon, refCounter);
+ }
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java
new file mode 100644
index 0000000000..b2f6cef3b0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java
@@ -0,0 +1,17 @@
+package com.mapbox.mapboxsdk.maps;
+
+class Image {
+ private final byte[] buffer;
+ private final float pixelRatio;
+ private final String name;
+ private final int width;
+ private final int height;
+
+ public Image(byte[] buffer, float pixelRatio, String name, int width, int height) {
+ this.buffer = buffer;
+ this.pixelRatio = pixelRatio;
+ this.name = name;
+ this.width = width;
+ this.height = height;
+ }
+}
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 2394e52193..4120e164a4 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
@@ -1,14 +1,19 @@
package com.mapbox.mapboxsdk.maps;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
import android.location.Location;
import android.support.annotation.Nullable;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.ScaleGestureDetectorCompat;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
+import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector;
@@ -57,8 +62,14 @@ final class MapGestureDetector {
private boolean scaleGestureOccurred;
private boolean recentScaleGestureOccurred;
+ private boolean scaleAnimating;
private long scaleBeginTime;
+ private VelocityTracker velocityTracker;
+ private boolean wasZoomingIn;
+ private boolean wasClockwiseRotating;
+ private boolean rotateGestureOccurred;
+
MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings,
TrackingSettings trackingSettings, AnnotationManager annotationManager,
CameraChangeDispatcher cameraChangeDispatcher) {
@@ -153,6 +164,12 @@ final class MapGestureDetector {
// Handle two finger tap
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ if (velocityTracker == null) {
+ velocityTracker = VelocityTracker.obtain();
+ } else {
+ velocityTracker.clear();
+ }
+ velocityTracker.addMovement(event);
// First pointer down, reset scaleGestureOccurred, used to avoid triggering a fling after a scale gesture #7666
recentScaleGestureOccurred = false;
transform.setGestureInProgress(true);
@@ -203,11 +220,23 @@ final class MapGestureDetector {
twoTap = false;
transform.setGestureInProgress(false);
+ if (velocityTracker != null) {
+ velocityTracker.recycle();
+ }
+ velocityTracker = null;
break;
case MotionEvent.ACTION_CANCEL:
twoTap = false;
transform.setGestureInProgress(false);
+ if (velocityTracker != null) {
+ velocityTracker.recycle();
+ }
+ velocityTracker = null;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ velocityTracker.addMovement(event);
+ velocityTracker.computeCurrentVelocity(1000);
break;
}
@@ -430,7 +459,11 @@ final class MapGestureDetector {
*/
private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+ private static final int ANIMATION_TIME_MULTIPLIER = 77;
+ private static final double ZOOM_DISTANCE_DIVIDER = 5;
+
private float scaleFactor = 1.0f;
+ private PointF scalePointBegin;
// Called when two fingers first touch the screen
@Override
@@ -440,6 +473,7 @@ final class MapGestureDetector {
}
recentScaleGestureOccurred = true;
+ scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY());
scaleBeginTime = detector.getEventTime();
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
@@ -447,15 +481,6 @@ final class MapGestureDetector {
return true;
}
- // Called when fingers leave screen
- @Override
- public void onScaleEnd(ScaleGestureDetector detector) {
- scaleGestureOccurred = false;
- scaleBeginTime = 0;
- scaleFactor = 1.0f;
- cameraChangeDispatcher.onCameraIdle();
- }
-
// Called each time a finger moves
// Called for pinch zooms and quickzooms/quickscales
@Override
@@ -464,6 +489,7 @@ final class MapGestureDetector {
return super.onScale(detector);
}
+ wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) >= 0;
if (tiltGestureOccurred) {
return false;
}
@@ -478,7 +504,7 @@ final class MapGestureDetector {
// If scale is large enough ignore a tap
scaleFactor *= detector.getScaleFactor();
- if ((scaleFactor > 1.05f) || (scaleFactor < 0.95f)) {
+ if ((scaleFactor > 1.1f) || (scaleFactor < 0.9f)) {
// notify camera change listener
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
scaleGestureOccurred = true;
@@ -497,7 +523,6 @@ final class MapGestureDetector {
// make an assumption here; if the zoom center is specified by the gesture, it's NOT going
// to be in the center of the map. Therefore the zoom will translate the map center, so tracking
// should be disabled.
-
trackingSettings.resetTrackingModesIfRequired(!quickZoom, false, false);
// Scale the map
if (focalPoint != null) {
@@ -506,19 +531,79 @@ final class MapGestureDetector {
} else if (quickZoom) {
cameraChangeDispatcher.onCameraMove();
// clamp scale factors we feed to core #7514
- float scaleFactor = MathUtils.clamp(detector.getScaleFactor(),
+ float scaleFactor = detector.getScaleFactor();
+ // around center map
+ double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2);
+ boolean negative = zoomBy < 0;
+ zoomBy = MathUtils.clamp(Math.abs(zoomBy),
MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP,
MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP);
- // around center map
- transform.zoomBy(Math.log(scaleFactor) / Math.log(Math.PI / 2),
- uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
+ transform.zoomBy(negative ? -zoomBy : zoomBy, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
+ recentScaleGestureOccurred = true;
} else {
// around gesture
transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2),
- detector.getFocusX(), detector.getFocusY());
+ scalePointBegin.x, scalePointBegin.y);
}
return true;
}
+
+ // Called when fingers leave screen
+ @Override
+ public void onScaleEnd(final ScaleGestureDetector detector) {
+ if (rotateGestureOccurred || quickZoom) {
+ reset();
+ return;
+ }
+
+ double velocityXY = Math.abs(velocityTracker.getYVelocity()) + Math.abs(velocityTracker.getXVelocity());
+ if (velocityXY > MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING / 2) {
+ scaleAnimating = true;
+ double zoomAddition = calculateScale(velocityXY);
+ double currentZoom = transform.getRawZoom();
+ long animationTime = (long) (Math.log(velocityXY) * ANIMATION_TIME_MULTIPLIER);
+ createScaleAnimator(currentZoom, zoomAddition, animationTime).start();
+ } else if (!scaleAnimating) {
+ reset();
+ }
+ }
+
+ private void reset() {
+ scaleAnimating = false;
+ scaleGestureOccurred = false;
+ scaleBeginTime = 0;
+ scaleFactor = 1.0f;
+ cameraChangeDispatcher.onCameraIdle();
+ }
+
+ private double calculateScale(double velocityXY) {
+ double zoomAddition = (float) (Math.log(velocityXY) / ZOOM_DISTANCE_DIVIDER);
+ if (!wasZoomingIn) {
+ zoomAddition = -zoomAddition;
+ }
+ return zoomAddition;
+ }
+
+ private Animator createScaleAnimator(double currentZoom, double zoomAddition, long animationTime) {
+ ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition));
+ animator.setDuration(animationTime);
+ animator.setInterpolator(new FastOutSlowInInterpolator());
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ transform.setZoom((Float) animation.getAnimatedValue(), scalePointBegin);
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ reset();
+ }
+ });
+ return animator;
+ }
}
/**
@@ -526,11 +611,13 @@ final class MapGestureDetector {
*/
private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
- private static final long ROTATE_INVOKE_WAIT_TIME = 1500;
+ private static final float ROTATE_INVOKE_ANGLE = 15.30f;
+ private static final float ROTATE_LIMITATION_ANGLE = 3.35f;
+ private static final float ROTATE_LIMITATION_DURATION = ROTATE_LIMITATION_ANGLE * 1.85f;
private long beginTime = 0;
- private float totalAngle = 0.0f;
private boolean started = false;
+ private boolean animating = false;
// Called when two fingers first touch the screen
@Override
@@ -546,15 +633,6 @@ final class MapGestureDetector {
return true;
}
- // Called when the fingers leave the screen
- @Override
- public void onRotateEnd(RotateGestureDetector detector) {
- // notify camera change listener
- beginTime = 0;
- totalAngle = 0.0f;
- started = false;
- }
-
// Called each time one of the two fingers moves
// Called for rotation
@Override
@@ -563,18 +641,10 @@ final class MapGestureDetector {
return false;
}
- // Ignore short touches in case it is a tap
- // Also ignore small rotate
- long time = detector.getEventTime();
- long interval = time - beginTime;
- if (!started && (interval <= ViewConfiguration.getTapTimeout() || isScaleGestureActive(time))) {
- return false;
- }
-
// If rotate is large enough ignore a tap
// Also is zoom already started, don't rotate
- totalAngle += detector.getRotationDegreesDelta();
- if (totalAngle > 35.0f || totalAngle < -35.0f) {
+ float angle = detector.getRotationDegreesDelta();
+ if (Math.abs(angle) >= ROTATE_INVOKE_ANGLE) {
MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
MapboxEvent.GESTURE_ROTATION_START, transform));
@@ -585,13 +655,17 @@ final class MapGestureDetector {
return false;
}
+ wasClockwiseRotating = detector.getRotationDegreesDelta() > 0;
+ if (scaleBeginTime != 0) {
+ rotateGestureOccurred = true;
+ }
+
// rotation constitutes translation of anything except the center of
// rotation, so cancel both location and bearing tracking if required
trackingSettings.resetTrackingModesIfRequired(true, true, false);
- // Get rotate value
- double bearing = transform.getRawBearing();
- bearing += detector.getRotationDegreesDelta();
+ // Calculate map bearing value
+ double bearing = transform.getRawBearing() + angle;
// Rotate the map
if (focalPoint != null) {
@@ -604,11 +678,81 @@ final class MapGestureDetector {
return true;
}
- private boolean isScaleGestureActive(long time) {
- long scaleExecutionTime = time - scaleBeginTime;
- boolean scaleGestureStarted = scaleBeginTime != 0;
- boolean scaleOffsetTimeValid = scaleExecutionTime > ROTATE_INVOKE_WAIT_TIME;
- return (scaleGestureStarted && scaleOffsetTimeValid) || scaleGestureOccurred;
+ // Called when the fingers leave the screen
+ @Override
+ public void onRotateEnd(RotateGestureDetector detector) {
+ long interval = detector.getEventTime() - beginTime;
+ if ((!started && (interval <= ViewConfiguration.getTapTimeout())) || scaleAnimating || interval > 500) {
+ reset();
+ return;
+ }
+
+ double angularVelocity = calculateVelocityVector(detector);
+ if (Math.abs(angularVelocity) > 0.001 && rotateGestureOccurred && !animating) {
+ animateRotateVelocity();
+ } else if (!animating) {
+ reset();
+ }
+ }
+
+ private void reset() {
+ beginTime = 0;
+ started = false;
+ animating = false;
+ rotateGestureOccurred = false;
+ }
+
+ private void animateRotateVelocity() {
+ animating = true;
+ double currentRotation = transform.getRawBearing();
+ double rotateAdditionDegrees = calculateVelocityInDegrees();
+ createAnimator(currentRotation, rotateAdditionDegrees).start();
+ }
+
+ private double calculateVelocityVector(RotateGestureDetector detector) {
+ return ((detector.getFocusX() * velocityTracker.getYVelocity())
+ + (detector.getFocusY() * velocityTracker.getXVelocity()))
+ / (Math.pow(detector.getFocusX(), 2) + Math.pow(detector.getFocusY(), 2));
+ }
+
+ private double calculateVelocityInDegrees() {
+ double angleRadians = Math.atan2(velocityTracker.getXVelocity(), velocityTracker.getYVelocity());
+ double angle = angleRadians / (Math.PI / 180);
+ if (angle <= 0) {
+ angle += 360;
+ }
+
+ // limit the angle
+ angle = angle / ROTATE_LIMITATION_ANGLE;
+
+ // correct direction
+ if (!wasClockwiseRotating) {
+ angle = -angle;
+ }
+
+ return angle;
+ }
+
+ private Animator createAnimator(double currentRotation, double rotateAdditionDegrees) {
+ ValueAnimator animator = ValueAnimator.ofFloat(
+ (float) currentRotation,
+ (float) (currentRotation + rotateAdditionDegrees)
+ );
+ animator.setDuration((long) (Math.abs(rotateAdditionDegrees) * ROTATE_LIMITATION_DURATION));
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ transform.setBearing((Float) animation.getAnimatedValue());
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ reset();
+ }
+ });
+ return animator;
}
}
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 92153f7f0b..c025a119b7 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
@@ -28,13 +28,14 @@ import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.Style;
-import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer;
import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
+import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer;
import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
+import com.mapbox.mapboxsdk.storage.FileSource;
import com.mapbox.services.android.telemetry.MapboxTelemetry;
import java.lang.annotation.Retention;
@@ -175,8 +176,9 @@ public class MapView extends FrameLayout {
Markers markers = new MarkerContainer(nativeMapView, this, annotationsArray, iconManager, markerViewManager);
Polygons polygons = new PolygonContainer(nativeMapView, annotationsArray);
Polylines polylines = new PolylineContainer(nativeMapView, annotationsArray);
+ ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(nativeMapView, annotationsArray);
AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, annotationsArray,
- markerViewManager, iconManager, annotations, markers, polygons, polylines);
+ markerViewManager, iconManager, annotations, markers, polygons, polylines, shapeAnnotations);
Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings,
cameraChangeDispatcher);
@@ -191,8 +193,10 @@ public class MapView extends FrameLayout {
annotationManager, cameraChangeDispatcher);
mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings);
+ // overlain zoom buttons
mapZoomButtonController = new MapZoomButtonController(new ZoomButtonsController(this));
- MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform);
+ MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform,
+ cameraChangeDispatcher, getWidth(), getHeight());
mapZoomButtonController.bind(uiSettings, zoomListener);
compassView.injectCompassAnimationListener(createCompassAnimationListener(cameraChangeDispatcher));
@@ -345,8 +349,10 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onSaveInstanceState(@NonNull Bundle outState) {
- outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true);
- mapboxMap.onSaveInstanceState(outState);
+ if (mapboxMap != null) {
+ outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true);
+ mapboxMap.onSaveInstanceState(outState);
+ }
}
/**
@@ -355,6 +361,7 @@ public class MapView extends FrameLayout {
@UiThread
public void onStart() {
ConnectivityReceiver.instance(getContext()).activate();
+ FileSource.getInstance(getContext()).activate();
if (mapboxMap != null) {
mapboxMap.onStart();
}
@@ -385,8 +392,12 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onStop() {
- mapboxMap.onStop();
+ if (mapboxMap != null) {
+ // map was destroyed before it was started
+ mapboxMap.onStop();
+ }
ConnectivityReceiver.instance(getContext()).deactivate();
+ FileSource.getInstance(getContext()).deactivate();
}
/**
@@ -406,6 +417,10 @@ public class MapView extends FrameLayout {
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (!isMapInitialized() || !isZoomButtonControllerInitialized()) {
+ return super.onTouchEvent(event);
+ }
+
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mapZoomButtonController.setVisible(true);
}
@@ -434,11 +449,18 @@ public class MapView extends FrameLayout {
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
+ if (mapGestureDetector == null) {
+ return super.onGenericMotionEvent(event);
+ }
return mapGestureDetector.onGenericMotionEvent(event) || super.onGenericMotionEvent(event);
}
@Override
public boolean onHoverEvent(MotionEvent event) {
+ if (!isZoomButtonControllerInitialized()) {
+ return super.onHoverEvent(event);
+ }
+
switch (event.getActionMasked()) {
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE:
@@ -495,7 +517,7 @@ public class MapView extends FrameLayout {
if (destroyed) {
return;
}
- if (nativeMapView == null) {
+ if (!isMapInitialized()) {
mapboxMapOptions.styleUrl(url);
return;
}
@@ -512,7 +534,7 @@ public class MapView extends FrameLayout {
return;
}
- if (!isInEditMode() && nativeMapView != null) {
+ if (!isInEditMode() && isMapInitialized()) {
nativeMapView.resizeView(width, height);
}
}
@@ -526,7 +548,9 @@ public class MapView extends FrameLayout {
@CallSuper
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mapZoomButtonController.setVisible(false);
+ if (isZoomButtonControllerInitialized()) {
+ mapZoomButtonController.setVisible(false);
+ }
}
// Called when view is hidden and shown
@@ -536,7 +560,7 @@ public class MapView extends FrameLayout {
return;
}
- if (mapZoomButtonController != null) {
+ if (isZoomButtonControllerInitialized()) {
mapZoomButtonController.setVisible(visibility == View.VISIBLE);
}
}
@@ -598,6 +622,14 @@ public class MapView extends FrameLayout {
}
}
+ private boolean isMapInitialized() {
+ return nativeMapView != null;
+ }
+
+ private boolean isZoomButtonControllerInitialized() {
+ return mapZoomButtonController != null;
+ }
+
MapboxMap getMapboxMap() {
return mapboxMap;
}
@@ -883,16 +915,23 @@ public class MapView extends FrameLayout {
}
}
- private class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener {
+ private static class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener {
private final MapGestureDetector mapGestureDetector;
private final UiSettings uiSettings;
private final Transform transform;
+ private final CameraChangeDispatcher cameraChangeDispatcher;
+ private final float mapWidth;
+ private final float mapHeight;
- MapZoomControllerListener(MapGestureDetector detector, UiSettings uiSettings, Transform transform) {
+ MapZoomControllerListener(MapGestureDetector detector, UiSettings uiSettings, Transform transform,
+ CameraChangeDispatcher dispatcher, float mapWidth, float mapHeight) {
this.mapGestureDetector = detector;
this.uiSettings = uiSettings;
this.transform = transform;
+ this.cameraChangeDispatcher = dispatcher;
+ this.mapWidth = mapWidth;
+ this.mapHeight = mapHeight;
}
// Not used
@@ -905,6 +944,7 @@ public class MapView extends FrameLayout {
@Override
public void onZoom(boolean zoomIn) {
if (uiSettings.isZoomGesturesEnabled()) {
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_ANIMATION);
onZoom(zoomIn, mapGestureDetector.getFocalPoint());
}
}
@@ -913,7 +953,7 @@ public class MapView extends FrameLayout {
if (focalPoint != null) {
transform.zoom(zoomIn, focalPoint);
} else {
- PointF centerPoint = new PointF(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
+ PointF centerPoint = new PointF(mapWidth / 2, mapHeight / 2);
transform.zoom(zoomIn, centerPoint);
}
}
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 83b4dc4e84..6bf8342efb 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
@@ -47,6 +47,7 @@ import com.mapbox.services.commons.geojson.Feature;
import com.mapbox.services.commons.geojson.Geometry;
import java.lang.reflect.ParameterizedType;
+import java.util.HashMap;
import java.util.List;
import timber.log.Timber;
@@ -463,6 +464,13 @@ public final class MapboxMap {
}
/**
+ * Adds an images to be used in the map's style
+ */
+ public void addImages(@NonNull HashMap<String, Bitmap> images) {
+ nativeMapView.addImages(images);
+ }
+
+ /**
* Removes an image from the map's style
*
* @param name the name of the image to remove
@@ -709,6 +717,10 @@ public final class MapboxMap {
// MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo`
// invalidate camera position to provide OnCameraChange event.
invalidateCameraPosition();
+
+ if (callback != null) {
+ callback.onFinish();
+ }
}
});
}
@@ -1753,39 +1765,123 @@ public final class MapboxMap {
* Sets a callback that is invoked when camera movement has ended.
*
* @param listener the listener to notify
+ * @deprecated use {@link #addOnCameraIdleListener(OnCameraIdleListener)}
+ * and {@link #removeOnCameraIdleListener(OnCameraIdleListener)} instead
*/
+ @Deprecated
public void setOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
cameraChangeDispatcher.setOnCameraIdleListener(listener);
}
/**
+ * Adds a callback that is invoked when camera movement has ended.
+ *
+ * @param listener the listener to notify
+ */
+ public void addOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
+ cameraChangeDispatcher.addOnCameraIdleListener(listener);
+ }
+
+ /**
+ * Removes a callback that is invoked when camera movement has ended.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
+ cameraChangeDispatcher.removeOnCameraIdleListener(listener);
+ }
+
+ /**
* Sets a callback that is invoked when camera movement was cancelled.
*
* @param listener the listener to notify
+ * @deprecated use {@link #addOnCameraMoveCancelListener(OnCameraMoveCanceledListener)} and
+ * {@link #removeOnCameraMoveCancelListener(OnCameraMoveCanceledListener)} instead
*/
+ @Deprecated
public void setOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
cameraChangeDispatcher.setOnCameraMoveCanceledListener(listener);
}
/**
+ * Adds a callback that is invoked when camera movement was cancelled.
+ *
+ * @param listener the listener to notify
+ */
+ public void addOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
+ cameraChangeDispatcher.addOnCameraMoveCancelListener(listener);
+ }
+
+ /**
+ * Removes a callback that is invoked when camera movement was cancelled.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
+ cameraChangeDispatcher.removeOnCameraMoveCancelListener(listener);
+ }
+
+ /**
* Sets a callback that is invoked when camera movement has started.
*
* @param listener the listener to notify
+ * @deprecated use {@link #addOnCameraMoveStartedListener(OnCameraMoveStartedListener)} and
+ * {@link #removeOnCameraMoveStartedListener(OnCameraMoveStartedListener)} instead
*/
+ @Deprecated
public void setOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
cameraChangeDispatcher.setOnCameraMoveStartedListener(listener);
}
/**
+ * Adds a callback that is invoked when camera movement has started.
+ *
+ * @param listener the listener to notify
+ */
+ public void addOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
+ cameraChangeDispatcher.addOnCameraMoveStartedListener(listener);
+ }
+
+ /**
+ * Removes a callback that is invoked when camera movement has started.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
+ cameraChangeDispatcher.removeOnCameraMoveStartedListener(listener);
+ }
+
+ /**
* Sets a callback that is invoked when camera position changes.
*
* @param listener the listener to notify
+ * @deprecated use {@link #addOnCameraMoveListener(OnCameraMoveListener)} and
+ * {@link #removeOnCameraMoveListener(OnCameraMoveListener)}instead
*/
+ @Deprecated
public void setOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
cameraChangeDispatcher.setOnCameraMoveListener(listener);
}
/**
+ * Adds a callback that is invoked when camera position changes.
+ *
+ * @param listener the listener to notify
+ */
+ public void addOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
+ cameraChangeDispatcher.addOnCameraMoveListener(listener);
+ }
+
+ /**
+ * Removes a callback that is invoked when camera position changes.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
+ cameraChangeDispatcher.removeOnCameraMoveListener(listener);
+ }
+
+ /**
* Sets a callback that's invoked on every frame rendered to the map view.
*
* @param listener The callback that's invoked on every frame rendered to the map view.
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
index 072382ce07..2c2f07a112 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
@@ -98,15 +98,8 @@ class MarkerContainer implements Markers {
@NonNull
@Override
public List<Marker> obtainAllIn(@NonNull RectF rectangle) {
- // convert Rectangle to be density depedent
- float pixelRatio = nativeMapView.getPixelRatio();
- RectF rect = new RectF(rectangle.left / pixelRatio,
- rectangle.top / pixelRatio,
- rectangle.right / pixelRatio,
- rectangle.bottom / pixelRatio);
-
+ RectF rect = nativeMapView.getDensityDependantRectangle(rectangle);
long[] ids = nativeMapView.queryPointAnnotations(rect);
-
List<Long> idsList = new ArrayList<>(ids.length);
for (long id : ids) {
idsList.add(id);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
index bd8a54783e..8b6bce69e2 100755
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
@@ -4,6 +4,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.RectF;
+import android.os.AsyncTask;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -34,7 +35,9 @@ import com.mapbox.services.commons.geojson.Geometry;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import timber.log.Timber;
@@ -468,6 +471,13 @@ final class NativeMapView {
return nativeQueryPointAnnotations(rect);
}
+ public long[] queryShapeAnnotations(RectF rectF) {
+ if (isDestroyedOn("queryShapeAnnotations")) {
+ return new long[] {};
+ }
+ return nativeQueryShapeAnnotations(rectF);
+ }
+
public void addAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels) {
if (isDestroyedOn("addAnnotationIcon")) {
return;
@@ -746,6 +756,7 @@ final class NativeMapView {
if (isDestroyedOn("addImage")) {
return;
}
+
// Check/correct config
if (image.getConfig() != Bitmap.Config.ARGB_8888) {
image = image.copy(Bitmap.Config.ARGB_8888, false);
@@ -762,6 +773,14 @@ final class NativeMapView {
nativeAddImage(name, image.getWidth(), image.getHeight(), pixelRatio, buffer.array());
}
+ public void addImages(@NonNull HashMap<String, Bitmap> bitmapHashMap) {
+ if (isDestroyedOn("addImages")) {
+ return;
+ }
+ //noinspection unchecked
+ new BitmapImageConversionTask(this).execute(bitmapHashMap);
+ }
+
public void removeImage(String name) {
if (isDestroyedOn("removeImage")) {
return;
@@ -825,6 +844,15 @@ final class NativeMapView {
return pixelRatio;
}
+ RectF getDensityDependantRectangle(final RectF rectangle) {
+ return new RectF(
+ rectangle.left / pixelRatio,
+ rectangle.top / pixelRatio,
+ rectangle.right / pixelRatio,
+ rectangle.bottom / pixelRatio
+ );
+ }
+
//
// Callbacks
//
@@ -927,6 +955,8 @@ final class NativeMapView {
private native long[] nativeQueryPointAnnotations(RectF rect);
+ private native long[] nativeQueryShapeAnnotations(RectF rect);
+
private native void nativeAddAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels);
private native void nativeRemoveAnnotationIcon(String symbol);
@@ -1006,6 +1036,8 @@ final class NativeMapView {
private native void nativeAddImage(String name, int width, int height, float pixelRatio,
byte[] array);
+ private native void nativeAddImages(Image[] images);
+
private native void nativeRemoveImage(String name);
private native Bitmap nativeGetImage(String name);
@@ -1093,4 +1125,55 @@ final class NativeMapView {
});
}
+
+
+ //
+ // Image conversion
+ //
+
+ private static class BitmapImageConversionTask extends AsyncTask<HashMap<String, Bitmap>, Void, List<Image>> {
+
+ private NativeMapView nativeMapView;
+
+ BitmapImageConversionTask(NativeMapView nativeMapView) {
+ this.nativeMapView = nativeMapView;
+ }
+
+ @Override
+ protected List<Image> doInBackground(HashMap<String, Bitmap>... params) {
+ HashMap<String, Bitmap> bitmapHashMap = params[0];
+
+ List<Image> images = new ArrayList<>();
+ ByteBuffer buffer;
+ String name;
+ Bitmap bitmap;
+
+ for (Map.Entry<String, Bitmap> stringBitmapEntry : bitmapHashMap.entrySet()) {
+ name = stringBitmapEntry.getKey();
+ bitmap = stringBitmapEntry.getValue();
+
+ if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) {
+ bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false);
+ }
+
+ buffer = ByteBuffer.allocate(bitmap.getByteCount());
+ bitmap.copyPixelsToBuffer(buffer);
+
+ float density = bitmap.getDensity() == Bitmap.DENSITY_NONE ? Bitmap.DENSITY_NONE : bitmap.getDensity();
+ float pixelRatio = density / DisplayMetrics.DENSITY_DEFAULT;
+
+ images.add(new Image(buffer.array(), pixelRatio, name, bitmap.getWidth(), bitmap.getHeight()));
+ }
+
+ return images;
+ }
+
+ @Override
+ protected void onPostExecute(List<Image> images) {
+ super.onPostExecute(images);
+ if (nativeMapView != null && !nativeMapView.isDestroyedOn("nativeAddImages")) {
+ nativeMapView.nativeAddImages(images.toArray(new Image[images.size()]));
+ }
+ }
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java
new file mode 100644
index 0000000000..6ded2f32fb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java
@@ -0,0 +1,38 @@
+package com.mapbox.mapboxsdk.maps;
+
+import android.graphics.RectF;
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class ShapeAnnotationContainer implements ShapeAnnotations {
+
+ private final NativeMapView nativeMapView;
+ private final LongSparseArray<Annotation> annotations;
+
+ ShapeAnnotationContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) {
+ this.nativeMapView = nativeMapView;
+ this.annotations = annotations;
+ }
+
+ @Override
+ public List<Annotation> obtainAllIn(RectF rectangle) {
+ RectF rect = nativeMapView.getDensityDependantRectangle(rectangle);
+ long[] annotationIds = nativeMapView.queryShapeAnnotations(rect);
+ return getAnnotationsFromIds(annotationIds);
+ }
+
+ private List<Annotation> getAnnotationsFromIds(long[] annotationIds) {
+ List<Annotation> shapeAnnotations = new ArrayList<>();
+ for (long annotationId : annotationIds) {
+ Annotation annotation = annotations.get(annotationId);
+ if (annotation != null) {
+ shapeAnnotations.add(annotation);
+ }
+ }
+ return shapeAnnotations;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java
new file mode 100644
index 0000000000..f9b2ca10db
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java
@@ -0,0 +1,13 @@
+package com.mapbox.mapboxsdk.maps;
+
+import android.graphics.RectF;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+
+import java.util.List;
+
+interface ShapeAnnotations {
+
+ List<Annotation> obtainAllIn(RectF rectF);
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
index 6f63c2eba8..0366e50627 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
@@ -98,9 +98,6 @@ final class Transform implements MapView.OnMapChangedListener {
cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
mapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom);
- if (callback != null) {
- callback.onFinish();
- }
cameraChangeDispatcher.onCameraIdle();
}
}
@@ -210,6 +207,10 @@ final class Transform implements MapView.OnMapChangedListener {
return cameraPosition.zoom;
}
+ double getRawZoom() {
+ return mapView.getZoom();
+ }
+
void zoom(boolean zoomIn, @NonNull PointF focalPoint) {
CameraPosition cameraPosition = invalidateCameraPosition();
if (cameraPosition != null) {
@@ -221,6 +222,17 @@ final class Transform implements MapView.OnMapChangedListener {
}
}
+ void zoom(double zoomAddition, @NonNull PointF focalPoint, long duration) {
+ CameraPosition cameraPosition = invalidateCameraPosition();
+ if (cameraPosition != null) {
+ int newZoom = (int) Math.round(cameraPosition.zoom + zoomAddition);
+ setZoom(newZoom, focalPoint, duration);
+ } else {
+ // we are not transforming, notify about being idle
+ cameraChangeDispatcher.onCameraIdle();
+ }
+ }
+
void setZoom(double zoom, @NonNull PointF focalPoint) {
setZoom(zoom, focalPoint, 0);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
index 45f72af1c5..1e604c9bef 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
@@ -6,10 +6,10 @@ import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
-import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
@@ -22,7 +22,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
* use {@link com.mapbox.mapboxsdk.maps.UiSettings}.
* </p>
*/
-public final class CompassView extends AppCompatImageView implements Runnable {
+public final class CompassView extends ImageView implements Runnable {
public static final long TIME_WAIT_IDLE = 500;
public static final long TIME_MAP_NORTH_ANIMATION = 150;