diff options
author | Langston Smith <langston.smith@mapbox.com> | 2018-03-05 17:56:29 -0800 |
---|---|---|
committer | Langston Smith <langston.smith@mapbox.com> | 2018-03-05 17:56:29 -0800 |
commit | aab4b971509f6f76943e2578cb13addc13ae079b (patch) | |
tree | f671cbf3dc5c7d5078423704df165eeb070f4d0f /platform/android/MapboxGLAndroidSDK/src | |
parent | 6996e2d81abb8ea2ad23a6efe1502809bbe7e882 (diff) | |
parent | 136e536159a1e22aa4a92c4e6463893600b809d0 (diff) | |
download | qtlocation-mapboxgl-aab4b971509f6f76943e2578cb13addc13ae079b.tar.gz |
Merge branch 'master' into ls-android-readme-snapshot-dependency-line-cleanup
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src')
48 files changed, 1816 insertions, 2056 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; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml index df7ccaaca9..29ff49f47e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml @@ -1,13 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> - <android.opengl.GLSurfaceView - android:id="@+id/surfaceView" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:contentDescription="@null" - android:visibility="gone"/> - <FrameLayout android:id="@+id/markerViewContainer" android:layout_width="match_parent" diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml index 11b20f5dc5..11b20f5dc5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml index 1c6a265587..00fc05cf6d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml @@ -6,4 +6,13 @@ <dimen name="mapbox_eight_dp">8dp</dimen> <dimen name="mapbox_ninety_two_dp">92dp</dimen> <dimen name="mapbox_my_locationview_outer_circle">18dp</dimen> + + <!--Minimum scale velocity required to start animation--> + <dimen name="mapbox_minimum_scale_velocity">150dp</dimen> + + <!--Minimum scale span delta required to execute scale gesture when rotating--> + <dimen name="mapbox_minimum_scale_span_when_rotating">100dp</dimen> + + <!--Minimum angular velocity required to start rotation animation--> + <dimen name="mapbox_minimum_angular_velocity">0.025dp</dimen> </resources> diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java index 7a28d846ea..d9e3ae427d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java @@ -5,7 +5,7 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException; -import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.android.core.location.LocationEngine; import org.junit.Before; import org.junit.Test; diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index e6c1fdd0cf..f03bbdb11c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -71,6 +71,82 @@ public class LatLngBoundsTest { } @Test + public void dateLineSpanBuilder1() { + latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, -170)) + .include(new LatLng(-10, 170)) + .build(); + + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20), + latLngSpan); + } + + @Test + public void dateLineSpanBuilder2() { + latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(-10, -170)) + .include(new LatLng(10, 170)) + .build(); + + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20), + latLngSpan); + } + + @Test + public void dateLineSpanFrom1() { + latLngBounds = LatLngBounds.from(10, -170, -10, 170); + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20), + latLngSpan); + } + + @Test + public void dateLineSpanFrom2() { + latLngBounds = LatLngBounds.from(10, 170, -10, -170); + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 340), + latLngSpan); + } + + @Test + public void nearDateLineCenter1() { + latLngBounds = LatLngBounds.from(10, -175, -10, 165); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, 175), center); + } + + @Test + public void nearDateLineCenter2() { + latLngBounds = LatLngBounds.from(10, -165, -10, 175); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, -175), center); + } + + @Test + public void nearDateLineCenter3() { + latLngBounds = LatLngBounds.from(10, -170, -10, 170); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, -180), center); + } + + @Test + public void nearDateLineCenter4() { + latLngBounds = LatLngBounds.from(10, -180, -10, 0); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, 90), center); + } + + @Test + public void nearDateLineCenter5() { + latLngBounds = LatLngBounds.from(10, 180, -10, 0); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, 90), center); + } + + + @Test public void center() { LatLng center = latLngBounds.getCenter(); assertEquals("Center should match", new LatLng(1, 1), center); @@ -121,6 +197,46 @@ public class LatLngBoundsTest { } @Test + public void includesOverDateline1() { + + LatLngBounds latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, -170)) + .include(new LatLng(-10, -175)) + .include(new LatLng(0, 170)) + .build(); + + assertEquals("LatLngSpan should be the same", + new LatLngSpan(20, 20), latLngBounds.getSpan()); + } + + @Test + public void includesOverDateline2() { + + LatLngBounds latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, 170)) + .include(new LatLng(-10, 175)) + .include(new LatLng(0, -170)) + .build(); + + assertEquals("LatLngSpan should be the same", + new LatLngSpan(20, 20), latLngBounds.getSpan()); + } + + @Test + public void includesOverDateline3() { + + LatLngBounds latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, 170)) + .include(new LatLng(-10, -170)) + .include(new LatLng(0, -180)) + .include(new LatLng(5, 180)) + .build(); + + assertEquals("LatLngSpan should be the same", + new LatLngSpan(20, 20), latLngBounds.getSpan()); + } + + @Test public void containsNot() { assertFalse("LatLng should not be included", latLngBounds.contains(new LatLng(3, 1))); } @@ -131,6 +247,21 @@ public class LatLngBoundsTest { } @Test + public void worldSpan() { + assertEquals("LatLngBounds world span should be 180, 360", + GeometryConstants.LATITUDE_SPAN, LatLngBounds.world().getLatitudeSpan(), DELTA); + assertEquals("LatLngBounds world span should be 180, 360", + GeometryConstants.LONGITUDE_SPAN, LatLngBounds.world().getLongitudeSpan(), DELTA); + } + + @Test + public void emptySpan() { + LatLngBounds latLngBounds = LatLngBounds.from(GeometryConstants.MIN_LATITUDE, GeometryConstants.MAX_LONGITUDE, + GeometryConstants.MIN_LATITUDE, GeometryConstants.MAX_LONGITUDE); + assertTrue("LatLngBounds empty span", latLngBounds.isEmptySpan()); + } + + @Test public void containsBounds() { LatLngBounds inner = new LatLngBounds.Builder() .include(new LatLng(-5, -5)) diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java index eeb00355bd..5de55f47c9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java @@ -2,8 +2,13 @@ package com.mapbox.mapboxsdk.maps; import android.graphics.PointF; +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.mapboxsdk.geometry.LatLng; +import org.junit.Before; import org.junit.Test; import static org.mockito.Mockito.mock; @@ -13,16 +18,23 @@ import static org.mockito.Mockito.when; public class MapTouchListenersTest { - @Test - public void onMapClickListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); + private MapGestureDetector mapGestureDetector; + private LatLng latLng; + private PointF pointF; + + @Before + public void setUp() throws Exception { + latLng = new LatLng(); + pointF = new PointF(); Projection projection = mock(Projection.class); when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); + mapGestureDetector = new MapGestureDetector(null, + null, projection, null, null, null); + } + @Test + public void onMapClickListenerTest() throws Exception { MapboxMap.OnMapClickListener listener = mock(MapboxMap.OnMapClickListener.class); mapGestureDetector.addOnMapClickListener(listener); mapGestureDetector.notifyOnMapClickListeners(pointF); @@ -35,14 +47,6 @@ public class MapTouchListenersTest { @Test public void onMapLongClickListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); - - Projection projection = mock(Projection.class); - when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); - MapboxMap.OnMapLongClickListener listener = mock(MapboxMap.OnMapLongClickListener.class); mapGestureDetector.addOnMapLongClickListener(listener); mapGestureDetector.notifyOnMapLongClickListeners(pointF); @@ -55,14 +59,6 @@ public class MapTouchListenersTest { @Test public void onFlingListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); - - Projection projection = mock(Projection.class); - when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); - MapboxMap.OnFlingListener listener = mock(MapboxMap.OnFlingListener.class); mapGestureDetector.addOnFlingListener(listener); mapGestureDetector.notifyOnFlingListeners(); @@ -75,14 +71,6 @@ public class MapTouchListenersTest { @Test public void onScrollListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); - - Projection projection = mock(Projection.class); - when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); - MapboxMap.OnScrollListener listener = mock(MapboxMap.OnScrollListener.class); mapGestureDetector.addOnScrollListener(listener); mapGestureDetector.notifyOnScrollListeners(); @@ -92,4 +80,88 @@ public class MapTouchListenersTest { mapGestureDetector.notifyOnScrollListeners(); verify(listener, times(1)).onScroll(); } + + @Test + public void onMoveListenerTest() throws Exception { + MapboxMap.OnMoveListener listener = mock(MapboxMap.OnMoveListener.class); + MoveGestureDetector detector = mock(MoveGestureDetector.class); + mapGestureDetector.addOnMoveListener(listener); + mapGestureDetector.notifyOnMoveBeginListeners(detector); + mapGestureDetector.notifyOnMoveListeners(detector); + mapGestureDetector.notifyOnMoveEndListeners(detector); + verify(listener, times(1)).onMoveBegin(detector); + verify(listener, times(1)).onMove(detector); + verify(listener, times(1)).onMoveEnd(detector); + + mapGestureDetector.removeOnMoveListener(listener); + mapGestureDetector.notifyOnMoveBeginListeners(detector); + mapGestureDetector.notifyOnMoveListeners(detector); + mapGestureDetector.notifyOnMoveEndListeners(detector); + verify(listener, times(1)).onMoveBegin(detector); + verify(listener, times(1)).onMove(detector); + verify(listener, times(1)).onMoveEnd(detector); + } + + @Test + public void onRotateListenerTest() throws Exception { + MapboxMap.OnRotateListener listener = mock(MapboxMap.OnRotateListener.class); + RotateGestureDetector detector = mock(RotateGestureDetector.class); + mapGestureDetector.addOnRotateListener(listener); + mapGestureDetector.notifyOnRotateBeginListeners(detector); + mapGestureDetector.notifyOnRotateListeners(detector); + mapGestureDetector.notifyOnRotateEndListeners(detector); + verify(listener, times(1)).onRotateBegin(detector); + verify(listener, times(1)).onRotate(detector); + verify(listener, times(1)).onRotateEnd(detector); + + mapGestureDetector.removeOnRotateListener(listener); + mapGestureDetector.notifyOnRotateBeginListeners(detector); + mapGestureDetector.notifyOnRotateListeners(detector); + mapGestureDetector.notifyOnRotateEndListeners(detector); + verify(listener, times(1)).onRotateBegin(detector); + verify(listener, times(1)).onRotate(detector); + verify(listener, times(1)).onRotateEnd(detector); + } + + @Test + public void onScaleListenerTest() throws Exception { + MapboxMap.OnScaleListener listener = mock(MapboxMap.OnScaleListener.class); + StandardScaleGestureDetector detector = mock(StandardScaleGestureDetector.class); + mapGestureDetector.addOnScaleListener(listener); + mapGestureDetector.notifyOnScaleBeginListeners(detector); + mapGestureDetector.notifyOnScaleListeners(detector); + mapGestureDetector.notifyOnScaleEndListeners(detector); + verify(listener, times(1)).onScaleBegin(detector); + verify(listener, times(1)).onScale(detector); + verify(listener, times(1)).onScaleEnd(detector); + + mapGestureDetector.removeOnScaleListener(listener); + mapGestureDetector.notifyOnScaleBeginListeners(detector); + mapGestureDetector.notifyOnScaleListeners(detector); + mapGestureDetector.notifyOnScaleEndListeners(detector); + verify(listener, times(1)).onScaleBegin(detector); + verify(listener, times(1)).onScale(detector); + verify(listener, times(1)).onScaleEnd(detector); + } + + @Test + public void onShoveListenerTest() throws Exception { + MapboxMap.OnShoveListener listener = mock(MapboxMap.OnShoveListener.class); + ShoveGestureDetector detector = mock(ShoveGestureDetector.class); + mapGestureDetector.addShoveListener(listener); + mapGestureDetector.notifyOnShoveBeginListeners(detector); + mapGestureDetector.notifyOnShoveListeners(detector); + mapGestureDetector.notifyOnShoveEndListeners(detector); + verify(listener, times(1)).onShoveBegin(detector); + verify(listener, times(1)).onShove(detector); + verify(listener, times(1)).onShoveEnd(detector); + + mapGestureDetector.removeShoveListener(listener); + mapGestureDetector.notifyOnShoveBeginListeners(detector); + mapGestureDetector.notifyOnShoveListeners(detector); + mapGestureDetector.notifyOnShoveEndListeners(detector); + verify(listener, times(1)).onShoveBegin(detector); + verify(listener, times(1)).onShove(detector); + verify(listener, times(1)).onShoveEnd(detector); + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java index 5e9f94db28..d61947f00e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java @@ -23,7 +23,7 @@ public class MapboxMapTest { mock(TrackingSettings.class), mock(MyLocationViewSettings.class), mock(Projection.class), - mock(MapboxMap.OnRegisterTouchListener.class), + mock(MapboxMap.OnGesturesManagerInteractionListener.class), mock(AnnotationManager.class), mock(CameraChangeDispatcher.class)); } |