summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidSDK/src/main/java
diff options
context:
space:
mode:
authorLangston Smith <langston.smith@mapbox.com>2018-03-05 17:56:29 -0800
committerLangston Smith <langston.smith@mapbox.com>2018-03-05 17:56:29 -0800
commitaab4b971509f6f76943e2578cb13addc13ae079b (patch)
treef671cbf3dc5c7d5078423704df165eeb070f4d0f /platform/android/MapboxGLAndroidSDK/src/main/java
parent6996e2d81abb8ea2ad23a6efe1502809bbe7e882 (diff)
parent136e536159a1e22aa4a92c4e6463893600b809d0 (diff)
downloadqtlocation-mapboxgl-aab4b971509f6f76943e2578cb13addc13ae079b.tar.gz
Merge branch 'master' into ls-android-readme-snapshot-dependency-line-cleanup
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java')
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java160
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java185
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java180
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java214
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java224
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java107
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java29
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java112
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java199
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java18
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java36
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java1079
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java8
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java126
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java57
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java214
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java8
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java34
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java29
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java221
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java132
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java49
41 files changed, 1572 insertions, 2017 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java
deleted file mode 100644
index b7bcb925a1..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package com.almeros.android.multitouch.gesturedetectors;
-
-import android.content.Context;
-import android.view.MotionEvent;
-
-/**
- * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
- * (code.almeros.com)
- * <p>
- * All rights reserved.
- * <p>
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * <p>
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * <p>
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-public abstract class BaseGestureDetector {
- protected final Context context;
- protected boolean gestureInProgress;
-
- protected MotionEvent prevEvent;
- protected MotionEvent currEvent;
-
- protected float currPressure;
- protected float prevPressure;
- protected long timeDelta;
-
- /**
- * This value is the threshold ratio between the previous combined pressure
- * and the current combined pressure. When pressure decreases rapidly
- * between events the position values can often be imprecise, as it usually
- * indicates that the user is in the process of lifting a pointer off of the
- * device. This value was tuned experimentally.
- */
- protected static final float PRESSURE_THRESHOLD = 0.67f;
-
- public BaseGestureDetector(Context context) {
- this.context = context;
- }
-
- /**
- * All gesture detectors need to be called through this method to be able to
- * detect gestures. This method delegates work to handler methods
- * (handleStartProgressEvent, handleInProgressEvent) implemented in
- * extending classes.
- *
- * @param event MotionEvent
- * @return {@code true} as handled
- */
- public boolean onTouchEvent(MotionEvent event) {
- final int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
- if (!gestureInProgress) {
- handleStartProgressEvent(actionCode, event);
- } else {
- handleInProgressEvent(actionCode, event);
- }
- return true;
- }
-
- /**
- * Called when the current event occurred when NO gesture is in progress
- * yet. The handling in this implementation may set the gesture in progress
- * (via gestureInProgress) or out of progress
- *
- * @param actionCode Action Code from MotionEvent
- * @param event MotionEvent
- */
- protected abstract void handleStartProgressEvent(int actionCode,
- MotionEvent event);
-
- /**
- * Called when the current event occurred when a gesture IS in progress. The
- * handling in this implementation may set the gesture out of progress (via
- * gestureInProgress).
- *
- * @param actionCode Action Code from MotionEvent
- * @param event MotionEvent
- */
- protected abstract void handleInProgressEvent(int actionCode,
- MotionEvent event);
-
- protected void updateStateByEvent(MotionEvent curr) {
- final MotionEvent prev = prevEvent;
-
- // Reset currEvent
- if (currEvent != null) {
- currEvent.recycle();
- currEvent = null;
- }
- currEvent = MotionEvent.obtain(curr);
-
- // Delta time
- timeDelta = curr.getEventTime() - prev.getEventTime();
-
- // Pressure
- currPressure = curr.getPressure(curr.getActionIndex());
- prevPressure = prev.getPressure(prev.getActionIndex());
- }
-
- protected void resetState() {
- if (prevEvent != null) {
- prevEvent.recycle();
- prevEvent = null;
- }
- if (currEvent != null) {
- currEvent.recycle();
- currEvent = null;
- }
- gestureInProgress = false;
- }
-
- /**
- * Returns {@code true} if a gesture is currently in progress.
- *
- * @return {@code true} if a gesture is currently in progress, {@code false}
- * otherwise.
- */
- public boolean isInProgress() {
- return gestureInProgress;
- }
-
- /**
- * Return the time difference in milliseconds between the previous accepted
- * GestureDetector event and the current GestureDetector event.
- *
- * @return Time difference since the last move event in milliseconds.
- */
- public long getTimeDelta() {
- return timeDelta;
- }
-
- /**
- * Return the event time of the current GestureDetector event being
- * processed.
- *
- * @return Current GestureDetector event time in milliseconds.
- */
- public long getEventTime() {
- return currEvent.getEventTime();
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java
deleted file mode 100644
index bc7dda6159..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java
+++ /dev/null
@@ -1,185 +0,0 @@
-package com.almeros.android.multitouch.gesturedetectors;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.view.MotionEvent;
-
-/**
- * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
- * (code.almeros.com)
- * <p>
- * All rights reserved.
- * <p>
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * <p>
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * <p>
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-public class MoveGestureDetector extends BaseGestureDetector {
-
- /**
- * Listener which must be implemented which is used by MoveGestureDetector
- * to perform callbacks to any implementing class which is registered to a
- * MoveGestureDetector via the constructor.
- *
- * @see MoveGestureDetector.SimpleOnMoveGestureListener
- */
- public interface OnMoveGestureListener {
- public boolean onMove(MoveGestureDetector detector);
-
- public boolean onMoveBegin(MoveGestureDetector detector);
-
- public void onMoveEnd(MoveGestureDetector detector);
- }
-
- /**
- * Helper class which may be extended and where the methods may be
- * implemented. This way it is not necessary to implement all methods of
- * OnMoveGestureListener.
- */
- public static class SimpleOnMoveGestureListener implements
- OnMoveGestureListener {
- public boolean onMove(MoveGestureDetector detector) {
- return false;
- }
-
- public boolean onMoveBegin(MoveGestureDetector detector) {
- return true;
- }
-
- public void onMoveEnd(MoveGestureDetector detector) {
- // Do nothing, overridden implementation may be used
- }
- }
-
- private static final PointF FOCUS_DELTA_ZERO = new PointF();
-
- private final OnMoveGestureListener listener;
-
- private PointF focusExternal = new PointF();
- private PointF focusDeltaExternal = new PointF();
-
- public MoveGestureDetector(Context context, OnMoveGestureListener listener) {
- super(context);
- this.listener = listener;
- }
-
- @Override
- protected void handleStartProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_DOWN:
- resetState(); // In case we missed an UP/CANCEL event
-
- prevEvent = MotionEvent.obtain(event);
- timeDelta = 0;
-
- updateStateByEvent(event);
- break;
-
- case MotionEvent.ACTION_MOVE:
- gestureInProgress = listener.onMoveBegin(this);
- break;
- }
- }
-
- @Override
- protected void handleInProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- listener.onMoveEnd(this);
- resetState();
- break;
-
- case MotionEvent.ACTION_MOVE:
- updateStateByEvent(event);
-
- // Only accept the event if our relative pressure is within
- // a certain limit. This can help filter shaky data as a
- // finger is lifted.
- if (currPressure / prevPressure > PRESSURE_THRESHOLD) {
- final boolean updatePrevious = listener.onMove(this);
- if (updatePrevious) {
- prevEvent.recycle();
- prevEvent = MotionEvent.obtain(event);
- }
- }
- break;
- }
- }
-
- protected void updateStateByEvent(MotionEvent curr) {
- super.updateStateByEvent(curr);
-
- final MotionEvent prev = prevEvent;
-
- // Focus intenal
- PointF currFocusInternal = determineFocalPoint(curr);
- PointF prevFocusInternal = determineFocalPoint(prev);
-
- // Focus external
- // - Prevent skipping of focus delta when a finger is added or removed
- boolean skipNextMoveEvent = prev.getPointerCount() != curr
- .getPointerCount();
- focusDeltaExternal = skipNextMoveEvent ? FOCUS_DELTA_ZERO
- : new PointF(currFocusInternal.x - prevFocusInternal.x,
- currFocusInternal.y - prevFocusInternal.y);
-
- // - Don't directly use mFocusInternal (or skipping will occur). Add
- // unskipped delta values to focusExternal instead.
- focusExternal.x += focusDeltaExternal.x;
- focusExternal.y += focusDeltaExternal.y;
- }
-
- /**
- * Determine (multi)finger focal point (a.k.a. center point between all
- * fingers)
- *
- * @param motionEvent a {@link MotionEvent} object.
- * @return PointF focal point
- */
- private PointF determineFocalPoint(MotionEvent motionEvent) {
- // Number of fingers on screen
- final int pCount = motionEvent.getPointerCount();
- float x = 0.0f;
- float y = 0.0f;
-
- for (int i = 0; i < pCount; i++) {
- x += motionEvent.getX(i);
- y += motionEvent.getY(i);
- }
-
- return new PointF(x / pCount, y / pCount);
- }
-
- public float getFocusX() {
- return focusExternal.x;
- }
-
- public float getFocusY() {
- return focusExternal.y;
- }
-
- public PointF getFocusDelta() {
- return focusDeltaExternal;
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java
deleted file mode 100644
index 8c111a68df..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java
+++ /dev/null
@@ -1,180 +0,0 @@
-package com.almeros.android.multitouch.gesturedetectors;
-
-import android.content.Context;
-import android.view.MotionEvent;
-
-/**
- * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
- * (code.almeros.com)
- * <p>
- * All rights reserved.
- * <p>
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * <p>
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * <p>
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-public class RotateGestureDetector extends TwoFingerGestureDetector {
-
- /**
- * Listener which must be implemented which is used by RotateGestureDetector
- * to perform callbacks to any implementing class which is registered to a
- * RotateGestureDetector via the constructor.
- *
- * @see RotateGestureDetector.SimpleOnRotateGestureListener
- */
- public interface OnRotateGestureListener {
- public boolean onRotate(RotateGestureDetector detector);
-
- public boolean onRotateBegin(RotateGestureDetector detector);
-
- public void onRotateEnd(RotateGestureDetector detector);
- }
-
- /**
- * Helper class which may be extended and where the methods may be
- * implemented. This way it is not necessary to implement all methods of
- * OnRotateGestureListener.
- */
- public static class SimpleOnRotateGestureListener implements
- OnRotateGestureListener {
- public boolean onRotate(RotateGestureDetector detector) {
- return false;
- }
-
- public boolean onRotateBegin(RotateGestureDetector detector) {
- return true;
- }
-
- public void onRotateEnd(RotateGestureDetector detector) {
- // Do nothing, overridden implementation may be used
- }
- }
-
- private final OnRotateGestureListener listener;
- private boolean sloppyGesture;
-
- public RotateGestureDetector(Context context,
- OnRotateGestureListener listener) {
- super(context);
- this.listener = listener;
- }
-
- @Override
- protected void handleStartProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_POINTER_DOWN:
- // At least the second finger is on screen now
-
- resetState(); // In case we missed an UP/CANCEL event
- prevEvent = MotionEvent.obtain(event);
- timeDelta = 0;
-
- updateStateByEvent(event);
-
- // See if we have a sloppy gesture
- sloppyGesture = isSloppyGesture(event);
- if (!sloppyGesture) {
- // No, start gesture now
- gestureInProgress = listener.onRotateBegin(this);
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (!sloppyGesture) {
- break;
- }
-
- // See if we still have a sloppy gesture
- sloppyGesture = isSloppyGesture(event);
- if (!sloppyGesture) {
- // No, start normal gesture now
- gestureInProgress = listener.onRotateBegin(this);
- }
-
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- if (!sloppyGesture) {
- break;
- }
-
- break;
- }
- }
-
- @Override
- protected void handleInProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_POINTER_UP:
- // Gesture ended but
- updateStateByEvent(event);
-
- if (!sloppyGesture) {
- listener.onRotateEnd(this);
- }
-
- resetState();
- break;
-
- case MotionEvent.ACTION_CANCEL:
- if (!sloppyGesture) {
- listener.onRotateEnd(this);
- }
-
- resetState();
- break;
-
- case MotionEvent.ACTION_MOVE:
- updateStateByEvent(event);
-
- // Only accept the event if our relative pressure is within
- // a certain limit. This can help filter shaky data as a
- // finger is lifted.
- if (currPressure / prevPressure > PRESSURE_THRESHOLD) {
- final boolean updatePrevious = listener.onRotate(this);
- if (updatePrevious) {
- prevEvent.recycle();
- prevEvent = MotionEvent.obtain(event);
- }
- }
- break;
- }
- }
-
- @Override
- protected void resetState() {
- super.resetState();
- sloppyGesture = false;
- }
-
- /**
- * Return the rotation difference from the previous rotate event to the
- * current event.
- *
- * @return The current rotation //difference in degrees.
- */
- public float getRotationDegreesDelta() {
- double diffRadians = Math.atan2(prevFingerDiffY, prevFingerDiffX)
- - Math.atan2(currFingerDiffY, currFingerDiffX);
- return (float) (diffRadians * 180.0 / Math.PI);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java
deleted file mode 100644
index 9396578e48..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java
+++ /dev/null
@@ -1,214 +0,0 @@
-package com.almeros.android.multitouch.gesturedetectors;
-
-import android.content.Context;
-import android.view.MotionEvent;
-
-/**
- * @author Robert Nordan (robert.nordan@norkart.no)
- * <p>
- * Copyright (c) 2013, Norkart AS
- * <p>
- * All rights reserved.
- * <p>
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * <p>
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * <p>
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-public class ShoveGestureDetector extends TwoFingerGestureDetector {
-
- /**
- * Listener which must be implemented which is used by ShoveGestureDetector
- * to perform callbacks to any implementing class which is registered to a
- * ShoveGestureDetector via the constructor.
- *
- * @see ShoveGestureDetector.SimpleOnShoveGestureListener
- */
- public interface OnShoveGestureListener {
- public boolean onShove(ShoveGestureDetector detector);
-
- public boolean onShoveBegin(ShoveGestureDetector detector);
-
- public void onShoveEnd(ShoveGestureDetector detector);
- }
-
- /**
- * Helper class which may be extended and where the methods may be
- * implemented. This way it is not necessary to implement all methods of
- * OnShoveGestureListener.
- */
- public static class SimpleOnShoveGestureListener implements
- OnShoveGestureListener {
- public boolean onShove(ShoveGestureDetector detector) {
- return false;
- }
-
- public boolean onShoveBegin(ShoveGestureDetector detector) {
- return true;
- }
-
- public void onShoveEnd(ShoveGestureDetector detector) {
- // Do nothing, overridden implementation may be used
- }
- }
-
- private float prevAverageY;
- private float currAverageY;
-
- private final OnShoveGestureListener listener;
- private boolean sloppyGesture;
-
- public ShoveGestureDetector(Context context, OnShoveGestureListener listener) {
- super(context);
- this.listener = listener;
- }
-
- @Override
- protected void handleStartProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_POINTER_DOWN:
- // At least the second finger is on screen now
-
- resetState(); // In case we missed an UP/CANCEL event
- prevEvent = MotionEvent.obtain(event);
- timeDelta = 0;
-
- updateStateByEvent(event);
-
- // See if we have a sloppy gesture
- sloppyGesture = isSloppyGesture(event);
- if (!sloppyGesture) {
- // No, start gesture now
- gestureInProgress = listener.onShoveBegin(this);
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (!sloppyGesture) {
- break;
- }
-
- // See if we still have a sloppy gesture
- sloppyGesture = isSloppyGesture(event);
- if (!sloppyGesture) {
- // No, start normal gesture now
- gestureInProgress = listener.onShoveBegin(this);
- }
-
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- if (!sloppyGesture) {
- break;
- }
-
- break;
- }
- }
-
- @Override
- protected void handleInProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_POINTER_UP:
- // Gesture ended but
- updateStateByEvent(event);
-
- if (!sloppyGesture) {
- listener.onShoveEnd(this);
- }
-
- resetState();
- break;
-
- case MotionEvent.ACTION_CANCEL:
- if (!sloppyGesture) {
- listener.onShoveEnd(this);
- }
-
- resetState();
- break;
-
- case MotionEvent.ACTION_MOVE:
- updateStateByEvent(event);
-
- // Only accept the event if our relative pressure is within
- // a certain limit. This can help filter shaky data as a
- // finger is lifted. Also check that shove is meaningful.
- if (currPressure / prevPressure > PRESSURE_THRESHOLD
- && Math.abs(getShovePixelsDelta()) > 0.5f) {
- final boolean updatePrevious = listener.onShove(this);
- if (updatePrevious) {
- prevEvent.recycle();
- prevEvent = MotionEvent.obtain(event);
- }
- }
- break;
- }
- }
-
- @Override
- protected void resetState() {
- super.resetState();
- sloppyGesture = false;
- prevAverageY = 0.0f;
- currAverageY = 0.0f;
- }
-
- @Override
- protected void updateStateByEvent(MotionEvent curr) {
- super.updateStateByEvent(curr);
-
- final MotionEvent prev = prevEvent;
- float py0 = prev.getY(0);
- float py1 = prev.getY(1);
- prevAverageY = (py0 + py1) / 2.0f;
-
- float cy0 = curr.getY(0);
- float cy1 = curr.getY(1);
- currAverageY = (cy0 + cy1) / 2.0f;
- }
-
- @Override
- protected boolean isSloppyGesture(MotionEvent event) {
- boolean sloppy = super.isSloppyGesture(event);
- if (sloppy) {
- return true;
- }
-
- // If it's not traditionally sloppy, we check if the angle between
- // fingers
- // is acceptable.
- double angle = Math.abs(Math.atan2(currFingerDiffY, currFingerDiffX));
- // about 20 degrees, left or right
- return !((0.0f < angle && angle < 0.35f) || 2.79f < angle
- && angle < Math.PI);
- }
-
- /**
- * Return the distance in pixels from the previous shove event to the
- * current event.
- *
- * @return The current distance in pixels.
- */
- public float getShovePixelsDelta() {
- return currAverageY - prevAverageY;
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java
deleted file mode 100644
index db492b6556..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java
+++ /dev/null
@@ -1,224 +0,0 @@
-package com.almeros.android.multitouch.gesturedetectors;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.util.DisplayMetrics;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-/**
- * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
- * (code.almeros.com)
- * <p>
- * All rights reserved.
- * <p>
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * <p>
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * <p>
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-public abstract class TwoFingerGestureDetector extends BaseGestureDetector {
-
- private final float edgeSlop;
-
- protected float prevFingerDiffX;
- protected float prevFingerDiffY;
- protected float currFingerDiffX;
- protected float currFingerDiffY;
-
- private float currLen;
- private float prevLen;
-
- private PointF focus;
-
- public TwoFingerGestureDetector(Context context) {
- super(context);
-
- ViewConfiguration config = ViewConfiguration.get(context);
-
- edgeSlop = config.getScaledEdgeSlop();
- }
-
- @Override
- protected abstract void handleStartProgressEvent(int actionCode,
- MotionEvent event);
-
- @Override
- protected abstract void handleInProgressEvent(int actionCode,
- MotionEvent event);
-
- protected void updateStateByEvent(MotionEvent curr) {
- super.updateStateByEvent(curr);
-
- final MotionEvent prev = prevEvent;
-
- currLen = -1;
- prevLen = -1;
-
- // Previous
- final float px0 = prev.getX(0);
- final float py0 = prev.getY(0);
- final float px1 = prev.getX(1);
- final float py1 = prev.getY(1);
- final float pvx = px1 - px0;
- final float pvy = py1 - py0;
- prevFingerDiffX = pvx;
- prevFingerDiffY = pvy;
-
- // Current
- final float cx0 = curr.getX(0);
- final float cy0 = curr.getY(0);
- final float cx1 = curr.getX(1);
- final float cy1 = curr.getY(1);
- final float cvx = cx1 - cx0;
- final float cvy = cy1 - cy0;
- currFingerDiffX = cvx;
- currFingerDiffY = cvy;
- focus = determineFocalPoint(curr);
- }
-
- /**
- * Return the current distance between the two pointers forming the gesture
- * in progress.
- *
- * @return Distance between pointers in pixels.
- */
- public float getCurrentSpan() {
- if (currLen == -1) {
- final float cvx = currFingerDiffX;
- final float cvy = currFingerDiffY;
- currLen = (float) Math.sqrt(cvx * cvx + cvy * cvy);
- }
- return currLen;
- }
-
- /**
- * Return the previous distance between the two pointers forming the gesture
- * in progress.
- *
- * @return Previous distance between pointers in pixels.
- */
- public float getPreviousSpan() {
- if (prevLen == -1) {
- final float pvx = prevFingerDiffX;
- final float pvy = prevFingerDiffY;
- prevLen = (float) Math.sqrt(pvx * pvx + pvy * pvy);
- }
- return prevLen;
- }
-
- /**
- * MotionEvent has no getRawX(int) method; simulate it pending future API
- * approval.
- *
- * @param event Motion Event
- * @param pointerIndex Pointer Index
- * @return Raw x value or 0
- */
- protected static float getRawX(MotionEvent event, int pointerIndex) {
- float offset = event.getRawX() - event.getX();
- if (pointerIndex < event.getPointerCount()) {
- return event.getX(pointerIndex) + offset;
- }
- return 0.0f;
- }
-
- /**
- * MotionEvent has no getRawY(int) method; simulate it pending future API
- * approval.
- *
- * @param event Motion Event
- * @param pointerIndex Pointer Index
- * @return Raw y value or 0
- */
- protected static float getRawY(MotionEvent event, int pointerIndex) {
- float offset = event.getRawY() - event.getY();
- if (pointerIndex < event.getPointerCount()) {
- return event.getY(pointerIndex) + offset;
- }
- return 0.0f;
- }
-
- /**
- * Check if we have a sloppy gesture. Sloppy gestures can happen if the edge
- * of the user's hand is touching the screen, for example.
- *
- * @param event Motion Event
- * @return {@code true} if is sloppy gesture, {@code false} if not
- */
- protected boolean isSloppyGesture(MotionEvent event) {
- // As orientation can change, query the metrics in touch down
- DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- float rightSlopEdge = metrics.widthPixels - edgeSlop;
- float bottomSlopEdge = metrics.heightPixels - edgeSlop;
-
- final float edgeSlop = this.edgeSlop;
-
- final float x0 = event.getRawX();
- final float y0 = event.getRawY();
- final float x1 = getRawX(event, 1);
- final float y1 = getRawY(event, 1);
-
- boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlopEdge
- || y0 > bottomSlopEdge;
- boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlopEdge
- || y1 > bottomSlopEdge;
-
- if (p0sloppy && p1sloppy) {
- return true;
- } else if (p0sloppy) {
- return true;
- } else if (p1sloppy) {
- return true;
- }
- return false;
- }
-
- /**
- * Determine (multi)finger focal point (a.k.a. center point between all
- * fingers)
- *
- * @param motionEvent Motion Event
- * @return PointF focal point
- */
- public static PointF determineFocalPoint(MotionEvent motionEvent) {
- // Number of fingers on screen
- final int pCount = motionEvent.getPointerCount();
- float x = 0.0f;
- float y = 0.0f;
-
- for (int i = 0; i < pCount; i++) {
- x += motionEvent.getX(i);
- y += motionEvent.getY(i);
- }
-
- return new PointF(x / pCount, y / pCount);
- }
-
- public float getFocusX() {
- return focus.x;
- }
-
- public float getFocusY() {
- return focus.y;
- }
-
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java
deleted file mode 100644
index cff2f086dc..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Do not use this package. Internal use only.
- */
-package com.almeros.android.multitouch.gesturedetectors;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java
deleted file mode 100644
index 8ea7e61eee..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/EmptyLocationSource.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package com.mapbox.mapboxsdk;
-
-
-import android.location.Location;
-import android.support.annotation.Nullable;
-
-import com.mapbox.mapboxsdk.location.LocationSource;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
-
-class EmptyLocationSource extends LocationSource {
-
- /**
- * Activate the location engine which will connect whichever location provider you are using. You'll need to call
- * this before requesting user location updates using {@link LocationEngine#requestLocationUpdates()}.
- */
- @Override
- public void activate() {
- // Intentionally left empty
- }
-
- /**
- * Disconnect the location engine which is useful when you no longer need location updates or requesting the users
- * {@link LocationEngine#getLastLocation()}. Before deactivating, you'll need to stop request user location updates
- * using {@link LocationEngine#removeLocationUpdates()}.
- */
- @Override
- public void deactivate() {
- // Intentionally left empty
- }
-
- /**
- * Check if your location provider has been activated/connected. This is mainly used internally but is also useful in
- * the rare case when you'd like to know if your location engine is connected or not.
- *
- * @return boolean true if the location engine has been activated/connected, else false.
- */
- @Override
- public boolean isConnected() {
- return false;
- }
-
- /**
- * Returns the Last known location is the location provider is connected and location permissions are granted.
- *
- * @return the last known location
- */
- @Override
- @Nullable
- public Location getLastLocation() {
- return null;
- }
-
- /**
- * Request location updates to the location provider.
- */
- @Override
- public void requestLocationUpdates() {
- // Intentionally left empty
- }
-
- /**
- * Dismiss ongoing location update to the location provider.
- */
- @Override
- public void removeLocationUpdates() {
- // Intentionally left empty
- }
-
- /**
- * Invoked when the Location has changed.
- *
- * @param location the new location
- */
- @Override
- public void onLocationChanged(Location location) {
- // Intentionally left empty
- }
-
- /**
- * Useful when you'd like to add a location listener to handle location connections and update events. It is important
- * to note, that the callback will continue getting called even when your application isn't in the foreground.
- * Therefore, it is a good idea to use {@link LocationEngine#removeLocationEngineListener(LocationEngineListener)}
- * inside your activities {@code onStop()} method.
- *
- * @param listener A {@link LocationEngineListener} which you'd like to add to your location engine.
- * @since 2.0.0
- */
- @Override
- public void addLocationEngineListener(LocationEngineListener listener) {
- // Intentionally left empty
- }
-
- /**
- * If you no longer need your {@link LocationEngineListener} to be invoked with every location update, use this
- * method to remove it. It's also important to remove your listeners before the activity is destroyed to prevent any
- * potential memory leaks.
- *
- * @param listener the {@link LocationEngineListener} you'd like to remove from this {@link LocationEngine}.
- * @return true.
- * @since 2.0.0
- */
- @Override
- public boolean removeLocationEngineListener(LocationEngineListener listener) {
- return true;
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java
index b67b6e96f2..853ea1c11b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java
@@ -8,16 +8,12 @@ import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.text.TextUtils;
+import com.mapbox.android.core.location.LocationEngine;
+import com.mapbox.android.core.location.LocationEnginePriority;
+import com.mapbox.android.core.location.LocationEngineProvider;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException;
-import com.mapbox.mapboxsdk.location.LocationSource;
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
-import com.mapbox.services.android.telemetry.MapboxTelemetry;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
-import com.mapbox.services.android.telemetry.location.LocationEngineProvider;
-
-import timber.log.Timber;
/**
* The entry point to initialize the Mapbox Android SDK.
@@ -56,15 +52,9 @@ public final class Mapbox {
INSTANCE = new Mapbox(appContext, accessToken, locationEngine);
locationEngine.setPriority(LocationEnginePriority.NO_POWER);
- try {
- MapboxTelemetry.getInstance().initialize(
- appContext, accessToken, BuildConfig.MAPBOX_EVENTS_USER_AGENT, locationEngine);
- } catch (Exception exception) {
- Timber.e(exception, "Unable to instantiate Mapbox telemetry");
- }
-
ConnectivityReceiver.instance(appContext);
}
+
return INSTANCE;
}
@@ -146,22 +136,11 @@ public final class Mapbox {
}
/**
- * Returns a location source instance with empty methods.
- *
- * @return an empty location source implementation
- * @deprecated Replaced by {@link Mapbox#getLocationEngine()}
- */
- @Deprecated
- public static LocationSource getLocationSource() {
- return new EmptyLocationSource();
- }
-
-
- /**
* Returns the location engine used by the SDK.
*
* @return the location engine configured
*/
+ // TODO Do we need to expose this?
public static LocationEngine getLocationEngine() {
return INSTANCE.locationEngine;
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
index c2f19072db..e732b2525f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
@@ -8,7 +8,7 @@ import android.support.annotation.FloatRange;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.services.android.telemetry.utils.MathUtils;
+import com.mapbox.mapboxsdk.utils.MathUtils;
/**
* Resembles the position, angle, zoom and tilt of the user's viewpoint.
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java
index 1a7544d33a..7a17e500ca 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java
@@ -37,6 +37,20 @@ public class GeometryConstants {
public static final double MIN_LATITUDE = -90;
/**
+ * This constant represents the latitude span when representing a geolocation.
+ *
+ * @since 6.0.0
+ */
+ public static final double LATITUDE_SPAN = 180;
+
+ /**
+ * This constant represents the longitude span when representing a geolocation.
+ *
+ * @since 6.0.0
+ */
+ public static final double LONGITUDE_SPAN = 360;
+
+ /**
* This constant represents the highest latitude value available to represent a geolocation.
*
* @since 6.0.0
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
index 60362dd2e9..6f263e4635 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
@@ -48,6 +48,31 @@ public class MapboxConstants {
public static final long VELOCITY_THRESHOLD_IGNORE_FLING = 1000;
/**
+ * Value by which the default rotation threshold will be increased when scaling
+ */
+ public static final float ROTATION_THRESHOLD_INCREASE_WHEN_SCALING = 25f;
+
+ /**
+ * Time within which user needs to lift fingers for velocity animation to start.
+ */
+ public static final long SCHEDULED_ANIMATION_TIMEOUT = 150L;
+
+ /**
+ * Minimum angular velocity for rotation animation
+ */
+ public static final float MINIMUM_ANGULAR_VELOCITY = 1.5f;
+
+ /**
+ * Maximum angular velocity for rotation animation
+ */
+ public static final float MAXIMUM_ANGULAR_VELOCITY = 20f;
+
+ /**
+ * Factor to calculate tilt change based on pixel change during shove gesture.
+ */
+ public static final float SHOVE_PIXEL_CHANGE_FACTOR = 0.1f;
+
+ /**
* The currently supported minimum zoom level.
*/
public static final float MINIMUM_ZOOM = 0.0f;
@@ -78,14 +103,14 @@ public class MapboxConstants {
public static final double MINIMUM_DIRECTION = 0;
/**
- * The currently used minimun scale factor to clamp to when a quick zoom gesture occurs
+ * The currently used minimum scale factor to clamp to when a quick zoom gesture occurs
*/
public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.00f;
/**
* The currently used maximum scale factor to clamp to when a quick zoom gesture occurs
*/
- public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.45f;
+ public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.15f;
/**
* Fragment Argument Key for MapboxMapOptions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
index ceac862f39..c042b00577 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
@@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public class MyBearingTracking {
- @IntDef( {NONE, COMPASS, GPS})
+ @IntDef( {NONE, COMPASS, GPS, GPS_NORTH_FACING})
@Retention(RetentionPolicy.SOURCE)
public @interface Mode {
}
@@ -42,4 +42,9 @@ public class MyBearingTracking {
*/
public static final int GPS = 0x00000008;
+ /**
+ * Tracking the bearing of the user based on GPS data, but camera always faces north direction
+ */
+ public static final int GPS_NORTH_FACING = 0x0000000B;
+
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
index cf647224ae..fc8d2ec8f0 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java
@@ -29,6 +29,10 @@ public class LatLngBounds implements Parcelable {
* Construct a new LatLngBounds based on its corners, given in NESW
* order.
*
+ * If eastern longitude is smaller than the western one, bounds will include antimeridian.
+ * For example, if the NE point is (10, -170) and the SW point is (-10, 170), then bounds will span over 20 degrees
+ * and cross the antimeridian.
+ *
* @param northLatitude Northern Latitude
* @param eastLongitude Eastern Longitude
* @param southLatitude Southern Latitude
@@ -48,10 +52,9 @@ public class LatLngBounds implements Parcelable {
* @return the bounds representing the world
*/
public static LatLngBounds world() {
- return new LatLngBounds.Builder()
- .include(new LatLng(GeometryConstants.MAX_LATITUDE, GeometryConstants.MAX_LONGITUDE))
- .include(new LatLng(GeometryConstants.MIN_LATITUDE, GeometryConstants.MIN_LONGITUDE))
- .build();
+ return LatLngBounds.from(
+ GeometryConstants.MAX_LATITUDE, GeometryConstants.MAX_LONGITUDE,
+ GeometryConstants.MIN_LATITUDE, GeometryConstants.MIN_LONGITUDE);
}
/**
@@ -61,8 +64,21 @@ public class LatLngBounds implements Parcelable {
* @return LatLng center of this LatLngBounds
*/
public LatLng getCenter() {
- return new LatLng((this.latitudeNorth + this.latitudeSouth) / 2,
- (this.longitudeEast + this.longitudeWest) / 2);
+ double latCenter = (this.latitudeNorth + this.latitudeSouth) / 2.0;
+ double longCenter;
+
+ if (this.longitudeEast > this.longitudeWest) {
+ longCenter = (this.longitudeEast + this.longitudeWest) / 2;
+ } else {
+ double halfSpan = (GeometryConstants.LONGITUDE_SPAN + this.longitudeEast - this.longitudeWest) / 2.0;
+ longCenter = this.longitudeWest + halfSpan;
+ if (longCenter >= GeometryConstants.MAX_LONGITUDE) {
+ longCenter = this.longitudeEast - halfSpan;
+ }
+ return new LatLng(latCenter, longCenter);
+ }
+
+ return new LatLng(latCenter, longCenter);
}
/**
@@ -163,10 +179,26 @@ public class LatLngBounds implements Parcelable {
* @return Span distance
*/
public double getLongitudeSpan() {
- return Math.abs(this.longitudeEast - this.longitudeWest);
+ double longSpan = Math.abs(this.longitudeEast - this.longitudeWest);
+ if (this.longitudeEast > this.longitudeWest) {
+ return longSpan;
+ }
+
+ // shortest span contains antimeridian
+ return GeometryConstants.LONGITUDE_SPAN - longSpan;
}
+ static double getLongitudeSpan(final double longEast, final double longWest) {
+ double longSpan = Math.abs(longEast - longWest);
+ if (longEast > longWest) {
+ return longSpan;
+ }
+
+ // shortest span contains antimeridian
+ return GeometryConstants.LONGITUDE_SPAN - longSpan;
+ }
+
/**
* Validate if LatLngBounds is empty, determined if absolute distance is
*
@@ -196,21 +228,44 @@ public class LatLngBounds implements Parcelable {
*/
static LatLngBounds fromLatLngs(final List<? extends ILatLng> latLngs) {
double minLat = GeometryConstants.MAX_LATITUDE;
- double minLon = GeometryConstants.MAX_LONGITUDE;
double maxLat = GeometryConstants.MIN_LATITUDE;
- double maxLon = GeometryConstants.MIN_LONGITUDE;
+
+ double eastLon = latLngs.get(0).getLongitude();
+ double westLon = latLngs.get(1).getLongitude();
+ double lonSpan = Math.abs(eastLon - westLon);
+ if (lonSpan < GeometryConstants.LONGITUDE_SPAN / 2) {
+ if (eastLon < westLon) {
+ double temp = eastLon;
+ eastLon = westLon;
+ westLon = temp;
+ }
+ } else {
+ lonSpan = GeometryConstants.LONGITUDE_SPAN - lonSpan;
+ if (westLon < eastLon) {
+ double temp = eastLon;
+ eastLon = westLon;
+ westLon = temp;
+ }
+ }
for (final ILatLng gp : latLngs) {
final double latitude = gp.getLatitude();
- final double longitude = gp.getLongitude();
-
minLat = Math.min(minLat, latitude);
- minLon = Math.min(minLon, longitude);
maxLat = Math.max(maxLat, latitude);
- maxLon = Math.max(maxLon, longitude);
+
+ final double longitude = gp.getLongitude();
+ if (!containsLongitude(eastLon, westLon, longitude)) {
+ final double eastSpan = getLongitudeSpan(longitude, westLon);
+ final double westSpan = getLongitudeSpan(eastLon, longitude);
+ if (eastSpan <= westSpan) {
+ eastLon = longitude;
+ } else {
+ westLon = longitude;
+ }
+ }
}
- return new LatLngBounds(maxLat, maxLon, minLat, minLon);
+ return new LatLngBounds(maxLat, eastLon, minLat, westLon);
}
/**
@@ -322,6 +377,24 @@ public class LatLngBounds implements Parcelable {
return false;
}
+
+ private boolean containsLatitude(final double latitude) {
+ return (latitude <= this.latitudeNorth)
+ && (latitude >= this.latitudeSouth);
+ }
+
+ private boolean containsLongitude(final double longitude) {
+ return containsLongitude(this.longitudeEast, this.longitudeWest, longitude);
+ }
+
+ static boolean containsLongitude(final double eastLon, final double westLon, final double longitude) {
+ if (eastLon > westLon) {
+ return (longitude <= eastLon)
+ && (longitude >= westLon);
+ }
+ return (longitude < eastLon) || (longitude > westLon);
+ }
+
/**
* Determines whether this LatLngBounds contains a point.
*
@@ -329,12 +402,8 @@ public class LatLngBounds implements Parcelable {
* @return true, if the point is contained within the bounds
*/
public boolean contains(final ILatLng latLng) {
- final double latitude = latLng.getLatitude();
- final double longitude = latLng.getLongitude();
- return ((latitude <= this.latitudeNorth)
- && (latitude >= this.latitudeSouth))
- && ((longitude <= this.longitudeEast)
- && (longitude >= this.longitudeWest));
+ return containsLatitude(latLng.getLatitude())
+ && containsLongitude(latLng.getLongitude());
}
/**
@@ -344,7 +413,8 @@ public class LatLngBounds implements Parcelable {
* @return true, if the bounds is contained within the bounds
*/
public boolean contains(final LatLngBounds other) {
- return contains(other.getNorthEast()) && contains(other.getSouthWest());
+ return contains(other.getNorthEast())
+ && contains(other.getSouthWest());
}
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java
deleted file mode 100644
index 1313587158..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.mapbox.mapboxsdk.location;
-
-import android.content.Context;
-import android.location.Location;
-import android.support.annotation.Nullable;
-
-import com.mapbox.mapboxsdk.Mapbox;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
-import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
-import com.mapzen.android.lost.api.LocationListener;
-import com.mapzen.android.lost.api.LocationRequest;
-import com.mapzen.android.lost.api.LocationServices;
-import com.mapzen.android.lost.api.LostApiClient;
-
-/**
- * LocationEngine using the Open Source Lost library
- * Manages locational updates. Contains methods to register and unregister location listeners.
- * <ul>
- * <li>You can register a LocationEngineListener with LocationSource#addLocationEngineListener(LocationEngineListener)
- * to receive location updates.</li>
- * <li> You can unregister a LocationEngineListener with
- * LocationEngine#removeLocationEngineListener(LocationEngineListener)} to stop receiving location updates.</li>
- * </ul>
- * <p>
- * Note: If registering a listener in your Activity.onStart() implementation, you should unregister it in
- * Activity.onStop(). (You won't receive location updates when paused, and this will cut down on unnecessary system
- * overhead). Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back
- * in the history stack.
- * </p>
- *
- * @deprecated Use a {@link Mapbox#getLocationEngine()} instead.
- */
-@Deprecated
-public class LocationSource extends LocationEngine implements LostApiClient.ConnectionCallbacks, LocationListener {
-
- private Context context;
- private LostApiClient lostApiClient;
-
- /**
- * Constructs a location source instance.
- *
- * @param context the context from which the Application context will be derived.
- */
- public LocationSource(Context context) {
- super();
- this.context = context.getApplicationContext();
- lostApiClient = new LostApiClient.Builder(this.context)
- .addConnectionCallbacks(this)
- .build();
- }
-
- /**
- * Constructs a location source instance.
- * Needed to create empty location source implementations.
- */
- public LocationSource() {
- }
-
- /**
- * Activate the location engine which will connect whichever location provider you are using. You'll need to call
- * this before requesting user location updates using {@link LocationEngine#requestLocationUpdates()}.
- */
- @Override
- public void activate() {
- connect();
- }
-
- /**
- * Disconnect the location engine which is useful when you no longer need location updates or requesting the users
- * {@link LocationEngine#getLastLocation()}. Before deactivating, you'll need to stop request user location updates
- * using {@link LocationEngine#removeLocationUpdates()}.
- */
- @Override
- public void deactivate() {
- if (lostApiClient != null && lostApiClient.isConnected()) {
- lostApiClient.disconnect();
- }
- }
-
- /**
- * Check if your location provider has been activated/connected. This is mainly used internally but is also useful in
- * the rare case when you'd like to know if your location engine is connected or not.
- *
- * @return boolean true if the location engine has been activated/connected, else false.
- */
- @Override
- public boolean isConnected() {
- return lostApiClient.isConnected();
- }
-
- /**
- * Invoked when the location provider has connected.
- */
- @Override
- public void onConnected() {
- for (LocationEngineListener listener : locationListeners) {
- listener.onConnected();
- }
- }
-
- /**
- * Invoked when the location provider connection has been suspended.
- */
- @Override
- public void onConnectionSuspended() {
- // Empty
- }
-
- /**
- * Returns the Last known location is the location provider is connected and location permissions are granted.
- *
- * @return the last known location
- */
- @Override
- @Nullable
- public Location getLastLocation() {
- if (lostApiClient.isConnected()) {
- //noinspection MissingPermission
- return LocationServices.FusedLocationApi.getLastLocation(lostApiClient);
- }
- return null;
- }
-
- /**
- * Request location updates to the location provider.
- */
- @Override
- public void requestLocationUpdates() {
- LocationRequest request = LocationRequest.create();
-
- if (interval != null) {
- request.setInterval(interval);
- }
- if (fastestInterval != null) {
- request.setFastestInterval(fastestInterval);
- }
- if (smallestDisplacement != null) {
- request.setSmallestDisplacement(smallestDisplacement);
- }
-
- if (priority == LocationEnginePriority.NO_POWER) {
- request.setPriority(LocationRequest.PRIORITY_NO_POWER);
- } else if (priority == LocationEnginePriority.LOW_POWER) {
- request.setPriority(LocationRequest.PRIORITY_LOW_POWER);
- } else if (priority == LocationEnginePriority.BALANCED_POWER_ACCURACY) {
- request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
- } else if (priority == LocationEnginePriority.HIGH_ACCURACY) {
- request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
- }
-
- if (lostApiClient.isConnected()) {
- //noinspection MissingPermission
- LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, this);
- }
- }
-
- /**
- * Dismiss ongoing location update to the location provider.
- */
- @Override
- public void removeLocationUpdates() {
- if (lostApiClient.isConnected()) {
- LocationServices.FusedLocationApi.removeLocationUpdates(lostApiClient, this);
- }
- }
-
- /**
- * Returns the location engine type.
- *
- * @return Lost type
- */
- @Override
- public Type obtainType() {
- return Type.LOST;
- }
-
- /**
- * Invoked when the Location has changed.
- *
- * @param location the new location
- */
- @Override
- public void onLocationChanged(Location location) {
- for (LocationEngineListener listener : locationListeners) {
- listener.onLocationChanged(location);
- }
- }
-
- private void connect() {
- if (lostApiClient != null) {
- if (lostApiClient.isConnected()) {
- onConnected();
- } else {
- lostApiClient.connect();
- }
- }
- }
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java
deleted file mode 100644
index b27559e95e..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Contains the Mapbox Maps Android Location API classes.
- */
-package com.mapbox.mapboxsdk.location; \ No newline at end of file
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 b12757c81e..5ccd6bd795 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,5 +1,6 @@
package com.mapbox.mapboxsdk.maps;
+import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -16,7 +17,6 @@ import com.mapbox.mapboxsdk.attribution.Attribution;
import com.mapbox.mapboxsdk.attribution.AttributionParser;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.style.sources.Source;
-import com.mapbox.services.android.telemetry.MapboxTelemetry;
import java.util.ArrayList;
import java.util.List;
@@ -49,7 +49,17 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt
@Override
public void onClick(View view) {
attributionSet = new AttributionBuilder(mapboxMap).build();
- showAttributionDialog(getAttributionTitles());
+
+ boolean isActivityFinishing = false;
+ if (context instanceof Activity) {
+ isActivityFinishing = ((Activity) context).isFinishing();
+ }
+
+ // check is hosting activity isn't finishing
+ // https://github.com/mapbox/mapbox-gl-native/issues/11238
+ if (!isActivityFinishing) {
+ showAttributionDialog(getAttributionTitles());
+ }
}
protected void showAttributionDialog(String[] attributionTitles) {
@@ -88,7 +98,7 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt
builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- MapboxTelemetry.getInstance().setTelemetryEnabled(true);
+ Events.obtainTelemetry().enable();
dialog.cancel();
}
});
@@ -102,7 +112,7 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt
builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- MapboxTelemetry.getInstance().setTelemetryEnabled(false);
+ Events.obtainTelemetry().disable();
dialog.cancel();
}
});
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java
new file mode 100644
index 0000000000..a68d4763ac
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Events.java
@@ -0,0 +1,36 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import com.mapbox.android.telemetry.MapboxTelemetry;
+import com.mapbox.android.telemetry.TelemetryEnabler;
+import com.mapbox.mapboxsdk.BuildConfig;
+import com.mapbox.mapboxsdk.Mapbox;
+
+class Events {
+ static final String TWO_FINGER_TAP = "TwoFingerTap";
+ static final String DOUBLE_TAP = "DoubleTap";
+ static final String SINGLE_TAP = "SingleTap";
+ static final String PAN = "Pan";
+ static final String PINCH = "Pinch";
+ static final String ROTATION = "Rotation";
+ static final String PITCH = "Pitch";
+ private MapboxTelemetry telemetry;
+
+ private Events() {
+ telemetry = new MapboxTelemetry(Mapbox.getApplicationContext(), Mapbox.getAccessToken(),
+ BuildConfig.MAPBOX_EVENTS_USER_AGENT);
+ TelemetryEnabler.State telemetryState = TelemetryEnabler.retrieveTelemetryStateFromPreferences();
+ if (TelemetryEnabler.State.NOT_INITIALIZED.equals(telemetryState)
+ || TelemetryEnabler.State.ENABLED.equals(telemetryState)) {
+ telemetry.enable();
+ }
+ }
+
+ private static class EventsHolder {
+ private static final Events INSTANCE = new Events();
+ }
+
+ static MapboxTelemetry obtainTelemetry() {
+ return EventsHolder.INSTANCE.telemetry;
+ }
+}
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 1788cb4428..5f5a10d0d0 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
@@ -5,27 +5,31 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
-import android.location.Location;
+import android.os.Handler;
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;
-import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector;
-import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector;
+import android.view.animation.DecelerateInterpolator;
+
+import com.mapbox.android.gestures.AndroidGesturesManager;
+import com.mapbox.android.gestures.MoveGestureDetector;
+import com.mapbox.android.gestures.MultiFingerTapGestureDetector;
+import com.mapbox.android.gestures.RotateGestureDetector;
+import com.mapbox.android.gestures.ShoveGestureDetector;
+import com.mapbox.android.gestures.StandardGestureDetector;
+import com.mapbox.android.gestures.StandardScaleGestureDetector;
+import com.mapbox.android.telemetry.Event;
+import com.mapbox.android.telemetry.MapEventFactory;
+import com.mapbox.android.telemetry.MapState;
+import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.services.android.telemetry.MapboxEvent;
-import com.mapbox.services.android.telemetry.MapboxTelemetry;
-import com.mapbox.services.android.telemetry.utils.MathUtils;
-import com.mapbox.services.android.telemetry.utils.TelemetryUtils;
+import com.mapbox.mapboxsdk.utils.MathUtils;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION;
@@ -33,24 +37,15 @@ import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.RE
/**
* Manages gestures events on a MapView.
- * <p>
- * Relies on gesture detection code in almeros.android.multitouch.gesturedetectors.
- * </p>
*/
final class MapGestureDetector {
private final Transform transform;
private final Projection projection;
private final UiSettings uiSettings;
- private final TrackingSettings trackingSettings;
private final AnnotationManager annotationManager;
private final CameraChangeDispatcher cameraChangeDispatcher;
- private GestureDetectorCompat gestureDetector;
- private ScaleGestureDetector scaleGestureDetector;
- private RotateGestureDetector rotateGestureDetector;
- private ShoveGestureDetector shoveGestureDetector;
-
// deprecated map touch API
private MapboxMap.OnMapClickListener onMapClickListener;
private MapboxMap.OnMapLongClickListener onMapLongClickListener;
@@ -70,43 +65,73 @@ final class MapGestureDetector {
private final CopyOnWriteArrayList<MapboxMap.OnScrollListener> onScrollListenerList
= new CopyOnWriteArrayList<>();
- private PointF focalPoint;
+ private final CopyOnWriteArrayList<MapboxMap.OnMoveListener> onMoveListenerList
+ = new CopyOnWriteArrayList<>();
- private boolean twoTap;
- private boolean quickZoom;
- private boolean tiltGestureOccurred;
- private boolean scrollGestureOccurred;
+ private final CopyOnWriteArrayList<MapboxMap.OnRotateListener> onRotateListenerList
+ = new CopyOnWriteArrayList<>();
- private boolean scaleGestureOccurred;
- private boolean recentScaleGestureOccurred;
- private long scaleBeginTime;
+ private final CopyOnWriteArrayList<MapboxMap.OnScaleListener> onScaleListenerList
+ = new CopyOnWriteArrayList<>();
- private boolean scaleAnimating;
- private boolean rotateAnimating;
+ private final CopyOnWriteArrayList<MapboxMap.OnShoveListener> onShoveListenerList
+ = new CopyOnWriteArrayList<>();
- private VelocityTracker velocityTracker;
- private boolean wasZoomingIn;
- private boolean wasClockwiseRotating;
- private boolean rotateGestureOccurred;
+ /**
+ * User-set focal point.
+ */
+ private PointF focalPoint;
+
+ private AndroidGesturesManager gesturesManager;
+ private boolean executeDoubleTap;
+
+ private Animator scaleAnimator;
+ private Animator rotateAnimator;
+ private final List<Animator> scheduledAnimators = new ArrayList<>();
+
+ /**
+ * Cancels scheduled velocity animations if user doesn't lift fingers within
+ * {@link MapboxConstants#SCHEDULED_ANIMATION_TIMEOUT}
+ */
+ private Handler animationsTimeoutHandler = new Handler();
MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings,
- TrackingSettings trackingSettings, AnnotationManager annotationManager,
- CameraChangeDispatcher cameraChangeDispatcher) {
+ AnnotationManager annotationManager, CameraChangeDispatcher cameraChangeDispatcher) {
this.annotationManager = annotationManager;
this.transform = transform;
this.projection = projection;
this.uiSettings = uiSettings;
- this.trackingSettings = trackingSettings;
this.cameraChangeDispatcher = cameraChangeDispatcher;
- // Touch gesture detectors
+ // Checking for context != null for testing purposes
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());
+ gesturesManager = new AndroidGesturesManager(context);
+
+ Set<Integer> shoveScaleSet = new HashSet<>();
+ shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE);
+ shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE);
+
+ Set<Integer> shoveRotateSet = new HashSet<>();
+ shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE);
+ shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_ROTATE);
+
+ Set<Integer> ScaleLongPressSet = new HashSet<>();
+ ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE);
+ ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_LONG_PRESS);
+
+ gesturesManager.setMutuallyExclusiveGestures(shoveScaleSet, shoveRotateSet, ScaleLongPressSet);
+
+ gesturesManager.setStandardGestureListener(new StandardGestureListener());
+ gesturesManager.setMoveGestureListener(new MoveGestureListener());
+ gesturesManager.setStandardScaleGestureListener(new ScaleGestureListener(
+ context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity)
+ ));
+ gesturesManager.setRotateGestureListener(new RotateGestureListener(
+ context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating),
+ context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity)
+ ));
+ gesturesManager.setShoveGestureListener(new ShoveGestureListener());
+ gesturesManager.setMultiFingerTapGestureListener(new TapGestureListener());
}
}
@@ -133,8 +158,9 @@ final class MapGestureDetector {
/**
* Get the current active gesture focal point.
* <p>
- * This could be either the user provided focal point in {@link UiSettings#setFocalPoint(PointF)} or the focal point
- * defined as a result of {@link TrackingSettings#setMyLocationEnabled(boolean)}.
+ * This could be either the user provided focal point in
+ * {@link UiSettings#setFocalPoint(PointF)}or <code>null</code>.
+ * If it's <code>null</code>, gestures will use focal pointed returned by the detector.
* </p>
*
* @return the current active gesture focal point.
@@ -145,128 +171,84 @@ final class MapGestureDetector {
}
/**
- * Given coordinates from a gesture, use the current projection to translate it into
- * a Location object.
- *
- * @param x coordinate
- * @param y coordinate
- * @return location
- */
- private Location getLocationFromGesture(float x, float y) {
- LatLng latLng = projection.fromScreenLocation(new PointF(x, y));
- return TelemetryUtils.buildLocation(latLng.getLongitude(), latLng.getLatitude());
- }
-
- /**
* Called when user touches the screen, all positions are absolute.
* <p>
* Forwards event to the related gesture detectors.
* </p>
*
- * @param event the MotionEvent
+ * @param motionEvent the MotionEvent
* @return True if touch event is handled
*/
- boolean onTouchEvent(MotionEvent event) {
- // framework can return null motion events in edge cases #9432
- if (event == null) {
+ boolean onTouchEvent(MotionEvent motionEvent) {
+ // Framework can return null motion events in edge cases #9432
+ if (motionEvent == null) {
return false;
}
// Check and ignore non touch or left clicks
- if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) {
+ if ((motionEvent.getButtonState() != 0) && (motionEvent.getButtonState() != MotionEvent.BUTTON_PRIMARY)) {
return false;
}
- // Check two finger gestures first
- scaleGestureDetector.onTouchEvent(event);
- rotateGestureDetector.onTouchEvent(event);
- shoveGestureDetector.onTouchEvent(event);
+ boolean result = gesturesManager.onTouchEvent(motionEvent);
- // Handle two finger tap
- switch (event.getActionMasked()) {
+ switch (motionEvent.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;
+ cancelAnimators();
transform.setGestureInProgress(true);
break;
+ case MotionEvent.ACTION_UP:
+ transform.setGestureInProgress(false);
- case MotionEvent.ACTION_POINTER_DOWN:
- // Second pointer down
- twoTap = event.getPointerCount() == 2
- && uiSettings.isZoomGesturesEnabled();
- if (twoTap) {
- // Confirmed 2nd Finger Down
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(event.getX(), event.getY()),
- MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, transform));
+ // Start all awaiting velocity animations
+ animationsTimeoutHandler.removeCallbacksAndMessages(null);
+ for (Animator animator : scheduledAnimators) {
+ animator.start();
}
+ scheduledAnimators.clear();
break;
- case MotionEvent.ACTION_POINTER_UP:
- // Second pointer up
+ case MotionEvent.ACTION_CANCEL:
+ scheduledAnimators.clear();
+ transform.setGestureInProgress(false);
break;
+ }
- case MotionEvent.ACTION_UP:
- // First pointer up
- long tapInterval = event.getEventTime() - event.getDownTime();
- boolean isTap = tapInterval <= ViewConfiguration.getTapTimeout();
- boolean inProgress = rotateGestureDetector.isInProgress()
- || scaleGestureDetector.isInProgress()
- || shoveGestureDetector.isInProgress();
-
- if (twoTap && isTap && !inProgress) {
- if (focalPoint != null) {
- transform.zoom(false, focalPoint);
- } else {
- PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event);
- transform.zoom(false, focalPoint);
- }
- twoTap = false;
- return true;
- }
-
- // Scroll / Pan Has Stopped
- if (scrollGestureOccurred) {
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent(
- getLocationFromGesture(event.getX(), event.getY()), transform));
- scrollGestureOccurred = false;
+ return result;
+ }
- if (!scaleAnimating && !rotateAnimating) {
- cameraChangeDispatcher.onCameraIdle();
- }
- }
+ void cancelAnimators() {
+ if (scaleAnimator != null) {
+ scaleAnimator.cancel();
+ }
+ if (rotateAnimator != null) {
+ rotateAnimator.cancel();
+ }
- twoTap = false;
- transform.setGestureInProgress(false);
- if (velocityTracker != null) {
- velocityTracker.recycle();
- }
- velocityTracker = null;
- break;
+ animationsTimeoutHandler.removeCallbacksAndMessages(null);
+ scheduledAnimators.clear();
+ }
- case MotionEvent.ACTION_CANCEL:
- twoTap = false;
- transform.setGestureInProgress(false);
- if (velocityTracker != null) {
- velocityTracker.recycle();
- }
- velocityTracker = null;
- break;
- case MotionEvent.ACTION_MOVE:
- if (velocityTracker != null) {
- velocityTracker.addMovement(event);
- velocityTracker.computeCurrentVelocity(1000);
- }
- break;
+ /**
+ * Posted on main thread with {@link #animationsTimeoutHandler}. Cancels all scheduled animators if needed.
+ */
+ private Runnable cancelAnimatorsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ cancelAnimators();
}
+ };
- return gestureDetector.onTouchEvent(event);
+ /**
+ * Schedules a velocity animator to be executed when user lift fingers,
+ * unless canceled by the {@link #cancelAnimatorsRunnable}.
+ *
+ * @param animator animator ot be scheduled
+ */
+ private void scheduleAnimator(Animator animator) {
+ scheduledAnimators.add(animator);
+ animationsTimeoutHandler.removeCallbacksAndMessages(null);
+ animationsTimeoutHandler.postDelayed(cancelAnimatorsRunnable, MapboxConstants.SCHEDULED_ANIMATION_TIMEOUT);
}
/**
@@ -275,7 +257,7 @@ final class MapGestureDetector {
* Examples of such events are mouse scroll events, mouse moves, joystick & trackpad.
* </p>
*
- * @param event The MotionEvent occured
+ * @param event The MotionEvent occurred
* @return True is the event is handled
*/
boolean onGenericMotionEvent(MotionEvent event) {
@@ -297,7 +279,7 @@ final class MapGestureDetector {
float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
// Scale the map by the appropriate power of two factor
- transform.zoomBy(scrollDist, event.getX(), event.getY());
+ transform.zoomBy(scrollDist, new PointF(event.getX(), event.getY()));
return true;
@@ -311,57 +293,14 @@ final class MapGestureDetector {
return false;
}
- /**
- * Responsible for handling one finger gestures.
- */
- private class GestureListener extends android.view.GestureDetector.SimpleOnGestureListener {
-
+ private final class StandardGestureListener extends StandardGestureDetector.SimpleStandardOnGestureListener {
@Override
- public boolean onDown(MotionEvent event) {
- return true;
- }
-
- @Override
- public boolean onDoubleTapEvent(MotionEvent e) {
- if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled()) {
- return false;
- }
-
- switch (e.getAction()) {
- case MotionEvent.ACTION_DOWN:
- break;
- case MotionEvent.ACTION_MOVE:
- break;
- case MotionEvent.ACTION_UP:
- if (quickZoom) {
- cameraChangeDispatcher.onCameraIdle();
- quickZoom = false;
- break;
- }
-
- // notify camera change listener
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
-
- // Single finger double tap
- if (focalPoint != null) {
- // User provided focal point
- transform.zoom(true, focalPoint);
- } else {
- // Zoom in on gesture
- transform.zoom(true, new PointF(e.getX(), e.getY()));
- }
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(e.getX(), e.getY()),
- MapboxEvent.GESTURE_DOUBLETAP, transform));
- break;
- }
-
+ public boolean onDown(MotionEvent motionEvent) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
- // Cancel any animation
transform.cancelTransitions();
return true;
}
@@ -380,27 +319,51 @@ final class MapGestureDetector {
notifyOnMapClickListeners(tapPoint);
}
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(motionEvent.getX(), motionEvent.getY()),
- MapboxEvent.GESTURE_SINGLETAP, transform));
+ sendTelemetryEvent(Events.SINGLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
return true;
}
@Override
- public void onLongPress(MotionEvent motionEvent) {
- PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY());
+ public boolean onDoubleTapEvent(MotionEvent motionEvent) {
+ int action = motionEvent.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ executeDoubleTap = true;
+ }
+ if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) {
+ if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled() || !executeDoubleTap) {
+ return false;
+ }
+
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+
+ // Single finger double tap
+ if (focalPoint != null) {
+ // User provided focal point
+ transform.zoomIn(focalPoint);
+ } else {
+ // Zoom in on gesture
+ transform.zoomIn(new PointF(motionEvent.getX(), motionEvent.getY()));
+ }
- if (!quickZoom) {
- notifyOnMapLongClickListeners(longClickPoint);
+ sendTelemetryEvent(Events.DOUBLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
+
+ return true;
}
+ return super.onDoubleTapEvent(motionEvent);
+ }
+
+ @Override
+ public void onLongPress(MotionEvent motionEvent) {
+ PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY());
+ notifyOnMapLongClickListeners(longClickPoint);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- if ((!trackingSettings.isScrollGestureCurrentlyEnabled()) || recentScaleGestureOccurred) {
+ if ((!uiSettings.isScrollGesturesEnabled())) {
// don't allow a fling is scroll is disabled
- // and ignore when a scale gesture has occurred
return false;
}
@@ -413,11 +376,7 @@ final class MapGestureDetector {
return false;
}
- trackingSettings.resetTrackingModesIfRequired(true, false, false);
-
- // cancel any animation
transform.cancelTransitions();
-
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
// tilt results in a bigger translation, limiting input for #5281
@@ -433,222 +392,149 @@ final class MapGestureDetector {
transform.moveBy(offsetX, offsetY, animationTime);
notifyOnFlingListeners();
+
return true;
}
+ }
- // Called for drags
+ private final class MoveGestureListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
@Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- if (!trackingSettings.isScrollGestureCurrentlyEnabled()) {
+ public boolean onMoveBegin(MoveGestureDetector detector) {
+ if (!uiSettings.isScrollGesturesEnabled()) {
return false;
}
- if (tiltGestureOccurred) {
- return false;
- }
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- if (!scrollGestureOccurred) {
- scrollGestureOccurred = true;
+ sendTelemetryEvent(Events.PAN, detector.getFocalPoint());
- // Cancel any animation
- if (!scaleGestureOccurred) {
- transform.cancelTransitions();
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- }
+ notifyOnMoveBeginListeners(detector);
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(e1.getX(), e1.getY()),
- MapboxEvent.GESTURE_PAN_START, transform));
- }
+ return true;
+ }
- // reset tracking if needed
- trackingSettings.resetTrackingModesIfRequired(true, false, false);
+ @Override
+ public boolean onMove(MoveGestureDetector detector, float distanceX, float distanceY) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
// Scroll the map
transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/);
notifyOnScrollListeners();
+ notifyOnMoveListeners(detector);
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));
+ @Override
+ public void onMoveEnd(MoveGestureDetector detector, float velocityX, float velocityY) {
+ cameraChangeDispatcher.onCameraIdle();
+ notifyOnMoveEndListeners(detector);
}
}
- void notifyOnMapLongClickListeners(PointF longClickPoint) {
- // deprecated API
- if (onMapLongClickListener != null) {
- onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint));
- }
+ private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener {
- // new API
- for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) {
- listener.onMapLongClick(projection.fromScreenLocation(longClickPoint));
- }
- }
+ private final float minimumVelocity;
- void notifyOnFlingListeners() {
- // deprecated API
- if (onFlingListener != null) {
- onFlingListener.onFling();
- }
+ private PointF scaleFocalPoint;
+ private boolean quickZoom;
- // new API
- for (MapboxMap.OnFlingListener listener : onFlingListenerList) {
- listener.onFling();
+ ScaleGestureListener(float minimumVelocity) {
+ this.minimumVelocity = minimumVelocity;
}
- }
- void notifyOnScrollListeners() {
- //deprecated API
- if (onScrollListener != null) {
- onScrollListener.onScroll();
- }
+ @Override
+ public boolean onScaleBegin(StandardScaleGestureDetector detector) {
+ if (!uiSettings.isZoomGesturesEnabled()) {
+ return false;
+ }
- // new API
- for (MapboxMap.OnScrollListener listener : onScrollListenerList) {
- listener.onScroll();
- }
- }
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- /**
- * Responsible for handling two finger gestures and double-tap drag gestures.
- */
- private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+ quickZoom = detector.getPointersCount() == 1;
+ if (quickZoom) {
+ // when quickzoom, dismiss double tap and disable move gesture
+ executeDoubleTap = false;
+ gesturesManager.getMoveGestureDetector().setEnabled(false);
+ }
- private static final int ANIMATION_TIME_MULTIPLIER = 77;
- private static final double ZOOM_DISTANCE_DIVIDER = 5;
+ // increase rotate angle threshold when scale is detected first
+ gesturesManager.getRotateGestureDetector().setAngleThreshold(
+ gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold()
+ + MapboxConstants.ROTATION_THRESHOLD_INCREASE_WHEN_SCALING
+ );
- private float scaleFactor = 1.0f;
- private PointF scalePointBegin;
+ // setting focalPoint in #onScaleBegin() as well, because #onScale() might not get called before #onScaleEnd()
+ setScaleFocalPoint(detector);
- // Called when two fingers first touch the screen
- @Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- if (!uiSettings.isZoomGesturesEnabled()) {
- return false;
- }
+ sendTelemetryEvent(Events.PINCH, scaleFocalPoint);
+
+ notifyOnScaleBeginListeners(detector);
- recentScaleGestureOccurred = true;
- scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY());
- scaleBeginTime = detector.getEventTime();
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
- MapboxEvent.GESTURE_PINCH_START, transform));
return true;
}
- // Called each time a finger moves
- // Called for pinch zooms and quickzooms/quickscales
@Override
- public boolean onScale(ScaleGestureDetector detector) {
- if (!uiSettings.isZoomGesturesEnabled()) {
- return super.onScale(detector);
- }
+ public boolean onScale(StandardScaleGestureDetector detector) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
- wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) > 0;
- if (tiltGestureOccurred) {
- return false;
- }
+ setScaleFocalPoint(detector);
- // Ignore short touches in case it is a tap
- // Also ignore small scales
- long time = detector.getEventTime();
- long interval = time - scaleBeginTime;
- if (!scaleGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
- return false;
- }
+ float scaleFactor = detector.getScaleFactor();
+ double zoomBy = getNewZoom(scaleFactor, quickZoom);
+ transform.zoomBy(zoomBy, scaleFocalPoint);
- // If scale is large enough ignore a tap
- scaleFactor *= detector.getScaleFactor();
- if ((scaleFactor > 1.1f) || (scaleFactor < 0.9f)) {
- // notify camera change listener
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- scaleGestureOccurred = true;
- }
+ notifyOnScaleListeners(detector);
- if (!scaleGestureOccurred) {
- return false;
- }
-
- // Gesture is a quickzoom if there aren't two fingers
- if (!quickZoom && !twoTap) {
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- }
- quickZoom = !twoTap;
-
- // 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) {
- // arround user provided focal point
- transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), focalPoint.x, focalPoint.y);
- } else if (quickZoom) {
- cameraChangeDispatcher.onCameraMove();
- // clamp scale factors we feed to core #7514
- 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);
- 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),
- scalePointBegin.x, scalePointBegin.y);
- }
return true;
}
- // Called when fingers leave screen
@Override
- public void onScaleEnd(final ScaleGestureDetector detector) {
- if (velocityTracker == null) {
- return;
- }
+ public void onScaleEnd(StandardScaleGestureDetector detector, float velocityX, float velocityY) {
+ cameraChangeDispatcher.onCameraIdle();
- if (rotateGestureOccurred || quickZoom) {
- reset();
- return;
+ if (quickZoom) {
+ //if quickzoom, re-enabling move gesture detector
+ gesturesManager.getMoveGestureDetector().setEnabled(true);
}
- double velocityXY = Math.abs(velocityTracker.getYVelocity()) + Math.abs(velocityTracker.getXVelocity());
- if (velocityXY > MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING / 2) {
- scaleAnimating = true;
- double zoomAddition = calculateScale(velocityXY);
+ // resetting default angle threshold
+ gesturesManager.getRotateGestureDetector().setAngleThreshold(
+ gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold()
+ );
+
+ float velocityXY = Math.abs(velocityX) + Math.abs(velocityY);
+ if (velocityXY > minimumVelocity) {
+ double zoomAddition = calculateScale(velocityXY, detector.isScalingOut());
double currentZoom = transform.getRawZoom();
- long animationTime = (long) (Math.log(velocityXY) * ANIMATION_TIME_MULTIPLIER);
- createScaleAnimator(currentZoom, zoomAddition, animationTime).start();
- } else if (!scaleAnimating) {
- reset();
+ long animationTime = (long) (Math.abs(zoomAddition) * 1000 / 4);
+ scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, animationTime);
+ scheduleAnimator(scaleAnimator);
}
+
+ notifyOnScaleEndListeners(detector);
}
- private void reset() {
- scaleAnimating = false;
- scaleGestureOccurred = false;
- scaleBeginTime = 0;
- scaleFactor = 1.0f;
- cameraChangeDispatcher.onCameraIdle();
+ private void setScaleFocalPoint(StandardScaleGestureDetector detector) {
+ if (focalPoint != null) {
+ // around user provided focal point
+ scaleFocalPoint = focalPoint;
+ } else if (quickZoom) {
+ // around center
+ scaleFocalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
+ } else {
+ // around gesture
+ scaleFocalPoint = detector.getFocalPoint();
+ }
}
- private double calculateScale(double velocityXY) {
- double zoomAddition = (float) (Math.log(velocityXY) / ZOOM_DISTANCE_DIVIDER);
- if (!wasZoomingIn) {
+ private double calculateScale(double velocityXY, boolean isScalingOut) {
+ double zoomAddition = (float) Math.log(velocityXY / 1000 + 1);
+ if (isScalingOut) {
zoomAddition = -zoomAddition;
}
return zoomAddition;
@@ -657,264 +543,387 @@ final class MapGestureDetector {
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.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- transform.setZoom((Float) animation.getAnimatedValue(), scalePointBegin, 0, true);
+ transform.setZoom((Float) animation.getAnimatedValue(), scaleFocalPoint, 0);
}
});
+
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION);
}
@Override
public void onAnimationCancel(Animator animation) {
- reset();
+ transform.cancelTransitions();
}
@Override
public void onAnimationEnd(Animator animation) {
- reset();
+ cameraChangeDispatcher.onCameraIdle();
}
});
return animator;
}
- }
- /**
- * Responsible for handling rotation gestures.
- */
- private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
+ private double getNewZoom(float scaleFactor, boolean quickZoom) {
+ double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2);
+ if (quickZoom) {
+ // clamp scale factors we feed to core #7514
+ boolean negative = zoomBy < 0;
+ zoomBy = MathUtils.clamp(Math.abs(zoomBy),
+ MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP,
+ MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP);
+ return negative ? -zoomBy : zoomBy;
+ }
+ return zoomBy;
+ }
+ }
- 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 final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
+ private PointF rotateFocalPoint;
+ private final float minimumScaleSpanWhenRotating;
+ private final float minimumAngularVelocity;
- private long beginTime = 0;
- private boolean started = false;
+ RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity) {
+ this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating;
+ this.minimumAngularVelocity = minimumAngularVelocity;
+ }
- // Called when two fingers first touch the screen
@Override
public boolean onRotateBegin(RotateGestureDetector detector) {
- if (!trackingSettings.isRotateGestureCurrentlyEnabled()) {
+ if (!uiSettings.isRotateGesturesEnabled()) {
return false;
}
- // notify camera change listener
+ transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- beginTime = detector.getEventTime();
- return true;
- }
+ // when rotation starts, interrupting scale and increasing the threshold
+ // to make rotation without scaling easier
+ gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(minimumScaleSpanWhenRotating);
+ gesturesManager.getStandardScaleGestureDetector().interrupt();
- // Called each time one of the two fingers moves
- // Called for rotation
- @Override
- public boolean onRotate(RotateGestureDetector detector) {
- if (!trackingSettings.isRotateGestureCurrentlyEnabled() || tiltGestureOccurred) {
- return false;
- }
+ // setting in #onRotateBegin() as well, because #onRotate() might not get called before #onRotateEnd()
+ setRotateFocalPoint(detector);
- // If rotate is large enough ignore a tap
- // Also is zoom already started, don't rotate
- 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));
- started = true;
- }
+ sendTelemetryEvent(Events.ROTATION, rotateFocalPoint);
- if (!started) {
- return false;
- }
+ notifyOnRotateBeginListeners(detector);
- wasClockwiseRotating = detector.getRotationDegreesDelta() > 0;
- if (scaleBeginTime != 0) {
- rotateGestureOccurred = true;
- }
+ return true;
+ }
+
+ @Override
+ public boolean onRotate(RotateGestureDetector detector, float rotationDegreesSinceLast,
+ float rotationDegreesSinceFirst) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
- // rotation constitutes translation of anything except the center of
- // rotation, so cancel both location and bearing tracking if required
- trackingSettings.resetTrackingModesIfRequired(true, true, false);
+ setRotateFocalPoint(detector);
// Calculate map bearing value
- double bearing = transform.getRawBearing() + angle;
+ double bearing = transform.getRawBearing() + rotationDegreesSinceLast;
// Rotate the map
- if (focalPoint != null) {
- // User provided focal point
- transform.setBearing(bearing, focalPoint.x, focalPoint.y);
- } else {
- // around gesture
- transform.setBearing(bearing, detector.getFocusX(), detector.getFocusY());
- }
+ transform.setBearing(bearing, rotateFocalPoint.x, rotateFocalPoint.y);
+
+ notifyOnRotateListeners(detector);
+
return true;
}
- // 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();
+ public void onRotateEnd(RotateGestureDetector detector, float velocityX, float velocityY, float angularVelocity) {
+ cameraChangeDispatcher.onCameraIdle();
+
+ // resetting default scale threshold values
+ gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(
+ gesturesManager.getStandardScaleGestureDetector().getDefaultSpanSinceStartThreshold());
+
+ if (Math.abs(angularVelocity) < minimumAngularVelocity) {
return;
}
- double angularVelocity = calculateVelocityVector(detector);
- if (Math.abs(angularVelocity) > 0.001 && rotateGestureOccurred && !rotateAnimating) {
- animateRotateVelocity();
- } else if (!rotateAnimating) {
- reset();
- }
- }
+ boolean negative = angularVelocity < 0;
+ angularVelocity = (float) Math.pow(angularVelocity, 2);
+ angularVelocity = MathUtils.clamp(
+ angularVelocity, MapboxConstants.MINIMUM_ANGULAR_VELOCITY, MapboxConstants.MAXIMUM_ANGULAR_VELOCITY);
- private void reset() {
- beginTime = 0;
- started = false;
- rotateAnimating = false;
- rotateGestureOccurred = false;
+ long animationTime = (long) (Math.log(angularVelocity + 1) * 500);
- if (!twoTap) {
- cameraChangeDispatcher.onCameraIdle();
+ if (negative) {
+ angularVelocity = -angularVelocity;
}
- }
- private void animateRotateVelocity() {
- rotateAnimating = true;
- double currentRotation = transform.getRawBearing();
- double rotateAdditionDegrees = calculateVelocityInDegrees();
- createAnimator(currentRotation, rotateAdditionDegrees).start();
- }
+ rotateAnimator = createRotateAnimator(angularVelocity, animationTime);
+ scheduleAnimator(rotateAnimator);
- private double calculateVelocityVector(RotateGestureDetector detector) {
- return ((detector.getFocusX() * velocityTracker.getYVelocity())
- + (detector.getFocusY() * velocityTracker.getXVelocity()))
- / (Math.pow(detector.getFocusX(), 2) + Math.pow(detector.getFocusY(), 2));
+ notifyOnRotateEndListeners(detector);
}
- 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;
+ private void setRotateFocalPoint(RotateGestureDetector detector) {
+ if (focalPoint != null) {
+ // User provided focal point
+ rotateFocalPoint = focalPoint;
+ } else {
+ // around gesture
+ rotateFocalPoint = detector.getFocalPoint();
}
-
- 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));
+ private Animator createRotateAnimator(float angularVelocity, long animationTime) {
+ ValueAnimator animator = ValueAnimator.ofFloat(angularVelocity, 0f);
+ animator.setDuration(animationTime);
+ animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- transform.setBearing((Float) animation.getAnimatedValue());
+ transform.setBearing(
+ transform.getRawBearing() + (float) animation.getAnimatedValue(),
+ rotateFocalPoint.x, rotateFocalPoint.y,
+ 0L
+ );
}
});
+
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION);
}
@Override
public void onAnimationCancel(Animator animation) {
- reset();
+ cameraChangeDispatcher.onCameraIdle();
}
@Override
public void onAnimationEnd(Animator animation) {
- reset();
+ cameraChangeDispatcher.onCameraIdle();
}
});
+
return animator;
}
}
- /**
- * Responsible for handling 2 finger shove gestures.
- */
- private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener {
-
- private long beginTime = 0;
- private float totalDelta = 0.0f;
-
+ private final class ShoveGestureListener extends ShoveGestureDetector.SimpleOnShoveGestureListener {
@Override
public boolean onShoveBegin(ShoveGestureDetector detector) {
if (!uiSettings.isTiltGesturesEnabled()) {
return false;
}
- // notify camera change listener
+ transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+
+ sendTelemetryEvent(Events.PITCH, detector.getFocalPoint());
+
+ // disabling move gesture during shove
+ gesturesManager.getMoveGestureDetector().setEnabled(false);
+
+ notifyOnShoveBeginListeners(detector);
+
return true;
}
@Override
- public void onShoveEnd(ShoveGestureDetector detector) {
- beginTime = 0;
- totalDelta = 0.0f;
- tiltGestureOccurred = false;
+ public boolean onShove(ShoveGestureDetector detector, float deltaPixelsSinceLast, float deltaPixelsSinceStart) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
+
+ // Get tilt value (scale and clamp)
+ double pitch = transform.getTilt();
+ pitch -= MapboxConstants.SHOVE_PIXEL_CHANGE_FACTOR * deltaPixelsSinceLast;
+ pitch = MathUtils.clamp(pitch, MapboxConstants.MINIMUM_TILT, MapboxConstants.MAXIMUM_TILT);
+
+ // Tilt the map
+ transform.setTilt(pitch);
+
+ notifyOnShoveListeners(detector);
+
+ return true;
}
@Override
- public boolean onShove(ShoveGestureDetector detector) {
- if (!uiSettings.isTiltGesturesEnabled()) {
- return false;
- }
+ public void onShoveEnd(ShoveGestureDetector detector, float velocityX, float velocityY) {
+ cameraChangeDispatcher.onCameraIdle();
- // Ignore short touches in case it is a tap
- // Also ignore small tilt
- long time = detector.getEventTime();
- long interval = time - beginTime;
- if (!tiltGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
- return false;
- }
+ // re-enabling move gesture
+ gesturesManager.getMoveGestureDetector().setEnabled(true);
- // If tilt is large enough ignore a tap
- // Also if zoom already started, don't tilt
- totalDelta += detector.getShovePixelsDelta();
- if (!tiltGestureOccurred && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) {
- tiltGestureOccurred = true;
- beginTime = detector.getEventTime();
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(detector.getFocusX(), detector.getFocusY()),
- MapboxEvent.GESTURE_PITCH_START, transform));
- }
+ notifyOnShoveEndListeners(detector);
+ }
+ }
- if (!tiltGestureOccurred) {
+ private final class TapGestureListener implements MultiFingerTapGestureDetector.OnMultiFingerTapGestureListener {
+ @Override
+ public boolean onMultiFingerTap(MultiFingerTapGestureDetector detector, int pointersCount) {
+ if (!uiSettings.isZoomGesturesEnabled() || pointersCount != 2) {
return false;
}
- // Get tilt value (scale and clamp)
- double pitch = transform.getTilt();
- pitch -= 0.1 * detector.getShovePixelsDelta();
- pitch = Math.max(MapboxConstants.MINIMUM_TILT, Math.min(MapboxConstants.MAXIMUM_TILT, pitch));
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+
+ if (focalPoint != null) {
+ transform.zoomOut(focalPoint);
+ } else {
+ transform.zoomOut(detector.getFocalPoint());
+ }
- // Tilt the map
- transform.setTilt(pitch);
return true;
}
}
+ private void sendTelemetryEvent(String eventType, PointF focalPoint) {
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(focalPoint);
+ MapState state = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ state.setGesture(eventType);
+ Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, state));
+ }
+ }
+
+ private boolean isZoomValid(Transform transform) {
+ if (transform == null) {
+ return false;
+ }
+ double mapZoom = transform.getZoom();
+ return mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM;
+ }
+
+ 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();
+ }
+ }
+
+ void notifyOnMoveBeginListeners(MoveGestureDetector detector) {
+ for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
+ listener.onMoveBegin(detector);
+ }
+ }
+
+ void notifyOnMoveListeners(MoveGestureDetector detector) {
+ for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
+ listener.onMove(detector);
+ }
+ }
+
+ void notifyOnMoveEndListeners(MoveGestureDetector detector) {
+ for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
+ listener.onMoveEnd(detector);
+ }
+ }
+
+ void notifyOnRotateBeginListeners(RotateGestureDetector detector) {
+ for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
+ listener.onRotateBegin(detector);
+ }
+ }
+
+ void notifyOnRotateListeners(RotateGestureDetector detector) {
+ for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
+ listener.onRotate(detector);
+ }
+ }
+
+ void notifyOnRotateEndListeners(RotateGestureDetector detector) {
+ for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
+ listener.onRotateEnd(detector);
+ }
+ }
+
+ void notifyOnScaleBeginListeners(StandardScaleGestureDetector detector) {
+ for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
+ listener.onScaleBegin(detector);
+ }
+ }
+
+ void notifyOnScaleListeners(StandardScaleGestureDetector detector) {
+ for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
+ listener.onScale(detector);
+ }
+ }
+
+ void notifyOnScaleEndListeners(StandardScaleGestureDetector detector) {
+ for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
+ listener.onScaleEnd(detector);
+ }
+ }
+
+ void notifyOnShoveBeginListeners(ShoveGestureDetector detector) {
+ for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
+ listener.onShoveBegin(detector);
+ }
+ }
+
+ void notifyOnShoveListeners(ShoveGestureDetector detector) {
+ for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
+ listener.onShove(detector);
+ }
+ }
+
+ void notifyOnShoveEndListeners(ShoveGestureDetector detector) {
+ for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
+ listener.onShoveEnd(detector);
+ }
+ }
+
void setOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) {
this.onMapClickListener = onMapClickListener;
}
@@ -962,4 +971,44 @@ final class MapGestureDetector {
void removeOnScrollListener(MapboxMap.OnScrollListener onScrollListener) {
onScrollListenerList.remove(onScrollListener);
}
-}
+
+ void addOnMoveListener(MapboxMap.OnMoveListener listener) {
+ onMoveListenerList.add(listener);
+ }
+
+ void removeOnMoveListener(MapboxMap.OnMoveListener listener) {
+ onMoveListenerList.remove(listener);
+ }
+
+ void addOnRotateListener(MapboxMap.OnRotateListener listener) {
+ onRotateListenerList.add(listener);
+ }
+
+ void removeOnRotateListener(MapboxMap.OnRotateListener listener) {
+ onRotateListenerList.remove(listener);
+ }
+
+ void addOnScaleListener(MapboxMap.OnScaleListener listener) {
+ onScaleListenerList.add(listener);
+ }
+
+ void removeOnScaleListener(MapboxMap.OnScaleListener listener) {
+ onScaleListenerList.remove(listener);
+ }
+
+ void addShoveListener(MapboxMap.OnShoveListener listener) {
+ onShoveListenerList.add(listener);
+ }
+
+ void removeShoveListener(MapboxMap.OnShoveListener listener) {
+ onShoveListenerList.remove(listener);
+ }
+
+ AndroidGesturesManager getGesturesManager() {
+ return gesturesManager;
+ }
+
+ void setGesturesManager(AndroidGesturesManager gesturesManager) {
+ this.gesturesManager = gesturesManager;
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java
index d1f01a30f7..9bd9499fff 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java
@@ -128,7 +128,7 @@ final class MapKeyListener {
// Zoom out
PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- transform.zoom(false, focalPoint);
+ transform.zoomOut(focalPoint);
return true;
default:
@@ -164,7 +164,7 @@ final class MapKeyListener {
// Zoom in
PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- transform.zoom(true, focalPoint);
+ transform.zoomIn(focalPoint);
return true;
}
@@ -219,7 +219,7 @@ final class MapKeyListener {
if (currentTrackballLongPressTimeOut != null) {
// Zoom in
PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- transform.zoom(true, focalPoint);
+ transform.zoomIn(focalPoint);
}
return true;
@@ -261,7 +261,7 @@ final class MapKeyListener {
if (!cancelled) {
// Zoom out
PointF pointF = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- transform.zoom(false, pointF);
+ transform.zoomOut(pointF);
// Ensure the up action is not run
currentTrackballLongPressTimeOut = null;
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 2e7d4c4270..90feb228ab 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
@@ -23,6 +23,12 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ZoomButtonsController;
+import com.mapbox.android.gestures.AndroidGesturesManager;
+import com.mapbox.android.telemetry.AppUserTurnstile;
+import com.mapbox.android.telemetry.Event;
+import com.mapbox.android.telemetry.MapEventFactory;
+import com.mapbox.android.telemetry.MapboxTelemetry;
+import com.mapbox.mapboxsdk.BuildConfig;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
@@ -36,10 +42,10 @@ 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;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -133,17 +139,7 @@ public class MapView extends FrameLayout {
setContentDescription(context.getString(R.string.mapbox_mapActionDescription));
setWillNotDraw(false);
- getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
- } else {
- getViewTreeObserver().removeGlobalOnLayoutListener(this);
- }
- initialiseDrawingSurface(options);
- }
- });
+ getViewTreeObserver().addOnGlobalLayoutListener(new MapViewLayoutListener(this, options));
}
private void initialiseMap() {
@@ -155,7 +151,7 @@ public class MapView extends FrameLayout {
focalPointInvalidator.addListener(createFocalPointChangeListener());
// callback for registering touch listeners
- RegisterTouchListener registerTouchListener = new RegisterTouchListener();
+ GesturesManagerInteractionListener registerTouchListener = new GesturesManagerInteractionListener();
// callback for zooming in the camera
CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator();
@@ -190,7 +186,7 @@ public class MapView extends FrameLayout {
mapCallback.attachMapboxMap(mapboxMap);
// user input
- mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings,
+ mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings,
annotationManager, cameraChangeDispatcher);
mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings);
@@ -285,7 +281,12 @@ public class MapView extends FrameLayout {
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapLoadEvent());
+ MapboxTelemetry telemetry = Events.obtainTelemetry();
+ AppUserTurnstile turnstileEvent = new AppUserTurnstile(BuildConfig.MAPBOX_SDK_IDENTIFIER,
+ BuildConfig.MAPBOX_SDK_VERSION);
+ telemetry.push(turnstileEvent);
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ telemetry.push(mapEventFactory.createMapLoadEvent(Event.Type.MAP_LOAD));
} else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) {
this.savedInstanceState = savedInstanceState;
}
@@ -304,7 +305,7 @@ public class MapView extends FrameLayout {
addView(textureView, 0);
} else {
- GLSurfaceView glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView);
+ GLSurfaceView glSurfaceView = new GLSurfaceView(getContext());
glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop());
mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView, options.getLocalIdeographFontFamily()) {
@Override
@@ -314,7 +315,7 @@ public class MapView extends FrameLayout {
}
};
- glSurfaceView.setVisibility(View.VISIBLE);
+ addView(glSurfaceView, 0);
}
nativeMapView = new NativeMapView(this, mapRenderer);
@@ -389,6 +390,7 @@ public class MapView extends FrameLayout {
public void onStop() {
if (mapboxMap != null) {
// map was destroyed before it was started
+ mapGestureDetector.cancelAnimators();
mapboxMap.onStop();
}
@@ -486,7 +488,9 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onLowMemory() {
- nativeMapView.onLowMemory();
+ if (nativeMapView != null) {
+ nativeMapView.onLowMemory();
+ }
}
/**
@@ -879,6 +883,30 @@ public class MapView extends FrameLayout {
void onMapChanged(@MapChange int change);
}
+ private static class MapViewLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
+
+ private WeakReference<MapView> mapViewWeakReference;
+ private MapboxMapOptions options;
+
+ MapViewLayoutListener(MapView mapView, MapboxMapOptions options) {
+ this.mapViewWeakReference = new WeakReference<>(mapView);
+ this.options = options;
+ }
+
+ @Override
+ public void onGlobalLayout() {
+ MapView mapView = mapViewWeakReference.get();
+ if (mapView != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ } else {
+ mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+ }
+ mapView.initialiseDrawingSurface(options);
+ }
+ }
+ }
+
private class FocalPointInvalidator implements FocalPointChangeListener {
private final List<FocalPointChangeListener> focalPointChangeListeners = new ArrayList<>();
@@ -896,7 +924,7 @@ public class MapView extends FrameLayout {
}
}
- private class RegisterTouchListener implements MapboxMap.OnRegisterTouchListener {
+ private class GesturesManagerInteractionListener implements MapboxMap.OnGesturesManagerInteractionListener {
@Override
public void onSetMapClickListener(MapboxMap.OnMapClickListener listener) {
@@ -957,6 +985,56 @@ public class MapView extends FrameLayout {
public void onRemoveFlingListener(MapboxMap.OnFlingListener listener) {
mapGestureDetector.removeOnFlingListener(listener);
}
+
+ @Override
+ public void onAddMoveListener(MapboxMap.OnMoveListener listener) {
+ mapGestureDetector.addOnMoveListener(listener);
+ }
+
+ @Override
+ public void onRemoveMoveListener(MapboxMap.OnMoveListener listener) {
+ mapGestureDetector.removeOnMoveListener(listener);
+ }
+
+ @Override
+ public void onAddRotateListener(MapboxMap.OnRotateListener listener) {
+ mapGestureDetector.addOnRotateListener(listener);
+ }
+
+ @Override
+ public void onRemoveRotateListener(MapboxMap.OnRotateListener listener) {
+ mapGestureDetector.removeOnRotateListener(listener);
+ }
+
+ @Override
+ public void onAddScaleListener(MapboxMap.OnScaleListener listener) {
+ mapGestureDetector.addOnScaleListener(listener);
+ }
+
+ @Override
+ public void onRemoveScaleListener(MapboxMap.OnScaleListener listener) {
+ mapGestureDetector.removeOnScaleListener(listener);
+ }
+
+ @Override
+ public void onAddShoveListener(MapboxMap.OnShoveListener listener) {
+ mapGestureDetector.addShoveListener(listener);
+ }
+
+ @Override
+ public void onRemoveShoveListener(MapboxMap.OnShoveListener listener) {
+ mapGestureDetector.removeShoveListener(listener);
+ }
+
+ @Override
+ public AndroidGesturesManager getGesturesManager() {
+ return mapGestureDetector.getGesturesManager();
+ }
+
+ @Override
+ public void setGesturesManager(AndroidGesturesManager gesturesManager) {
+ mapGestureDetector.setGesturesManager(gesturesManager);
+ }
}
private static class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener {
@@ -994,11 +1072,13 @@ public class MapView extends FrameLayout {
}
private void onZoom(boolean zoomIn, @Nullable PointF focalPoint) {
- if (focalPoint != null) {
- transform.zoom(zoomIn, focalPoint);
+ if (focalPoint == null) {
+ focalPoint = new PointF(mapWidth / 2, mapHeight / 2);
+ }
+ if (zoomIn) {
+ transform.zoomIn(focalPoint);
} else {
- PointF centerPoint = new PointF(mapWidth / 2, mapHeight / 2);
- transform.zoom(zoomIn, centerPoint);
+ transform.zoomOut(focalPoint);
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java
deleted file mode 100644
index 6730278d79..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.mapbox.mapboxsdk.maps;
-
-import android.location.Location;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
-import com.mapbox.services.android.telemetry.MapboxEvent;
-
-import java.util.Hashtable;
-
-/**
- * Wrapper class for MapboxEvent
- * <p>
- * Provides facility methods to use Transform and handle the case that the zoom, required for a telemetry event,
- * isn't available yet.
- * </p>
- */
-class MapboxEventWrapper {
-
- @Nullable
- static Hashtable<String, Object> buildMapClickEvent(
- @NonNull Location location, @NonNull String gestureId, Transform transform) {
- try {
- double mapZoom = transform.getZoom();
- if (mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM) {
- // validate zoom #8057
- return MapboxEvent.buildMapClickEvent(location, gestureId, transform.getZoom());
- }
- } catch (NullPointerException exception) {
- // Map/Transform is not ready yet #8650
- // returning null is valid, event is ignored.
- }
- return null;
- }
-
- @Nullable
- static Hashtable<String, Object> buildMapDragEndEvent(
- @NonNull Location location, Transform transform) {
- try {
- double mapZoom = transform.getZoom();
- if (mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM) {
- // validate zoom #8057
- return MapboxEvent.buildMapDragEndEvent(location, transform.getZoom());
- }
- } catch (NullPointerException exception) {
- // Map/Transform is not ready yet #8650
- // returning null is valid, event is ignored.
- }
- return null;
- }
-
- @Nullable
- static Hashtable<String, Object> buildMapLoadEvent() {
- return MapboxEvent.buildMapLoadEvent();
- }
-}
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 509e784e58..cbd3842a02 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
@@ -16,6 +16,14 @@ import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
+import com.mapbox.android.core.location.LocationEngine;
+import com.mapbox.android.gestures.AndroidGesturesManager;
+import com.mapbox.android.gestures.MoveGestureDetector;
+import com.mapbox.android.gestures.RotateGestureDetector;
+import com.mapbox.android.gestures.ShoveGestureDetector;
+import com.mapbox.android.gestures.StandardScaleGestureDetector;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.Geometry;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
@@ -41,9 +49,6 @@ import com.mapbox.mapboxsdk.style.layers.Filter;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.sources.Source;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.Geometry;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
@@ -73,13 +78,13 @@ public final class MapboxMap {
private final MyLocationViewSettings myLocationViewSettings;
private final CameraChangeDispatcher cameraChangeDispatcher;
- private final OnRegisterTouchListener onRegisterTouchListener;
+ private final OnGesturesManagerInteractionListener onGesturesManagerInteractionListener;
private MapboxMap.OnFpsChangedListener onFpsChangedListener;
private PointF focalPoint;
MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking,
- MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener,
+ MyLocationViewSettings myLocationView, Projection projection, OnGesturesManagerInteractionListener listener,
AnnotationManager annotations, CameraChangeDispatcher cameraChangeDispatcher) {
this.nativeMapView = map;
this.uiSettings = ui;
@@ -88,7 +93,7 @@ public final class MapboxMap {
this.myLocationViewSettings = myLocationView;
this.annotationManager = annotations.bind(this);
this.transform = transform;
- this.onRegisterTouchListener = listener;
+ this.onGesturesManagerInteractionListener = listener;
this.cameraChangeDispatcher = cameraChangeDispatcher;
}
@@ -1882,12 +1887,11 @@ 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.onSetScrollListener(listener);
+ onGesturesManagerInteractionListener.onSetScrollListener(listener);
}
/**
@@ -1895,10 +1899,9 @@ public final class MapboxMap {
*
* @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);
+ onGesturesManagerInteractionListener.onAddScrollListener(listener);
}
/**
@@ -1906,10 +1909,9 @@ public final class MapboxMap {
*
* @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);
+ onGesturesManagerInteractionListener.onRemoveScrollListener(listener);
}
/**
@@ -1917,12 +1919,11 @@ 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.onSetFlingListener(listener);
+ onGesturesManagerInteractionListener.onSetFlingListener(listener);
}
/**
@@ -1932,7 +1933,7 @@ public final class MapboxMap {
* To unset the callback, use null.
*/
public void addOnFlingListener(@Nullable OnFlingListener listener) {
- onRegisterTouchListener.onAddFlingListener(listener);
+ onGesturesManagerInteractionListener.onAddFlingListener(listener);
}
/**
@@ -1942,7 +1943,98 @@ public final class MapboxMap {
* To unset the callback, use null.
*/
public void removeOnFlingListener(@Nullable OnFlingListener listener) {
- onRegisterTouchListener.onRemoveFlingListener(listener);
+ onGesturesManagerInteractionListener.onRemoveFlingListener(listener);
+ }
+
+ /**
+ * Adds a callback that's invoked when the map is moved.
+ *
+ * @param listener The callback that's invoked when the map is moved.
+ */
+ public void addOnMoveListener(OnMoveListener listener) {
+ onGesturesManagerInteractionListener.onAddMoveListener(listener);
+ }
+
+ /**
+ * Removes a callback that's invoked when the map is moved.
+ *
+ * @param listener The callback that's invoked when the map is moved.
+ */
+ public void removeOnMoveListener(OnMoveListener listener) {
+ onGesturesManagerInteractionListener.onRemoveMoveListener(listener);
+ }
+
+ /**
+ * Adds a callback that's invoked when the map is rotated.
+ *
+ * @param listener The callback that's invoked when the map is rotated.
+ */
+ public void addOnRotateListener(OnRotateListener listener) {
+ onGesturesManagerInteractionListener.onAddRotateListener(listener);
+ }
+
+ /**
+ * Removes a callback that's invoked when the map is rotated.
+ *
+ * @param listener The callback that's invoked when the map is rotated.
+ */
+ public void removeOnRotateListener(OnRotateListener listener) {
+ onGesturesManagerInteractionListener.onRemoveRotateListener(listener);
+ }
+
+ /**
+ * Adds a callback that's invoked when the map is scaled.
+ *
+ * @param listener The callback that's invoked when the map is scaled.
+ */
+ public void addOnScaleListener(OnScaleListener listener) {
+ onGesturesManagerInteractionListener.onAddScaleListener(listener);
+ }
+
+ /**
+ * Removes a callback that's invoked when the map is scaled.
+ *
+ * @param listener The callback that's invoked when the map is scaled.
+ */
+ public void removeOnScaleListener(OnScaleListener listener) {
+ onGesturesManagerInteractionListener.onRemoveScaleListener(listener);
+ }
+
+ /**
+ * Adds a callback that's invoked when the map is tilted.
+ *
+ * @param listener The callback that's invoked when the map is tilted.
+ */
+ public void addOnShoveListener(OnShoveListener listener) {
+ onGesturesManagerInteractionListener.onAddShoveListener(listener);
+ }
+
+ /**
+ * Remove a callback that's invoked when the map is tilted.
+ *
+ * @param listener The callback that's invoked when the map is tilted.
+ */
+ public void removeOnShoveListener(OnShoveListener listener) {
+ onGesturesManagerInteractionListener.onRemoveShoveListener(listener);
+ }
+
+ /**
+ * Sets a custom {@link AndroidGesturesManager} to handle {@link android.view.MotionEvent}s registered by the map.
+ *
+ * @param androidGesturesManager Gestures manager that interprets gestures based on the motion events.
+ * @see <a href="https://github.com/mapbox/mapbox-gestures-android">mapbox-gestures-android library</a>
+ */
+ public void setGesturesManager(AndroidGesturesManager androidGesturesManager) {
+ onGesturesManagerInteractionListener.setGesturesManager(androidGesturesManager);
+ }
+
+ /**
+ * Get current {@link AndroidGesturesManager} that handles {@link android.view.MotionEvent}s registered by the map.
+ *
+ * @return Current gestures manager.
+ */
+ public AndroidGesturesManager getGesturesManager() {
+ return onGesturesManagerInteractionListener.getGesturesManager();
}
/**
@@ -1950,12 +2042,11 @@ 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.onSetMapClickListener(listener);
+ onGesturesManagerInteractionListener.onSetMapClickListener(listener);
}
/**
@@ -1965,7 +2056,7 @@ public final class MapboxMap {
* To unset the callback, use null.
*/
public void addOnMapClickListener(@Nullable OnMapClickListener listener) {
- onRegisterTouchListener.onAddMapClickListener(listener);
+ onGesturesManagerInteractionListener.onAddMapClickListener(listener);
}
/**
@@ -1975,7 +2066,7 @@ public final class MapboxMap {
* To unset the callback, use null.
*/
public void removeOnMapClickListener(@Nullable OnMapClickListener listener) {
- onRegisterTouchListener.onRemoveMapClickListener(listener);
+ onGesturesManagerInteractionListener.onRemoveMapClickListener(listener);
}
/**
@@ -1983,12 +2074,11 @@ 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.onSetMapLongClickListener(listener);
+ onGesturesManagerInteractionListener.onSetMapLongClickListener(listener);
}
/**
@@ -1998,7 +2088,7 @@ public final class MapboxMap {
* To unset the callback, use null.
*/
public void addOnMapLongClickListener(@Nullable OnMapLongClickListener listener) {
- onRegisterTouchListener.onAddMapLongClickListener(listener);
+ onGesturesManagerInteractionListener.onAddMapLongClickListener(listener);
}
/**
@@ -2008,7 +2098,7 @@ public final class MapboxMap {
* To unset the callback, use null.
*/
public void removeOnMapLongClickListener(@Nullable OnMapLongClickListener listener) {
- onRegisterTouchListener.onRemoveMapLongClickListener(listener);
+ onGesturesManagerInteractionListener.onRemoveMapLongClickListener(listener);
}
/**
@@ -2267,7 +2357,9 @@ public final class MapboxMap {
* Interface definition for a callback to be invoked when the map is scrolled.
*
* @see MapboxMap#setOnScrollListener(OnScrollListener)
+ * @deprecated Use {@link OnMoveListener} instead.
*/
+ @Deprecated
public interface OnScrollListener {
/**
* Called when the map is scrolled.
@@ -2276,6 +2368,58 @@ public final class MapboxMap {
}
/**
+ * Interface definition for a callback to be invoked when the map is moved.
+ *
+ * @see MapboxMap#addOnMoveListener(OnMoveListener)
+ */
+ public interface OnMoveListener {
+ void onMoveBegin(MoveGestureDetector detector);
+
+ void onMove(MoveGestureDetector detector);
+
+ void onMoveEnd(MoveGestureDetector detector);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the map is rotated.
+ *
+ * @see MapboxMap#addOnRotateListener(OnRotateListener)
+ */
+ public interface OnRotateListener {
+ void onRotateBegin(RotateGestureDetector detector);
+
+ void onRotate(RotateGestureDetector detector);
+
+ void onRotateEnd(RotateGestureDetector detector);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the map is scaled.
+ *
+ * @see MapboxMap#addOnScaleListener(OnScaleListener)
+ */
+ public interface OnScaleListener {
+ void onScaleBegin(StandardScaleGestureDetector detector);
+
+ void onScale(StandardScaleGestureDetector detector);
+
+ void onScaleEnd(StandardScaleGestureDetector detector);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the map is tilted.
+ *
+ * @see MapboxMap#addOnShoveListener(OnShoveListener)
+ */
+ public interface OnShoveListener {
+ void onShoveBegin(ShoveGestureDetector detector);
+
+ void onShove(ShoveGestureDetector detector);
+
+ void onShoveEnd(ShoveGestureDetector detector);
+ }
+
+ /**
* Interface definition for a callback to be invoked when the camera changes position.
*
* @deprecated Replaced by {@link MapboxMap.OnCameraMoveStartedListener}, {@link MapboxMap.OnCameraMoveListener} and
@@ -2377,7 +2521,7 @@ public final class MapboxMap {
* Interface definition for a callback to be invoked when a user registers an listener that is
* related to touch and click events.
*/
- interface OnRegisterTouchListener {
+ interface OnGesturesManagerInteractionListener {
void onSetMapClickListener(OnMapClickListener listener);
void onAddMapClickListener(OnMapClickListener listener);
@@ -2401,6 +2545,26 @@ public final class MapboxMap {
void onAddFlingListener(OnFlingListener listener);
void onRemoveFlingListener(OnFlingListener listener);
+
+ void onAddMoveListener(OnMoveListener listener);
+
+ void onRemoveMoveListener(OnMoveListener listener);
+
+ void onAddRotateListener(OnRotateListener listener);
+
+ void onRemoveRotateListener(OnRotateListener listener);
+
+ void onAddScaleListener(OnScaleListener listener);
+
+ void onRemoveScaleListener(OnScaleListener listener);
+
+ void onAddShoveListener(OnShoveListener listener);
+
+ void onRemoveShoveListener(OnShoveListener listener);
+
+ AndroidGesturesManager getGesturesManager();
+
+ void setGesturesManager(AndroidGesturesManager gesturesManager);
}
/**
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 f1635c898f..785b045779 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
@@ -11,6 +11,8 @@ import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.Geometry;
import com.mapbox.mapboxsdk.LibraryLoader;
import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.Marker;
@@ -29,8 +31,6 @@ import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.sources.CannotAddSourceException;
import com.mapbox.mapboxsdk.style.sources.Source;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.Geometry;
import java.nio.ByteBuffer;
import java.util.ArrayList;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
index 16c73b1ca5..ae559189ad 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
@@ -104,11 +104,12 @@ public class Projection {
LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom));
return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight,
- LatLngBounds.from(
- topRight.getLatitude(),
- topRight.getLongitude(),
- bottomLeft.getLatitude(),
- bottomLeft.getLongitude())
+ new LatLngBounds.Builder()
+ .include(topRight)
+ .include(bottomLeft)
+ .include(bottomRight)
+ .include(topLeft)
+ .build()
);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
index 6eacbbaeaf..3743096824 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
@@ -12,9 +12,9 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
-import com.mapbox.services.android.telemetry.permissions.PermissionsManager;
+import com.mapbox.android.core.location.LocationEngine;
+import com.mapbox.android.core.location.LocationEngineListener;
+import com.mapbox.android.core.permissions.PermissionsManager;
import timber.log.Timber;
@@ -374,7 +374,7 @@ public final class TrackingSettings {
}
private void setMyLocationEnabled(boolean locationEnabled, boolean isCustomLocationSource) {
- if (!PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) {
+ if (locationEnabled && !PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) {
Timber.e("Could not activate user location tracking: "
+ "user did not accept the permission or permissions were not requested.");
return;
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 84a601039f..43c943a16f 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
@@ -205,6 +205,8 @@ final class Transform implements MapView.OnMapChangedListener {
// cancel ongoing transitions
mapView.cancelTransitions();
+
+ cameraChangeDispatcher.onCameraIdle();
}
@UiThread
@@ -235,39 +237,37 @@ final class Transform implements MapView.OnMapChangedListener {
return mapView.getZoom();
}
- void zoom(boolean zoomIn, @NonNull PointF focalPoint) {
+ void zoomIn(@NonNull PointF focalPoint) {
CameraPosition cameraPosition = invalidateCameraPosition();
if (cameraPosition != null) {
- int newZoom = (int) Math.round(cameraPosition.zoom + (zoomIn ? 1 : -1));
- setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION, false);
- } else {
- // we are not transforming, notify about being idle
- cameraChangeDispatcher.onCameraIdle();
+ int newZoom = (int) Math.round(cameraPosition.zoom + 1);
+ setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION);
}
}
- void zoom(double zoomAddition, @NonNull PointF focalPoint, long duration) {
+ void zoomOut(@NonNull PointF focalPoint) {
CameraPosition cameraPosition = invalidateCameraPosition();
if (cameraPosition != null) {
- int newZoom = (int) Math.round(cameraPosition.zoom + zoomAddition);
- setZoom(newZoom, focalPoint, duration, false);
- } else {
- // we are not transforming, notify about being idle
- cameraChangeDispatcher.onCameraIdle();
+ int newZoom = (int) Math.round(cameraPosition.zoom - 1);
+ setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION);
}
}
+ void zoomBy(double zoomAddition, @NonNull PointF focalPoint) {
+ setZoom(mapView.getZoom() + zoomAddition, focalPoint, 0);
+ }
+
void setZoom(double zoom, @NonNull PointF focalPoint) {
- setZoom(zoom, focalPoint, 0, false);
+ setZoom(zoom, focalPoint, 0);
}
- void setZoom(double zoom, @NonNull PointF focalPoint, long duration, final boolean isAnimator) {
+ void setZoom(double zoom, @NonNull PointF focalPoint, long duration) {
if (mapView != null) {
mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
@Override
public void onMapChanged(int change) {
if (change == MapView.REGION_DID_CHANGE_ANIMATED) {
- if (!isAnimator) {
+ if (duration > 0) {
cameraChangeDispatcher.onCameraIdle();
}
mapView.removeOnMapChangedListener(this);
@@ -361,10 +361,6 @@ final class Transform implements MapView.OnMapChangedListener {
}
}
- void zoomBy(double z, float x, float y) {
- mapView.setZoom(mapView.getZoom() + z, new PointF(x, y), 0);
- }
-
void moveBy(double offsetX, double offsetY, long duration) {
if (duration > 0) {
mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java
index c34833e9ce..1e76ffe3fb 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java
@@ -4,7 +4,6 @@ import android.graphics.SurfaceTexture;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.view.TextureView;
-
import com.mapbox.mapboxsdk.maps.renderer.egl.EGLConfigChooser;
import java.lang.ref.WeakReference;
@@ -219,13 +218,6 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu
break;
}
- // Check if the size has changed
- if (sizeChanged) {
- recreateSurface = true;
- sizeChanged = false;
- break;
- }
-
// Reset the request render flag now, so we can catch new requests
// while rendering
requestRender = false;
@@ -273,6 +265,12 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu
continue;
}
+ if (sizeChanged) {
+ mapRenderer.onSurfaceChanged(gl, w, h);
+ sizeChanged = false;
+ continue;
+ }
+
// Don't continue without a surface
if (eglHolder.eglSurface == EGL10.EGL_NO_SURFACE) {
continue;
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 1e604c9bef..45f72af1c5 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 ImageView implements Runnable {
+public final class CompassView extends AppCompatImageView implements Runnable {
public static final long TIME_WAIT_IDLE = 500;
public static final long TIME_MAP_NORTH_ANIMATION = 150;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
index 4f27e0ada8..3f37da99d5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
@@ -35,9 +35,9 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.Projection;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
-import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
+import com.mapbox.android.core.location.LocationEngine;
+import com.mapbox.android.core.location.LocationEngineListener;
+import com.mapbox.android.core.location.LocationEnginePriority;
import java.lang.ref.WeakReference;
@@ -356,7 +356,9 @@ public class MyLocationView extends View {
foregroundDrawable.draw(canvas);
}
} else if (foregroundBearingDrawable != null && foregroundBounds != null) {
- if (myBearingTrackingMode == MyBearingTracking.GPS || compassListener.isSensorAvailable()) {
+ if (myBearingTrackingMode == MyBearingTracking.GPS
+ || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING
+ || compassListener.isSensorAvailable()) {
foregroundBearingDrawable.draw(canvas);
} else {
// We are tracking MyBearingTracking.COMPASS, but sensor is not available.
@@ -383,7 +385,8 @@ public class MyLocationView extends View {
public void setBearing(double bearing) {
this.bearing = bearing;
if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- if (myBearingTrackingMode == MyBearingTracking.GPS) {
+ if (myBearingTrackingMode == MyBearingTracking.GPS
+ || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) {
if (location != null) {
setCompass(location.getBearing() - bearing);
}
@@ -519,7 +522,8 @@ public class MyLocationView extends View {
}
private void toggleGps(boolean enableGps) {
- toggleGps(enableGps, mapboxMap != null && mapboxMap.getTrackingSettings().isCustomLocationSource());
+ toggleGps(enableGps, mapboxMap != null
+ && mapboxMap.getTrackingSettings().isCustomLocationSource());
}
/**
@@ -580,7 +584,8 @@ public class MyLocationView extends View {
this.location = location;
myLocationBehavior.updateLatLng(location);
- if (mapboxMap != null && myBearingTrackingMode == MyBearingTracking.GPS
+ if (mapboxMap != null && (myBearingTrackingMode == MyBearingTracking.GPS
+ || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING)
&& myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
setBearing(mapboxMap.getCameraPosition().bearing);
}
@@ -616,7 +621,8 @@ public class MyLocationView extends View {
compassListener.onResume();
} else {
compassListener.onPause();
- if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
+ if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW
+ && myBearingTrackingMode == MyBearingTracking.GPS) {
// always face north
setCompass(0);
} else {
@@ -1017,6 +1023,13 @@ public class MyLocationView extends View {
setCompass(0, COMPASS_UPDATE_RATE_MS);
}
+ if (myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) {
+ builder.bearing(0);
+ if (location.hasBearing()) {
+ setCompass(location.getBearing(), COMPASS_UPDATE_RATE_MS);
+ }
+ }
+
// accuracy
updateAccuracy(location);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
index f0cb8d973a..929e4b4279 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
@@ -6,6 +6,8 @@ import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Environment;
import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import timber.log.Timber;
@@ -43,6 +45,7 @@ public class FileSource {
* @param context the context to derive the cache path from
* @return the single instance of FileSource
*/
+ @UiThread
public static synchronized FileSource getInstance(Context context) {
if (INSTANCE == null) {
String cachePath = getCachePath(context);
@@ -122,6 +125,8 @@ public class FileSource {
initialize(Mapbox.getAccessToken(), cachePath, assetManager);
}
+ public native boolean isActivated();
+
public native void activate();
public native void deactivate();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java
new file mode 100644
index 0000000000..6b8fd65def
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java
@@ -0,0 +1,221 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`.
+
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+
+/**
+ * A heatmap.
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers-heatmap">The online documentation</a>
+ */
+@UiThread
+public class HeatmapLayer extends Layer {
+
+ /**
+ * Creates a HeatmapLayer.
+ *
+ * @param nativePtr pointer used by core
+ */
+ public HeatmapLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ /**
+ * Creates a HeatmapLayer.
+ *
+ * @param layerId the id of the layer
+ * @param sourceId the id of the source
+ */
+ public HeatmapLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ /**
+ * Set the source layer.
+ *
+ * @param sourceLayer the source layer to set
+ */
+ public void setSourceLayer(String sourceLayer) {
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ /**
+ * Set the source Layer.
+ *
+ * @param sourceLayer the source layer to set
+ * @return This
+ */
+ public HeatmapLayer withSourceLayer(String sourceLayer) {
+ setSourceLayer(sourceLayer);
+ return this;
+ }
+
+ /**
+ * Get the source layer.
+ *
+ * @return sourceLayer the source layer to get
+ */
+ public String getSourceLayer() {
+ return nativeGetSourceLayer();
+ }
+
+ /**
+ * Set a single filter.
+ *
+ * @param filter the filter to set
+ */
+ public void setFilter(Filter.Statement filter) {
+ nativeSetFilter(filter.toArray());
+ }
+
+ /**
+ * Set a single filter.
+ *
+ * @param filter the filter to set
+ * @return This
+ */
+ public HeatmapLayer withFilter(Filter.Statement filter) {
+ setFilter(filter);
+ return this;
+ }
+
+ /**
+ * Set a property or properties.
+ *
+ * @param properties the var-args properties
+ * @return This
+ */
+ public HeatmapLayer withProperties(@NonNull PropertyValue<?>... properties) {
+ setProperties(properties);
+ return this;
+ }
+
+ // Property getters
+
+ /**
+ * Get the HeatmapRadius property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getHeatmapRadius() {
+ return (PropertyValue<Float>) new PropertyValue("heatmap-radius", nativeGetHeatmapRadius());
+ }
+
+ /**
+ * Get the HeatmapRadius property transition options
+ *
+ * @return transition options for Float
+ */
+ public TransitionOptions getHeatmapRadiusTransition() {
+ return nativeGetHeatmapRadiusTransition();
+ }
+
+ /**
+ * Set the HeatmapRadius property transition options
+ *
+ * @param options transition options for Float
+ */
+ public void setHeatmapRadiusTransition(TransitionOptions options) {
+ nativeSetHeatmapRadiusTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the HeatmapWeight property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getHeatmapWeight() {
+ return (PropertyValue<Float>) new PropertyValue("heatmap-weight", nativeGetHeatmapWeight());
+ }
+
+ /**
+ * Get the HeatmapIntensity property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getHeatmapIntensity() {
+ return (PropertyValue<Float>) new PropertyValue("heatmap-intensity", nativeGetHeatmapIntensity());
+ }
+
+ /**
+ * Get the HeatmapIntensity property transition options
+ *
+ * @return transition options for Float
+ */
+ public TransitionOptions getHeatmapIntensityTransition() {
+ return nativeGetHeatmapIntensityTransition();
+ }
+
+ /**
+ * Set the HeatmapIntensity property transition options
+ *
+ * @param options transition options for Float
+ */
+ public void setHeatmapIntensityTransition(TransitionOptions options) {
+ nativeSetHeatmapIntensityTransition(options.getDuration(), options.getDelay());
+ }
+
+ /**
+ * Get the HeatmapOpacity property
+ *
+ * @return property wrapper value around Float
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<Float> getHeatmapOpacity() {
+ return (PropertyValue<Float>) new PropertyValue("heatmap-opacity", nativeGetHeatmapOpacity());
+ }
+
+ /**
+ * Get the HeatmapOpacity property transition options
+ *
+ * @return transition options for Float
+ */
+ public TransitionOptions getHeatmapOpacityTransition() {
+ return nativeGetHeatmapOpacityTransition();
+ }
+
+ /**
+ * Set the HeatmapOpacity property transition options
+ *
+ * @param options transition options for Float
+ */
+ public void setHeatmapOpacityTransition(TransitionOptions options) {
+ nativeSetHeatmapOpacityTransition(options.getDuration(), options.getDelay());
+ }
+
+ private native Object nativeGetHeatmapRadius();
+
+ private native TransitionOptions nativeGetHeatmapRadiusTransition();
+
+ private native void nativeSetHeatmapRadiusTransition(long duration, long delay);
+
+ private native Object nativeGetHeatmapWeight();
+
+ private native Object nativeGetHeatmapIntensity();
+
+ private native TransitionOptions nativeGetHeatmapIntensityTransition();
+
+ private native void nativeSetHeatmapIntensityTransition(long duration, long delay);
+
+ private native Object nativeGetHeatmapOpacity();
+
+ private native TransitionOptions nativeGetHeatmapOpacityTransition();
+
+ private native void nativeSetHeatmapOpacityTransition(long duration, long delay);
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java
index dc65cb5081..7e3d3a7779 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java
@@ -11,7 +11,7 @@ import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
- * Client-side hillshading visualization based on DEM data. Currently, the implementation only supports Mapbox Terrain RGB tiles
+ * Client-side hillshading visualization based on DEM data. Currently, the implementation only supports Mapbox Terrain RGB and Mapzen Terrarium tiles.
*
* @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers-hillshade">The online documentation</a>
*/
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
index 6e644c5591..18ee05e63b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
@@ -1516,6 +1516,138 @@ public class PropertyFactory {
}
/**
+ * Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> heatmapRadius(Float value) {
+ return new PaintPropertyValue<>("heatmap-radius", value);
+ }
+
+ /**
+ * Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> heatmapRadius(Expression expression) {
+ return new PaintPropertyValue<>("heatmap-radius", expression);
+ }
+
+
+ /**
+ * Radius of influence of one heatmap point in density-independent pixels. Increasing the value makes the heatmap smoother, but less detailed.
+ *
+ * @param <T> the function input type
+ * @param function a wrapper function for Float
+ * @return property wrapper around a Float function
+ */
+ @Deprecated
+ public static <T> PropertyValue<Function<T, Float>> heatmapRadius(Function<T, Float> function) {
+ return new PaintPropertyValue<>("heatmap-radius", function);
+ }
+
+ /**
+ * A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> heatmapWeight(Float value) {
+ return new PaintPropertyValue<>("heatmap-weight", value);
+ }
+
+ /**
+ * A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> heatmapWeight(Expression expression) {
+ return new PaintPropertyValue<>("heatmap-weight", expression);
+ }
+
+
+ /**
+ * A measure of how much an individual point contributes to the heatmap. A value of 10 would be equivalent to having 10 points of weight 1 in the same spot. Especially useful when combined with clustering.
+ *
+ * @param <T> the function input type
+ * @param function a wrapper function for Float
+ * @return property wrapper around a Float function
+ */
+ @Deprecated
+ public static <T> PropertyValue<Function<T, Float>> heatmapWeight(Function<T, Float> function) {
+ return new PaintPropertyValue<>("heatmap-weight", function);
+ }
+
+ /**
+ * Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> heatmapIntensity(Float value) {
+ return new PaintPropertyValue<>("heatmap-intensity", value);
+ }
+
+ /**
+ * Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> heatmapIntensity(Expression expression) {
+ return new PaintPropertyValue<>("heatmap-intensity", expression);
+ }
+
+
+ /**
+ * Similar to {@link PropertyFactory#heatmapWeight} but controls the intensity of the heatmap globally. Primarily used for adjusting the heatmap based on zoom level.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for Float
+ * @return property wrapper around a Float function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> heatmapIntensity(CameraFunction<Z, Float> function) {
+ return new PaintPropertyValue<>("heatmap-intensity", function);
+ }
+
+ /**
+ * The global opacity at which the heatmap layer will be drawn.
+ *
+ * @param value a Float value
+ * @return property wrapper around Float
+ */
+ public static PropertyValue<Float> heatmapOpacity(Float value) {
+ return new PaintPropertyValue<>("heatmap-opacity", value);
+ }
+
+ /**
+ * The global opacity at which the heatmap layer will be drawn.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> heatmapOpacity(Expression expression) {
+ return new PaintPropertyValue<>("heatmap-opacity", expression);
+ }
+
+
+ /**
+ * The global opacity at which the heatmap layer will be drawn.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for Float
+ * @return property wrapper around a Float function
+ */
+ @Deprecated
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> heatmapOpacity(CameraFunction<Z, Float> function) {
+ return new PaintPropertyValue<>("heatmap-opacity", function);
+ }
+
+ /**
* The opacity of the entire fill extrusion layer. This is rendered on a per-layer, not per-feature, basis, and data-driven styling is not available.
*
* @param value a Float value
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java
index 62f1719ddf..1b0999ae2e 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java
@@ -5,10 +5,10 @@ import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.annotation.WorkerThread;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.style.layers.Filter;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -37,18 +37,18 @@ public class CustomGeometrySource extends Source {
* @param provider The tile provider that returns geometry data for this source.
*/
public CustomGeometrySource(String id, GeometryTileProvider provider) {
- this(id, provider, new GeoJsonOptions());
+ this(id, provider, new CustomGeometrySourceOptions());
}
/**
- * Create a CustomGeometrySource with non-default GeoJsonOptions.
+ * Create a CustomGeometrySource with non-default CustomGeometrySourceOptions.
* <p>Supported options are minZoom, maxZoom, buffer, and tolerance.</p>
*
* @param id The source id.
* @param provider The tile provider that returns geometry data for this source.
- * @param options GeoJsonOptions.
+ * @param options CustomGeometrySourceOptions.
*/
- public CustomGeometrySource(String id, GeometryTileProvider provider, GeoJsonOptions options) {
+ public CustomGeometrySource(String id, GeometryTileProvider provider, CustomGeometrySourceOptions options) {
this.provider = provider;
executor = Executors.newFixedThreadPool(4);
initialize(id, options);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java
new file mode 100644
index 0000000000..4ada38c238
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java
@@ -0,0 +1,31 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+/**
+ * Builder class for composing CustomGeometrySource objects.
+ */
+public class CustomGeometrySourceOptions extends GeoJsonOptions {
+
+ /**
+ * If the data includes wrapped coordinates, setting this to true unwraps the coordinates.
+ *
+ * @param wrap defaults to false
+ * @return the current instance for chaining
+ */
+ public CustomGeometrySourceOptions withWrap(boolean wrap) {
+ this.put("wrap", wrap);
+ return this;
+ }
+
+ /**
+ * If the data includes geometry outside the tile boundaries, setting this to true clips the geometry
+ * to the tile boundaries.
+ *
+ * @param clip defaults to false
+ * @return the current instance for chaining
+ */
+ public CustomGeometrySourceOptions withClip(boolean clip) {
+ this.put("clip", clip);
+ return this;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java
index 81f7255b86..79cde7429c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java
@@ -13,7 +13,7 @@ public class GeoJsonOptions extends HashMap<String, Object> {
/**
* Maximum zoom level at which to create vector tiles (higher means greater detail at high zoom levels).
*
- * @param maxZoom the maximum zoom - Defaults to 18.
+ * @param minZoom the maximum zoom - Defaults to 18.
* @return the current instance for chaining
*/
public GeoJsonOptions withMinZoom(int minZoom) {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
index 10ecb945ad..5c740554cd 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
@@ -4,10 +4,10 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
+import com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Geometry;
import com.mapbox.mapboxsdk.style.layers.Filter;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
-import com.mapbox.services.commons.geojson.Geometry;
import java.net.URL;
import java.util.ArrayList;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java
index 3f1eb315d3..17e7f0f5e4 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java
@@ -2,8 +2,8 @@ package com.mapbox.mapboxsdk.style.sources;
import android.support.annotation.WorkerThread;
+import com.mapbox.geojson.FeatureCollection;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.services.commons.geojson.FeatureCollection;
/**
* Interface that defines methods for working with {@link CustomGeometrySource}.
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java
index 84e5e96fa4..b7679b5a16 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java
@@ -124,6 +124,15 @@ public class ImageSource extends Source {
return nativeGetUrl();
}
+ /**
+ * Updates the latitude and longitude of the four corners of the image
+ *
+ * @param latLngQuad latitude and longitude of the four corners of the image
+ */
+ public void setCoordinates(LatLngQuad latLngQuad) {
+ nativeSetCoordinates(latLngQuad);
+ }
+
protected native void initialize(String layerId, LatLngQuad payload);
protected native void nativeSetUrl(String url);
@@ -132,6 +141,8 @@ public class ImageSource extends Source {
protected native void nativeSetImage(Bitmap bitmap);
+ protected native void nativeSetCoordinates(LatLngQuad latLngQuad);
+
@Override
protected native void finalize() throws Throwable;
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
index 9b59cf8967..62b08a90ed 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
@@ -5,8 +5,8 @@ import android.support.annotation.Nullable;
import android.support.annotation.Size;
import android.support.annotation.UiThread;
+import com.mapbox.geojson.Feature;
import com.mapbox.mapboxsdk.style.layers.Filter;
-import com.mapbox.services.commons.geojson.Feature;
import java.net.URL;
import java.util.ArrayList;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java
new file mode 100644
index 0000000000..0c90e4b244
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java
@@ -0,0 +1,49 @@
+package com.mapbox.mapboxsdk.utils;
+
+// TODO Remove this class if we finally include it within MAS 3.x (GeoJSON)
+public class MathUtils {
+
+ /**
+ * Test a value in specified range, returning minimum if it's below, and maximum if it's above
+ *
+ * @param value Value to test
+ * @param min Minimum value of range
+ * @param max Maximum value of range
+ * @return value if it's between min and max, min if it's below, max if it's above
+ */
+ public static double clamp(double value, double min, double max) {
+ return Math.max(min, Math.min(max, value));
+ }
+
+ /**
+ * Test a value in specified range, returning minimum if it's below, and maximum if it's above
+ *
+ * @param value Value to test
+ * @param min Minimum value of range
+ * @param max Maximum value of range
+ * @return value if it's between min and max, min if it's below, max if it's above
+ */
+ public static float clamp(float value, float min, float max) {
+ return Math.max(min, Math.min(max, value));
+ }
+
+ /**
+ * Constrains value to the given range (including min, excluding max) via modular arithmetic.
+ * <p>
+ * Same formula as used in Core GL (wrap.hpp)
+ * std::fmod((std::fmod((value - min), d) + d), d) + min;
+ *
+ * @param value Value to wrap
+ * @param min Minimum value
+ * @param max Maximum value
+ * @return Wrapped value
+ */
+ public static double wrap(double value, double min, double max) {
+ double delta = max - min;
+
+ double firstMod = (value - min) % delta;
+ double secondMod = (firstMod + delta) % delta;
+
+ return secondMod + min;
+ }
+}