[android] Move into platform subdirectory
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
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)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 mContext;
|
||||
protected boolean mGestureInProgress;
|
||||
|
||||
protected MotionEvent mPrevEvent;
|
||||
protected MotionEvent mCurrEvent;
|
||||
|
||||
protected float mCurrPressure;
|
||||
protected float mPrevPressure;
|
||||
protected long mTimeDelta;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
mContext = 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 (!mGestureInProgress) {
|
||||
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 mGestureInProgress) 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
|
||||
* mGestureInProgress).
|
||||
*
|
||||
*
|
||||
* @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 = mPrevEvent;
|
||||
|
||||
// Reset mCurrEvent
|
||||
if (mCurrEvent != null) {
|
||||
mCurrEvent.recycle();
|
||||
mCurrEvent = null;
|
||||
}
|
||||
mCurrEvent = MotionEvent.obtain(curr);
|
||||
|
||||
// Delta time
|
||||
mTimeDelta = curr.getEventTime() - prev.getEventTime();
|
||||
|
||||
// Pressure
|
||||
mCurrPressure = curr.getPressure(curr.getActionIndex());
|
||||
mPrevPressure = prev.getPressure(prev.getActionIndex());
|
||||
}
|
||||
|
||||
protected void resetState() {
|
||||
if (mPrevEvent != null) {
|
||||
mPrevEvent.recycle();
|
||||
mPrevEvent = null;
|
||||
}
|
||||
if (mCurrEvent != null) {
|
||||
mCurrEvent.recycle();
|
||||
mCurrEvent = null;
|
||||
}
|
||||
mGestureInProgress = 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 mGestureInProgress;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 mTimeDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the event time of the current GestureDetector event being
|
||||
* processed.
|
||||
*
|
||||
* @return Current GestureDetector event time in milliseconds.
|
||||
*/
|
||||
public long getEventTime() {
|
||||
return mCurrEvent.getEventTime();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
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)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 mListener;
|
||||
|
||||
private PointF mFocusExternal = new PointF();
|
||||
private PointF mFocusDeltaExternal = new PointF();
|
||||
|
||||
public MoveGestureDetector(Context context, OnMoveGestureListener listener) {
|
||||
super(context);
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleStartProgressEvent(int actionCode, MotionEvent event) {
|
||||
switch (actionCode) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
resetState(); // In case we missed an UP/CANCEL event
|
||||
|
||||
mPrevEvent = MotionEvent.obtain(event);
|
||||
mTimeDelta = 0;
|
||||
|
||||
updateStateByEvent(event);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mGestureInProgress = mListener.onMoveBegin(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleInProgressEvent(int actionCode, MotionEvent event) {
|
||||
switch (actionCode) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mListener.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 (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
|
||||
final boolean updatePrevious = mListener.onMove(this);
|
||||
if (updatePrevious) {
|
||||
mPrevEvent.recycle();
|
||||
mPrevEvent = MotionEvent.obtain(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateStateByEvent(MotionEvent curr) {
|
||||
super.updateStateByEvent(curr);
|
||||
|
||||
final MotionEvent prev = mPrevEvent;
|
||||
|
||||
// Focus intenal
|
||||
PointF mCurrFocusInternal = determineFocalPoint(curr);
|
||||
PointF mPrevFocusInternal = determineFocalPoint(prev);
|
||||
|
||||
// Focus external
|
||||
// - Prevent skipping of focus delta when a finger is added or removed
|
||||
boolean mSkipNextMoveEvent = prev.getPointerCount() != curr
|
||||
.getPointerCount();
|
||||
mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO
|
||||
: new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x,
|
||||
mCurrFocusInternal.y - mPrevFocusInternal.y);
|
||||
|
||||
// - Don't directly use mFocusInternal (or skipping will occur). Add
|
||||
// unskipped delta values to mFocusExternal instead.
|
||||
mFocusExternal.x += mFocusDeltaExternal.x;
|
||||
mFocusExternal.y += mFocusDeltaExternal.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine (multi)finger focal point (a.k.a. center point between all
|
||||
* fingers)
|
||||
*
|
||||
* @param e
|
||||
* @return PointF focal point
|
||||
*/
|
||||
private PointF determineFocalPoint(MotionEvent e) {
|
||||
// Number of fingers on screen
|
||||
final int pCount = e.getPointerCount();
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
|
||||
for (int i = 0; i < pCount; i++) {
|
||||
x += e.getX(i);
|
||||
y += e.getY(i);
|
||||
}
|
||||
|
||||
return new PointF(x / pCount, y / pCount);
|
||||
}
|
||||
|
||||
public float getFocusX() {
|
||||
return mFocusExternal.x;
|
||||
}
|
||||
|
||||
public float getFocusY() {
|
||||
return mFocusExternal.y;
|
||||
}
|
||||
|
||||
public PointF getFocusDelta() {
|
||||
return mFocusDeltaExternal;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
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)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 mListener;
|
||||
private boolean mSloppyGesture;
|
||||
|
||||
public RotateGestureDetector(Context context,
|
||||
OnRotateGestureListener listener) {
|
||||
super(context);
|
||||
mListener = 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
|
||||
mPrevEvent = MotionEvent.obtain(event);
|
||||
mTimeDelta = 0;
|
||||
|
||||
updateStateByEvent(event);
|
||||
|
||||
// See if we have a sloppy gesture
|
||||
mSloppyGesture = isSloppyGesture(event);
|
||||
if (!mSloppyGesture) {
|
||||
// No, start gesture now
|
||||
mGestureInProgress = mListener.onRotateBegin(this);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (!mSloppyGesture) {
|
||||
break;
|
||||
}
|
||||
|
||||
// See if we still have a sloppy gesture
|
||||
mSloppyGesture = isSloppyGesture(event);
|
||||
if (!mSloppyGesture) {
|
||||
// No, start normal gesture now
|
||||
mGestureInProgress = mListener.onRotateBegin(this);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
if (!mSloppyGesture) {
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleInProgressEvent(int actionCode, MotionEvent event) {
|
||||
switch (actionCode) {
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
// Gesture ended but
|
||||
updateStateByEvent(event);
|
||||
|
||||
if (!mSloppyGesture) {
|
||||
mListener.onRotateEnd(this);
|
||||
}
|
||||
|
||||
resetState();
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
if (!mSloppyGesture) {
|
||||
mListener.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 (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
|
||||
final boolean updatePrevious = mListener.onRotate(this);
|
||||
if (updatePrevious) {
|
||||
mPrevEvent.recycle();
|
||||
mPrevEvent = MotionEvent.obtain(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetState() {
|
||||
super.resetState();
|
||||
mSloppyGesture = 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(mPrevFingerDiffY, mPrevFingerDiffX)
|
||||
- Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX);
|
||||
return (float) (diffRadians * 180.0 / Math.PI);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package com.almeros.android.multitouch.gesturedetectors;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* @author Robert Nordan (robert.nordan@norkart.no)
|
||||
*
|
||||
* Copyright (c) 2013, Norkart AS
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 mPrevAverageY;
|
||||
private float mCurrAverageY;
|
||||
|
||||
private final OnShoveGestureListener mListener;
|
||||
private boolean mSloppyGesture;
|
||||
|
||||
public ShoveGestureDetector(Context context, OnShoveGestureListener listener) {
|
||||
super(context);
|
||||
mListener = 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
|
||||
mPrevEvent = MotionEvent.obtain(event);
|
||||
mTimeDelta = 0;
|
||||
|
||||
updateStateByEvent(event);
|
||||
|
||||
// See if we have a sloppy gesture
|
||||
mSloppyGesture = isSloppyGesture(event);
|
||||
if (!mSloppyGesture) {
|
||||
// No, start gesture now
|
||||
mGestureInProgress = mListener.onShoveBegin(this);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (!mSloppyGesture) {
|
||||
break;
|
||||
}
|
||||
|
||||
// See if we still have a sloppy gesture
|
||||
mSloppyGesture = isSloppyGesture(event);
|
||||
if (!mSloppyGesture) {
|
||||
// No, start normal gesture now
|
||||
mGestureInProgress = mListener.onShoveBegin(this);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
if (!mSloppyGesture) {
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleInProgressEvent(int actionCode, MotionEvent event) {
|
||||
switch (actionCode) {
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
// Gesture ended but
|
||||
updateStateByEvent(event);
|
||||
|
||||
if (!mSloppyGesture) {
|
||||
mListener.onShoveEnd(this);
|
||||
}
|
||||
|
||||
resetState();
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
if (!mSloppyGesture) {
|
||||
mListener.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 (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD
|
||||
&& Math.abs(getShovePixelsDelta()) > 0.5f) {
|
||||
final boolean updatePrevious = mListener.onShove(this);
|
||||
if (updatePrevious) {
|
||||
mPrevEvent.recycle();
|
||||
mPrevEvent = MotionEvent.obtain(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetState() {
|
||||
super.resetState();
|
||||
mSloppyGesture = false;
|
||||
mPrevAverageY = 0.0f;
|
||||
mCurrAverageY = 0.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateStateByEvent(MotionEvent curr) {
|
||||
super.updateStateByEvent(curr);
|
||||
|
||||
final MotionEvent prev = mPrevEvent;
|
||||
float py0 = prev.getY(0);
|
||||
float py1 = prev.getY(1);
|
||||
mPrevAverageY = (py0 + py1) / 2.0f;
|
||||
|
||||
float cy0 = curr.getY(0);
|
||||
float cy1 = curr.getY(1);
|
||||
mCurrAverageY = (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(mCurrFingerDiffY, mCurrFingerDiffX));
|
||||
// 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 mCurrAverageY - mPrevAverageY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
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)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 mEdgeSlop;
|
||||
|
||||
protected float mPrevFingerDiffX;
|
||||
protected float mPrevFingerDiffY;
|
||||
protected float mCurrFingerDiffX;
|
||||
protected float mCurrFingerDiffY;
|
||||
|
||||
private float mCurrLen;
|
||||
private float mPrevLen;
|
||||
|
||||
private PointF mFocus;
|
||||
|
||||
public TwoFingerGestureDetector(Context context) {
|
||||
super(context);
|
||||
|
||||
ViewConfiguration config = ViewConfiguration.get(context);
|
||||
mEdgeSlop = 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 = mPrevEvent;
|
||||
|
||||
mCurrLen = -1;
|
||||
mPrevLen = -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;
|
||||
mPrevFingerDiffX = pvx;
|
||||
mPrevFingerDiffY = 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;
|
||||
mCurrFingerDiffX = cvx;
|
||||
mCurrFingerDiffY = cvy;
|
||||
mFocus = 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 (mCurrLen == -1) {
|
||||
final float cvx = mCurrFingerDiffX;
|
||||
final float cvy = mCurrFingerDiffY;
|
||||
mCurrLen = (float) Math.sqrt(cvx * cvx + cvy * cvy);
|
||||
}
|
||||
return mCurrLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the previous distance between the two pointers forming the gesture
|
||||
* in progress.
|
||||
*
|
||||
* @return Previous distance between pointers in pixels.
|
||||
*/
|
||||
public float getPreviousSpan() {
|
||||
if (mPrevLen == -1) {
|
||||
final float pvx = mPrevFingerDiffX;
|
||||
final float pvy = mPrevFingerDiffY;
|
||||
mPrevLen = (float) Math.sqrt(pvx * pvx + pvy * pvy);
|
||||
}
|
||||
return mPrevLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.getX() - event.getRawX();
|
||||
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.getY() - event.getRawY();
|
||||
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 = mContext.getResources().getDisplayMetrics();
|
||||
float mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
|
||||
float mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
|
||||
|
||||
final float edgeSlop = mEdgeSlop;
|
||||
|
||||
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 > mRightSlopEdge
|
||||
|| y0 > mBottomSlopEdge;
|
||||
boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > mRightSlopEdge
|
||||
|| y1 > mBottomSlopEdge;
|
||||
|
||||
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 e Motion Event
|
||||
* @return PointF focal point
|
||||
*/
|
||||
public static PointF determineFocalPoint(MotionEvent e) {
|
||||
// Number of fingers on screen
|
||||
final int pCount = e.getPointerCount();
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
|
||||
for (int i = 0; i < pCount; i++) {
|
||||
x += e.getX(i);
|
||||
y += e.getY(i);
|
||||
}
|
||||
|
||||
return new PointF(x / pCount, y / pCount);
|
||||
}
|
||||
|
||||
public float getFocusX() {
|
||||
return mFocus.x;
|
||||
}
|
||||
|
||||
public float getFocusY() {
|
||||
return mFocus.y;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Do not use this package. Used internally by the SDK.
|
||||
*/
|
||||
package com.almeros.android.multitouch.gesturedetectors;
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.mapbox.mapboxsdk;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.mapbox.mapboxsdk.utils.ApiAccess;
|
||||
import com.mapbox.mapboxsdk.views.MapView;
|
||||
|
||||
public class MapFragment extends Fragment {
|
||||
|
||||
//
|
||||
// Static members
|
||||
//
|
||||
|
||||
// Tag used for logging
|
||||
private static final String TAG = "MapFragment";
|
||||
|
||||
//
|
||||
// Instance members
|
||||
//
|
||||
|
||||
// The map
|
||||
private MapView mMap;
|
||||
|
||||
//
|
||||
// Lifecycle events
|
||||
//
|
||||
|
||||
// Called when the fragment is created
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
Log.v(TAG, "onCreateView");
|
||||
|
||||
// Create the map
|
||||
mMap = (MapView) inflater.inflate(R.layout.fragment_mapview, container, false);
|
||||
|
||||
// Set accessToken
|
||||
mMap.setAccessToken(ApiAccess.getToken(container.getContext()));
|
||||
|
||||
// Need to pass on any saved state to the map
|
||||
mMap.onCreate(savedInstanceState);
|
||||
|
||||
// Return the map as the root view
|
||||
return mMap;
|
||||
}
|
||||
|
||||
// Called when the fragment is destroyed
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
Log.v(TAG, "onDestroyView");
|
||||
|
||||
// Need to pass on to view
|
||||
mMap.onDestroy();
|
||||
mMap = null;
|
||||
}
|
||||
|
||||
// Called when the fragment is visible
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
Log.v(TAG, "onStart");
|
||||
|
||||
// Need to pass on to view
|
||||
mMap.onStart();
|
||||
}
|
||||
|
||||
// Called when the fragment is invisible
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
Log.v(TAG, "onStop");
|
||||
|
||||
// Need to pass on to view
|
||||
mMap.onStop();
|
||||
}
|
||||
|
||||
// Called when the fragment is in the background
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
Log.v(TAG, "onPause");
|
||||
|
||||
// Need to pass on to view
|
||||
mMap.onPause();
|
||||
}
|
||||
|
||||
// Called when the fragment is no longer in the background
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Log.v(TAG, "onResume");
|
||||
|
||||
// Need to pass on to view
|
||||
mMap.onResume();
|
||||
}
|
||||
|
||||
// Called before fragment is destroyed
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
Log.v(TAG, "onSaveInstanceState");
|
||||
|
||||
// Need to retrieve any saved state from the map
|
||||
mMap.onSaveInstanceState(outState);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
Log.v(TAG, "OnLowMemory");
|
||||
|
||||
// Need to pass on to view
|
||||
mMap.onLowMemory();
|
||||
super.onLowMemory();
|
||||
}
|
||||
|
||||
//
|
||||
// Property methods
|
||||
//
|
||||
|
||||
public MapView getMap() {
|
||||
return mMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.views.MapView;
|
||||
|
||||
/**
|
||||
* Annotation is the most general kind of overlay on top of a map,
|
||||
* from which {@link InfoWindow} and {@link Marker} are derived: it manages
|
||||
* attachment to a map and identification, but does not require
|
||||
* content to be placed at a geographical point.
|
||||
*/
|
||||
public abstract class Annotation implements Comparable<Annotation> {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* The annotation id
|
||||
* </p>
|
||||
* Internal C++ id is stored as unsigned int.
|
||||
*/
|
||||
private long id = -1; // -1 unless added to a MapView
|
||||
private MapView mapView;
|
||||
|
||||
protected Annotation() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Gets the annotation's unique ID.
|
||||
* </p>
|
||||
* This ID is unique for a MapView instance and is suitable for associating your own extra
|
||||
* data with.
|
||||
*/
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
if (mapView == null) {
|
||||
return;
|
||||
}
|
||||
mapView.removeAnnotation(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*/
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*/
|
||||
public void setMapView(MapView mapView) {
|
||||
this.mapView = mapView;
|
||||
}
|
||||
|
||||
protected MapView getMapView() {
|
||||
if (mapView == null) {
|
||||
return null;
|
||||
}
|
||||
return mapView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull Annotation annotation) {
|
||||
if (id < annotation.getId()) {
|
||||
return 1;
|
||||
} else if (id > annotation.getId()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Equal
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Annotation that = (Annotation) o;
|
||||
|
||||
return getId() == that.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (getId() ^ (getId() >>> 32));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PointF;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.views.MapView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A tooltip view. This is a UI element placed over a map at a specific geographic
|
||||
* location.
|
||||
* </p>
|
||||
*/
|
||||
public class InfoWindow {
|
||||
|
||||
private WeakReference<Marker> mBoundMarker;
|
||||
private WeakReference<MapView> mMapView;
|
||||
private float mMarkerHeightOffset;
|
||||
private float mViewWidthOffset;
|
||||
private PointF mCoordinates;
|
||||
private boolean mIsVisible;
|
||||
protected View mView;
|
||||
|
||||
static int mTitleId = 0;
|
||||
static int mDescriptionId = 0;
|
||||
static int mSubDescriptionId = 0;
|
||||
static int mImageId = 0;
|
||||
|
||||
InfoWindow(int layoutResId, MapView mapView) {
|
||||
View view = LayoutInflater.from(mapView.getContext()).inflate(layoutResId, mapView, false);
|
||||
|
||||
if (mTitleId == 0) {
|
||||
setResIds(mapView.getContext());
|
||||
}
|
||||
|
||||
initialize(view, mapView);
|
||||
}
|
||||
|
||||
InfoWindow(View view, MapView mapView) {
|
||||
initialize(view, mapView);
|
||||
}
|
||||
|
||||
private void initialize(View view, MapView mapView) {
|
||||
mMapView = new WeakReference<>(mapView);
|
||||
mIsVisible = false;
|
||||
mView = view;
|
||||
|
||||
// default behavior: close it when clicking on the tooltip:
|
||||
mView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent e) {
|
||||
if (e.getAction() == MotionEvent.ACTION_UP) {
|
||||
boolean handledDefaultClick = false;
|
||||
MapView.OnInfoWindowClickListener onInfoWindowClickListener =
|
||||
mMapView.get().getOnInfoWindowClickListener();
|
||||
if (onInfoWindowClickListener != null) {
|
||||
handledDefaultClick = onInfoWindowClickListener.onMarkerClick(getBoundMarker());
|
||||
}
|
||||
|
||||
if (!handledDefaultClick) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* open the window at the specified position.
|
||||
*
|
||||
* @param boundMarker the marker on which is hooked the view
|
||||
* @param position to place the window on the map
|
||||
* @param offsetX (&offsetY) the offset of the view to the position, in pixels.
|
||||
* This allows to offset the view from the object position.
|
||||
* @return this infowindow
|
||||
*/
|
||||
InfoWindow open(Marker boundMarker, LatLng position, int offsetX, int offsetY) {
|
||||
setBoundMarker(boundMarker);
|
||||
|
||||
MapView.LayoutParams lp = new MapView.LayoutParams(MapView.LayoutParams.WRAP_CONTENT, MapView.LayoutParams.WRAP_CONTENT);
|
||||
mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
|
||||
|
||||
// Calculate y-offset for update method
|
||||
mMarkerHeightOffset = -mView.getMeasuredHeight() + offsetY;
|
||||
|
||||
// Calculate default Android x,y coordinate
|
||||
mCoordinates = mMapView.get().toScreenLocation(position);
|
||||
float x = mCoordinates.x - (mView.getMeasuredWidth() / 2) + offsetX;
|
||||
float y = mCoordinates.y - mView.getMeasuredHeight() + offsetY;
|
||||
|
||||
if (mView instanceof InfoWindowView) {
|
||||
// only apply repositioning/margin for InfoWindowView
|
||||
Resources resources = mMapView.get().getContext().getResources();
|
||||
|
||||
// get right/left popup window
|
||||
float rightSideInfowWindow = x + mView.getMeasuredWidth();
|
||||
float leftSideInfoWindow = x;
|
||||
|
||||
// get right/left map view
|
||||
final float mapRight = mMapView.get().getRight();
|
||||
final float mapLeft = mMapView.get().getLeft();
|
||||
|
||||
float marginHorizontal = resources.getDimension(R.dimen.infowindow_margin);
|
||||
float tipViewOffset = resources.getDimension(R.dimen.infowindow_tipview_width) / 2;
|
||||
float tipViewMarginLeft = mView.getMeasuredWidth() / 2 - tipViewOffset;
|
||||
|
||||
boolean outOfBoundsLeft = false;
|
||||
boolean outOfBoundsRight = false;
|
||||
|
||||
// if out of bounds right
|
||||
if (rightSideInfowWindow > mapRight) {
|
||||
outOfBoundsRight = true;
|
||||
x -= rightSideInfowWindow - mapRight;
|
||||
tipViewMarginLeft += rightSideInfowWindow - mapRight + tipViewOffset;
|
||||
rightSideInfowWindow = x + mView.getMeasuredWidth();
|
||||
}
|
||||
|
||||
// fit screen left
|
||||
if (leftSideInfoWindow < mapLeft) {
|
||||
outOfBoundsLeft = true;
|
||||
x += mapLeft - leftSideInfoWindow;
|
||||
tipViewMarginLeft -= mapLeft - leftSideInfoWindow + tipViewOffset;
|
||||
leftSideInfoWindow = x;
|
||||
}
|
||||
|
||||
// Add margin right
|
||||
if (outOfBoundsRight && mapRight - rightSideInfowWindow < marginHorizontal) {
|
||||
x -= marginHorizontal - (mapRight - rightSideInfowWindow);
|
||||
tipViewMarginLeft += marginHorizontal - (mapRight - rightSideInfowWindow) - tipViewOffset;
|
||||
leftSideInfoWindow = x;
|
||||
}
|
||||
|
||||
// Add margin left
|
||||
if (outOfBoundsLeft && leftSideInfoWindow - mapLeft < marginHorizontal) {
|
||||
x += marginHorizontal - (leftSideInfoWindow - mapLeft);
|
||||
tipViewMarginLeft -= (marginHorizontal - (leftSideInfoWindow - mapLeft)) - tipViewOffset;
|
||||
}
|
||||
|
||||
// Adjust tipView
|
||||
InfoWindowView infoWindowView = (InfoWindowView) mView;
|
||||
infoWindowView.setTipViewMarginLeft((int) tipViewMarginLeft);
|
||||
}
|
||||
|
||||
// set anchor popupwindowview
|
||||
mView.setX(x);
|
||||
mView.setY(y);
|
||||
|
||||
// Calculate x-offset for update method
|
||||
mViewWidthOffset = x - mCoordinates.x - offsetX;
|
||||
|
||||
close(); //if it was already opened
|
||||
mMapView.get().addView(mView, lp);
|
||||
mIsVisible = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this InfoWindow if it is visible, otherwise don't do anything.
|
||||
*
|
||||
* @return this info window
|
||||
*/
|
||||
InfoWindow close() {
|
||||
if (mIsVisible) {
|
||||
mIsVisible = false;
|
||||
((ViewGroup) mView.getParent()).removeView(mView);
|
||||
setBoundMarker(null);
|
||||
onClose();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the view that is displayed when the InfoWindow opens.
|
||||
* This retrieves data from overlayItem and shows it in the tooltip.
|
||||
*
|
||||
* @param overlayItem the tapped overlay item
|
||||
*/
|
||||
void adaptDefaultMarker(Marker overlayItem) {
|
||||
String title = overlayItem.getTitle();
|
||||
((TextView) mView.findViewById(mTitleId /*R.id.title*/)).setText(title);
|
||||
String snippet = overlayItem.getSnippet();
|
||||
((TextView) mView.findViewById(mDescriptionId /*R.id.description*/)).setText(snippet);
|
||||
|
||||
/*
|
||||
//handle sub-description, hiding or showing the text view:
|
||||
TextView subDescText = (TextView) mView.findViewById(mSubDescriptionId);
|
||||
String subDesc = overlayItem.getSubDescription();
|
||||
if ("".equals(subDesc)) {
|
||||
subDescText.setVisibility(View.GONE);
|
||||
} else {
|
||||
subDescText.setText(subDesc);
|
||||
subDescText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private void onClose() {
|
||||
mMapView.get().deselectMarker(getBoundMarker());
|
||||
}
|
||||
|
||||
InfoWindow setBoundMarker(Marker boundMarker) {
|
||||
mBoundMarker = new WeakReference<>(boundMarker);
|
||||
return this;
|
||||
}
|
||||
|
||||
Marker getBoundMarker() {
|
||||
if (mBoundMarker == null) {
|
||||
return null;
|
||||
}
|
||||
return mBoundMarker.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a context, set the resource ids for the layout
|
||||
* of the InfoWindow.
|
||||
*
|
||||
* @param context the apps Context
|
||||
*/
|
||||
private static void setResIds(Context context) {
|
||||
String packageName = context.getPackageName(); //get application package name
|
||||
mTitleId = context.getResources().getIdentifier("id/infowindow_title", null, packageName);
|
||||
mDescriptionId =
|
||||
context.getResources().getIdentifier("id/infowindow_description", null, packageName);
|
||||
mSubDescriptionId = context.getResources()
|
||||
.getIdentifier("id/infowindow_subdescription", null, packageName);
|
||||
mImageId = context.getResources().getIdentifier("id/infowindow_image", null, packageName);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
MapView mapView = mMapView.get();
|
||||
Marker marker = mBoundMarker.get();
|
||||
if (mapView != null && marker != null) {
|
||||
mCoordinates = mapView.toScreenLocation(marker.getPosition());
|
||||
mView.setX(mCoordinates.x + mViewWidthOffset);
|
||||
mView.setY(mCoordinates.y + mMarkerHeightOffset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
|
||||
final class InfoWindowTipView extends View {
|
||||
|
||||
private Paint mPaint;
|
||||
private Path mPath;
|
||||
private int mLineWidth;
|
||||
|
||||
public InfoWindowTipView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
mPath = new Path();
|
||||
mLineWidth = (int) context.getResources().getDimension(R.dimen.infowindow_line_width);
|
||||
mPaint = new Paint();
|
||||
mPaint.setColor(Color.WHITE);
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setStrokeWidth(0.0f);
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
int height = getMeasuredHeight();
|
||||
int width = getMeasuredWidth();
|
||||
|
||||
mPath.rewind();
|
||||
|
||||
this.mPaint.setColor(Color.WHITE);
|
||||
this.mPaint.setAntiAlias(true);
|
||||
this.mPaint.setStrokeWidth(0.0f);
|
||||
this.mPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
mPath.moveTo(0, 0);
|
||||
mPath.lineTo(width, 0);
|
||||
mPath.lineTo((width / 2), height);
|
||||
mPath.lineTo(0, 0);
|
||||
canvas.drawPath(mPath, this.mPaint);
|
||||
|
||||
mPath.rewind();
|
||||
|
||||
this.mPaint.setColor(Color.parseColor("#C2C2C2"));
|
||||
this.mPaint.setAntiAlias(true);
|
||||
this.mPaint.setStrokeWidth(mLineWidth);
|
||||
this.mPaint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
mPath.moveTo(0, 0);
|
||||
mPath.lineTo(width / 2, height);
|
||||
mPath.lineTo(width, 0);
|
||||
canvas.drawPath(mPath, this.mPaint);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
|
||||
class InfoWindowView extends RelativeLayout {
|
||||
|
||||
private InfoWindowTipView mTipView;
|
||||
|
||||
public InfoWindowView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public InfoWindowView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public InfoWindowView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
private void initialize(Context context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.infowindow_content, this);
|
||||
mTipView = (InfoWindowTipView) findViewById(R.id.infowindow_tipview);
|
||||
}
|
||||
|
||||
void setTipViewMarginLeft(int marginLeft) {
|
||||
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mTipView.getLayoutParams();
|
||||
layoutParams.leftMargin = marginLeft;
|
||||
// This is a bit of a hack but prevents an occasional gap between the InfoWindow
|
||||
layoutParams.topMargin = (int) getResources().getDimension(R.dimen.infowindow_offset);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.views.MapView;
|
||||
|
||||
/**
|
||||
* A marker is a map overlay that shows an icon image at a specific
|
||||
* geographical location and can be associated with a {@link InfoWindow}
|
||||
* that is shown when the marker is tapped.
|
||||
*/
|
||||
public final class Marker extends Annotation {
|
||||
|
||||
private LatLng position;
|
||||
private String snippet;
|
||||
private Sprite icon;
|
||||
private String title;
|
||||
private InfoWindow infoWindow = null;
|
||||
private boolean infoWindowShown = false;
|
||||
private int topOffsetPixels;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Marker() {
|
||||
super();
|
||||
}
|
||||
|
||||
public LatLng getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public String getSnippet() {
|
||||
return snippet;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*/
|
||||
public void hideInfoWindow() {
|
||||
if (infoWindow != null) {
|
||||
infoWindow.close();
|
||||
}
|
||||
infoWindowShown = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*/
|
||||
public boolean isInfoWindowShown() {
|
||||
return infoWindowShown;
|
||||
}
|
||||
|
||||
void setPosition(LatLng position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
void setSnippet(String snippet) {
|
||||
this.snippet = snippet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*/
|
||||
public void setIcon(@Nullable Sprite icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public Sprite getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*/
|
||||
public InfoWindow showInfoWindow() {
|
||||
if (getMapView() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MapView.InfoWindowAdapter infoWindowAdapter = getMapView().getInfoWindowAdapter();
|
||||
if (infoWindowAdapter != null) {
|
||||
// end developer is using a custom InfoWindowAdapter
|
||||
View content = infoWindowAdapter.getInfoWindow(this);
|
||||
if (content != null) {
|
||||
infoWindow = new InfoWindow(content, getMapView());
|
||||
showInfoWindow(infoWindow);
|
||||
return infoWindow;
|
||||
}
|
||||
}
|
||||
|
||||
getInfoWindow().adaptDefaultMarker(this);
|
||||
return showInfoWindow(getInfoWindow());
|
||||
}
|
||||
|
||||
private InfoWindow showInfoWindow(InfoWindow iw) {
|
||||
iw.open(this, getPosition(), 0, topOffsetPixels);
|
||||
infoWindowShown = true;
|
||||
return iw;
|
||||
}
|
||||
|
||||
private InfoWindow getInfoWindow() {
|
||||
if (infoWindow == null) {
|
||||
infoWindow = new InfoWindow(R.layout.infowindow_view, getMapView());
|
||||
}
|
||||
return infoWindow;
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
void setVisible(boolean visible) {
|
||||
super.setVisible(visible);
|
||||
if (!visible && infoWindowShown) {
|
||||
hideInfoWindow();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*/
|
||||
public void setTopOffsetPixels(int topOffsetPixels) {
|
||||
this.topOffsetPixels = topOffsetPixels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
|
||||
Marker marker = (Marker) o;
|
||||
return !(getPosition() != null ? !getPosition().equals(marker.getPosition()) : marker.getPosition() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + (getPosition() != null ? getPosition().hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Marker [position[" + getPosition() + "]]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A builder object for the options configuration {@link com.mapbox.mapboxsdk.annotations.Marker} and
|
||||
* {@link com.mapbox.mapboxsdk.annotations.InfoWindow}
|
||||
* instances on maps.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Example</h3>
|
||||
*
|
||||
* <pre>
|
||||
* mMapView.addMarker(new MarkerOptions()
|
||||
* .title("Intersection")
|
||||
* .snippet("H St NW with 15th St NW")
|
||||
* .position(new LatLng(38.9002073, -77.03364419)));
|
||||
* </pre>
|
||||
*/
|
||||
public final class MarkerOptions implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<MarkerOptions> CREATOR
|
||||
= new Parcelable.Creator<MarkerOptions>() {
|
||||
public MarkerOptions createFromParcel(Parcel in) {
|
||||
return new MarkerOptions(in);
|
||||
}
|
||||
|
||||
public MarkerOptions[] newArray(int size) {
|
||||
return new MarkerOptions[size];
|
||||
}
|
||||
};
|
||||
|
||||
private MarkerOptions(Parcel in) {
|
||||
marker = new Marker();
|
||||
position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
|
||||
snippet(in.readString());
|
||||
String spriteId = in.readString();
|
||||
Bitmap spriteBitmap = in.readParcelable(Bitmap.class.getClassLoader());
|
||||
Sprite icon = new Sprite(spriteId, spriteBitmap);
|
||||
icon(icon);
|
||||
title(in.readString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeParcelable(getPosition(), flags);
|
||||
out.writeString(getSnippet());
|
||||
out.writeString(getIcon().getId());
|
||||
out.writeParcelable(getIcon().getBitmap(), flags);
|
||||
out.writeString(getTitle());
|
||||
}
|
||||
|
||||
private Marker marker;
|
||||
|
||||
public MarkerOptions() {
|
||||
marker = new Marker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*/
|
||||
public Marker getMarker() {
|
||||
return marker;
|
||||
}
|
||||
|
||||
public LatLng getPosition() {
|
||||
return marker.getPosition();
|
||||
}
|
||||
|
||||
public String getSnippet() {
|
||||
return marker.getSnippet();
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return marker.getTitle();
|
||||
}
|
||||
|
||||
public Sprite getIcon() {
|
||||
return marker.getIcon();
|
||||
}
|
||||
|
||||
public MarkerOptions position(LatLng position) {
|
||||
marker.setPosition(position);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MarkerOptions snippet(String snippet) {
|
||||
marker.setSnippet(snippet);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MarkerOptions icon(@Nullable Sprite icon) {
|
||||
marker.setIcon(icon);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MarkerOptions title(String title) {
|
||||
marker.setTitle(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
MarkerOptions marker = (MarkerOptions) o;
|
||||
|
||||
if (getPosition() != null ? !getPosition().equals(marker.getPosition()) : marker.getPosition() != null)
|
||||
return false;
|
||||
if (getSnippet() != null ? !getSnippet().equals(marker.getSnippet()) : marker.getSnippet() != null)
|
||||
return false;
|
||||
if (getIcon() != null ? !getIcon().equals(marker.getIcon()) : marker.getIcon() != null)
|
||||
return false;
|
||||
return !(getTitle() != null ? !getTitle().equals(marker.getTitle()) : marker.getTitle() != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + (getPosition() != null ? getPosition().hashCode() : 0);
|
||||
result = 31 * result + (getSnippet() != null ? getSnippet().hashCode() : 0);
|
||||
result = 31 * result + (getIcon() != null ? getIcon().hashCode() : 0);
|
||||
result = 31 * result + (getTitle() != null ? getTitle().hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class MultiPoint extends Annotation {
|
||||
|
||||
private List<LatLng> points;
|
||||
private float alpha = 1.0f;
|
||||
|
||||
protected MultiPoint() {
|
||||
super();
|
||||
points = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the points.
|
||||
*
|
||||
* @return points - as a copy
|
||||
*/
|
||||
public List<LatLng> getPoints() {
|
||||
return new ArrayList<>(points);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the points of this polyline. This method will take a copy
|
||||
* of the points, so further mutations to points will have no effect
|
||||
* on this polyline.
|
||||
*
|
||||
* @param points the points of the polyline
|
||||
*/
|
||||
void setPoints(List<LatLng> points) {
|
||||
this.points = new ArrayList<>(points);
|
||||
}
|
||||
|
||||
void addPoint(LatLng point) {
|
||||
points.add(point);
|
||||
}
|
||||
|
||||
public float getAlpha() {
|
||||
return alpha;
|
||||
}
|
||||
|
||||
void setAlpha(float alpha) {
|
||||
this.alpha = alpha;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Polygon is a geometry annotation that's a closed loop of coordinates.
|
||||
*/
|
||||
public final class Polygon extends MultiPoint {
|
||||
|
||||
private int fillColor = Color.BLACK; // default fillColor is black
|
||||
private int strokeColor = Color.BLACK; // default strokeColor is black
|
||||
|
||||
Polygon() {
|
||||
super();
|
||||
}
|
||||
|
||||
public int getFillColor() {
|
||||
return fillColor;
|
||||
}
|
||||
|
||||
public int getStrokeColor() {
|
||||
return strokeColor;
|
||||
}
|
||||
|
||||
void setFillColor(int color) {
|
||||
fillColor = color;
|
||||
}
|
||||
|
||||
void setStrokeColor(int color) {
|
||||
strokeColor = color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class PolygonOptions implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<PolygonOptions> CREATOR
|
||||
= new Parcelable.Creator<PolygonOptions>() {
|
||||
public PolygonOptions createFromParcel(Parcel in) {
|
||||
return new PolygonOptions(in);
|
||||
}
|
||||
|
||||
public PolygonOptions[] newArray(int size) {
|
||||
return new PolygonOptions[size];
|
||||
}
|
||||
};
|
||||
|
||||
private PolygonOptions(Parcel in) {
|
||||
polygon = new Polygon();
|
||||
ArrayList<LatLng> pointsList = new ArrayList<>();
|
||||
in.readList(pointsList, LatLng.class.getClassLoader());
|
||||
addAll(pointsList);
|
||||
alpha(in.readFloat());
|
||||
fillColor(in.readInt());
|
||||
strokeColor(in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeList(getPoints());
|
||||
out.writeFloat(getAlpha());
|
||||
out.writeInt(getFillColor());
|
||||
out.writeInt(getStrokeColor());
|
||||
}
|
||||
|
||||
private Polygon polygon;
|
||||
|
||||
public PolygonOptions() {
|
||||
polygon = new Polygon();
|
||||
}
|
||||
|
||||
public PolygonOptions add(LatLng point) {
|
||||
polygon.addPoint(point);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PolygonOptions add(LatLng... points) {
|
||||
for (LatLng point : points) {
|
||||
add(point);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public PolygonOptions addAll(Iterable<LatLng> points) {
|
||||
for (LatLng point : points) {
|
||||
add(point);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public PolygonOptions alpha(float alpha) {
|
||||
polygon.setAlpha(alpha);
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getAlpha() {
|
||||
return polygon.getAlpha();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the polygon.
|
||||
*
|
||||
* @param color - the color in ARGB format
|
||||
* @return PolygonOptions - the options object
|
||||
*/
|
||||
public PolygonOptions fillColor(int color) {
|
||||
polygon.setFillColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getFillColor() {
|
||||
return polygon.getFillColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*/
|
||||
public Polygon getPolygon() {
|
||||
return polygon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the stroke of the polygon.
|
||||
*
|
||||
* @param color - the color in ARGB format
|
||||
* @return PolygonOptions - the options object
|
||||
*/
|
||||
public PolygonOptions strokeColor(int color) {
|
||||
polygon.setStrokeColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getStrokeColor() {
|
||||
return polygon.getStrokeColor();
|
||||
}
|
||||
|
||||
public List<LatLng> getPoints() {
|
||||
// the getter gives us a copy, which is the safe thing to do...
|
||||
return polygon.getPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
PolygonOptions polygon = (PolygonOptions) o;
|
||||
|
||||
if (Float.compare(polygon.getAlpha(), getAlpha()) != 0) return false;
|
||||
if (getFillColor() != polygon.getFillColor()) return false;
|
||||
if (getStrokeColor() != polygon.getStrokeColor()) return false;
|
||||
return !(getPoints() != null ? !getPoints().equals(polygon.getPoints()) : polygon.getPoints() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + (getAlpha() != +0.0f ? Float.floatToIntBits(getAlpha()) : 0);
|
||||
result = 31 * result + getFillColor();
|
||||
result = 31 * result + getStrokeColor();
|
||||
result = 31 * result + (getPoints() != null ? getPoints().hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
/**
|
||||
* Polyline is a geometry feature with an unclosed list of coordinates drawn as a line
|
||||
*/
|
||||
public final class Polyline extends MultiPoint {
|
||||
|
||||
private int color = Color.BLACK; // default color is black
|
||||
private float width = 10; // As specified by Google API Docs (in pixels)
|
||||
|
||||
Polyline() {
|
||||
super();
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public float getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the polyline.
|
||||
*
|
||||
* @param color - the color in ARGB format
|
||||
*/
|
||||
void setColor(int color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the width of the polyline.
|
||||
*
|
||||
* @param width in pixels
|
||||
*/
|
||||
void setWidth(float width) {
|
||||
this.width = width;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class PolylineOptions implements Parcelable {
|
||||
|
||||
|
||||
public static final Parcelable.Creator<PolylineOptions> CREATOR
|
||||
= new Parcelable.Creator<PolylineOptions>() {
|
||||
public PolylineOptions createFromParcel(Parcel in) {
|
||||
return new PolylineOptions(in);
|
||||
}
|
||||
|
||||
public PolylineOptions[] newArray(int size) {
|
||||
return new PolylineOptions[size];
|
||||
}
|
||||
};
|
||||
|
||||
private PolylineOptions(Parcel in) {
|
||||
polyline = new Polyline();
|
||||
ArrayList<LatLng> pointsList = new ArrayList<>();
|
||||
in.readList(pointsList, LatLng.class.getClassLoader());
|
||||
addAll(pointsList);
|
||||
alpha(in.readFloat());
|
||||
color(in.readInt());
|
||||
width(in.readFloat());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeList(getPoints());
|
||||
out.writeFloat(getAlpha());
|
||||
out.writeInt(getColor());
|
||||
out.writeFloat(getWidth());
|
||||
}
|
||||
|
||||
private Polyline polyline;
|
||||
|
||||
public PolylineOptions() {
|
||||
polyline = new Polyline();
|
||||
}
|
||||
|
||||
public PolylineOptions add(LatLng point) {
|
||||
polyline.addPoint(point);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PolylineOptions add(LatLng... points) {
|
||||
for (LatLng point : points) {
|
||||
add(point);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public PolylineOptions addAll(Iterable<LatLng> points) {
|
||||
for (LatLng point : points) {
|
||||
add(point);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public PolylineOptions alpha(float alpha) {
|
||||
polyline.setAlpha(alpha);
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getAlpha() {
|
||||
return polyline.getAlpha();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the polyline.
|
||||
*
|
||||
* @param color - the color in ARGB format
|
||||
*/
|
||||
public PolylineOptions color(int color) {
|
||||
polyline.setColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return polyline.getColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not use this method. Used internally by the SDK.
|
||||
*/
|
||||
public Polyline getPolyline() {
|
||||
return polyline;
|
||||
}
|
||||
|
||||
public float getWidth() {
|
||||
return polyline.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the width of the polyline.
|
||||
*
|
||||
* @param width in pixels
|
||||
* @return a new PolylineOptions
|
||||
*/
|
||||
public PolylineOptions width(float width) {
|
||||
polyline.setWidth(width);
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<LatLng> getPoints() {
|
||||
// the getter gives us a copy, which is the safe thing to do...
|
||||
return polyline.getPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
PolylineOptions polyline = (PolylineOptions) o;
|
||||
|
||||
if (Float.compare(polyline.getAlpha(), getAlpha()) != 0) return false;
|
||||
if (getColor() != polyline.getColor()) return false;
|
||||
if (Float.compare(polyline.getWidth(), getWidth()) != 0) return false;
|
||||
return !(getPoints() != null ? !getPoints().equals(polyline.getPoints()) : polyline.getPoints() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + (getAlpha() != +0.0f ? Float.floatToIntBits(getAlpha()) : 0);
|
||||
result = 31 * result + getColor();
|
||||
result = 31 * result + (getWidth() != +0.0f ? Float.floatToIntBits(getWidth()) : 0);
|
||||
result = 31 * result + (getPoints() != null ? getPoints().hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
public final class Sprite {
|
||||
private Bitmap mBitmap;
|
||||
private String mId;
|
||||
|
||||
Sprite(String id, Bitmap bitmap) {
|
||||
mId = id;
|
||||
mBitmap = bitmap;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public Bitmap getBitmap() {
|
||||
return mBitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Sprite sprite = (Sprite) o;
|
||||
|
||||
if (!mBitmap.equals(sprite.mBitmap)) return false;
|
||||
return mId.equals(sprite.mId);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = mBitmap.hashCode();
|
||||
result = 31 * result + mId.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.exceptions.TooManySpritesException;
|
||||
import com.mapbox.mapboxsdk.views.MapView;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public final class SpriteFactory {
|
||||
|
||||
private static final String SPRITE_ID_PREFIX = "com.mapbox.sprites.sprite_";
|
||||
|
||||
private MapView mMapView;
|
||||
private Sprite mDefaultMarker;
|
||||
private BitmapFactory.Options mOptions;
|
||||
|
||||
private int mNextId = 0;
|
||||
|
||||
public SpriteFactory(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
DisplayMetrics realMetrics = null;
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
WindowManager wm = (WindowManager) mMapView.getContext().getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
realMetrics = new DisplayMetrics();
|
||||
wm.getDefaultDisplay().getRealMetrics(realMetrics);
|
||||
}
|
||||
wm.getDefaultDisplay().getMetrics(metrics);
|
||||
|
||||
mOptions = new BitmapFactory.Options();
|
||||
mOptions.inScaled = true;
|
||||
mOptions.inDensity = DisplayMetrics.DENSITY_DEFAULT;
|
||||
mOptions.inTargetDensity = metrics.densityDpi;
|
||||
if (realMetrics != null) {
|
||||
mOptions.inScreenDensity = realMetrics.densityDpi;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Sprite fromBitmap(Bitmap bitmap) {
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mNextId < 0) {
|
||||
throw new TooManySpritesException();
|
||||
}
|
||||
String id = SPRITE_ID_PREFIX + ++mNextId;
|
||||
|
||||
return new Sprite(id, bitmap);
|
||||
}
|
||||
|
||||
public Sprite fromDrawable(Drawable drawable) {
|
||||
int width = drawable.getIntrinsicWidth();
|
||||
int height = drawable.getIntrinsicHeight();
|
||||
|
||||
return fromDrawable(drawable, width, height);
|
||||
}
|
||||
|
||||
|
||||
public Sprite fromDrawable(Drawable drawable, int width, int height) {
|
||||
if ((width < 0) || (height < 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Rect temp = drawable.getBounds();
|
||||
Rect bounds = new Rect(0, 0, width, height);
|
||||
drawable.setBounds(bounds);
|
||||
drawable.draw(canvas);
|
||||
drawable.setBounds(temp);
|
||||
|
||||
return fromBitmap(bitmap);
|
||||
}
|
||||
|
||||
public Sprite fromResource(int resourceId) {
|
||||
Bitmap bitmap = BitmapFactory.decodeResource(mMapView.getResources(), resourceId);
|
||||
return fromBitmap(bitmap);
|
||||
}
|
||||
|
||||
public Sprite defaultMarker() {
|
||||
if (mDefaultMarker == null) {
|
||||
mDefaultMarker = fromResource(R.drawable.default_marker);
|
||||
}
|
||||
return mDefaultMarker;
|
||||
}
|
||||
|
||||
private Sprite fromInputStream(InputStream is) {
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(is, null, mOptions);
|
||||
return fromBitmap(bitmap);
|
||||
}
|
||||
|
||||
public Sprite fromAsset(String assetName) {
|
||||
InputStream is;
|
||||
try {
|
||||
is = mMapView.getContext().getAssets().open(assetName);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
return fromInputStream(is);
|
||||
}
|
||||
|
||||
public Sprite fromPath(String absolutePath) {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(absolutePath, mOptions);
|
||||
return fromBitmap(bitmap);
|
||||
}
|
||||
|
||||
public Sprite fromFile(String fileName) {
|
||||
FileInputStream is;
|
||||
try {
|
||||
is = mMapView.getContext().openFileInput(fileName);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
return fromInputStream(is);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This package contains classes to add and manage annotations and markers in your map.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.annotations;
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
|
||||
/**
|
||||
* GeoConstants exposes constants for doing locational calculations on Earth
|
||||
*/
|
||||
public class GeoConstants {
|
||||
|
||||
/**
|
||||
* The <a href='http://en.wikipedia.org/wiki/Earth_radius#Equatorial_radius'>equatorial radius</a>
|
||||
* value in meters
|
||||
*/
|
||||
public static final int RADIUS_EARTH_METERS = 6378137;
|
||||
|
||||
/**
|
||||
* The minimum latitude on Earth. This is the minimum latitude representable
|
||||
* by Mapbox GL's Mercator projection, because the projection distorts latitude
|
||||
* near the poles towards infinity.
|
||||
*/
|
||||
public static final double MIN_LATITUDE = -85.05112878;
|
||||
|
||||
/**
|
||||
* The maximum latitude on Earth. This is the maximum latitude representable
|
||||
* by Mapbox GL's Mercator projection, because the projection distorts latitude
|
||||
* near the poles towards infinity.
|
||||
*/
|
||||
public static final double MAX_LATITUDE = 85.05112878;
|
||||
|
||||
/**
|
||||
* The minimum longitude on Earth
|
||||
*/
|
||||
public static final double MIN_LONGITUDE = -180;
|
||||
|
||||
/**
|
||||
* The maximum longitude on Earth
|
||||
*/
|
||||
public static final double MAX_LONGITUDE = 180;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* MapboxConstants exposes Mapbox related constants
|
||||
*/
|
||||
public class MapboxConstants {
|
||||
|
||||
/**
|
||||
* Default Locale for data processing (ex: String.toLowerCase(MAPBOX_LOCALE, "foo"))
|
||||
*/
|
||||
public static final Locale MAPBOX_LOCALE = Locale.US;
|
||||
|
||||
/**
|
||||
* Key used to store access token in AndroidManifest.xml
|
||||
*/
|
||||
public static final String KEY_META_DATA_MANIFEST = "com.mapbox.AccessToken";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
|
||||
/**
|
||||
* MathConstants exposes math related constant values
|
||||
*/
|
||||
public class MathConstants {
|
||||
|
||||
/**
|
||||
* Constant used to convert degrees to radials
|
||||
*/
|
||||
public static final double DEG2RAD = (Math.PI / 180.0);
|
||||
|
||||
/**
|
||||
* Constant used to convert radials to degrees
|
||||
*/
|
||||
public static final double RAD2DEG = (180.0 / Math.PI);
|
||||
|
||||
/**
|
||||
* The number PI
|
||||
*/
|
||||
public static final double PI = Math.PI;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
|
||||
import android.support.annotation.IntDef;
|
||||
|
||||
import com.mapbox.mapboxsdk.views.MapView;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* MyBearingTracking exposes different types bearing tracking modes.
|
||||
*
|
||||
* @see MapView#setMyBearingTrackingMode(int)
|
||||
* @see com.mapbox.mapboxsdk.views.UserLocationView#setMyBearingTrackingMode(int)
|
||||
*/
|
||||
public class MyBearingTracking {
|
||||
|
||||
/**
|
||||
* Indicates the parameter accepts one of the values from {@link MyBearingTracking}.
|
||||
*/
|
||||
@IntDef({NONE, COMPASS, GPS, /**COMBINED**/})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Mode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Bearing tracking is disabled
|
||||
*/
|
||||
public static final int NONE = 0x00000000;
|
||||
|
||||
/**
|
||||
* Tracking the bearing of the user based on sensor data
|
||||
*/
|
||||
public static final int COMPASS = 0x00000004;
|
||||
|
||||
/**
|
||||
* Tracking the bearing of the user based on GPS data
|
||||
*/
|
||||
public static final int GPS = 0x00000008;
|
||||
|
||||
//public static final int COMBINED = 0x00000012;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
|
||||
import android.support.annotation.IntDef;
|
||||
|
||||
import com.mapbox.mapboxsdk.views.MapView;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* MyLocationTracking exposes different types of locational tracking modes.
|
||||
*
|
||||
* @see MapView#setMyLocationTrackingMode(int)
|
||||
* @see com.mapbox.mapboxsdk.views.UserLocationView#setMyLocationTrackingMode(int)
|
||||
*/
|
||||
public class MyLocationTracking {
|
||||
|
||||
/**
|
||||
* Indicates the parameter accepts one of the values from {@link MyLocationTracking}.
|
||||
*/
|
||||
@IntDef({TRACKING_NONE, TRACKING_FOLLOW})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Mode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Location tracking is disabled.
|
||||
*/
|
||||
public static final int TRACKING_NONE = 0x00000000;
|
||||
|
||||
/**
|
||||
* Tracking the location of the user, {@link MapView} will reposition to center of {@link com.mapbox.mapboxsdk.views.UserLocationView}
|
||||
*/
|
||||
public static final int TRACKING_FOLLOW = 0x00000004;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
|
||||
import android.support.annotation.StringDef;
|
||||
|
||||
import com.mapbox.mapboxsdk.views.MapView;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
/**
|
||||
* <p>
|
||||
* Style provides URLs to several professional styles designed by Mapbox.
|
||||
* </p>
|
||||
* These styles are all ready to go in your app. To load one, pass it into {@link MapView#setStyleUrl(String)}
|
||||
*
|
||||
* @see MapView#setStyleUrl(String)
|
||||
*/
|
||||
public class Style {
|
||||
|
||||
/**
|
||||
* Indicates the parameter accepts one of the values from {@link Style}.
|
||||
*/
|
||||
@StringDef({MAPBOX_STREETS, EMERALD, LIGHT, DARK, SATELLITE, SATELLITE_STREETS})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface StyleUrl {
|
||||
}
|
||||
|
||||
// IMPORTANT: If you change any of these you also need to edit them in strings.xml
|
||||
|
||||
/**
|
||||
* Mapbox Streets: A complete basemap, perfect for incorporating your own data.
|
||||
*/
|
||||
public static final String MAPBOX_STREETS = "mapbox://styles/mapbox/streets-v8";
|
||||
/**
|
||||
* Emerald: A versatile style, with emphasis on road networks and public transit.
|
||||
*/
|
||||
public static final String EMERALD = "mapbox://styles/mapbox/emerald-v8";
|
||||
/**
|
||||
* Light: Subtle light backdrop for data visualizations.
|
||||
*/
|
||||
public static final String LIGHT = "mapbox://styles/mapbox/light-v8";
|
||||
/**
|
||||
* Dark: Subtle dark backdrop for data visualizations.
|
||||
*/
|
||||
public static final String DARK = "mapbox://styles/mapbox/dark-v8";
|
||||
/**
|
||||
* Satellite: A beautiful global satellite and aerial imagery layer.
|
||||
*/
|
||||
public static final String SATELLITE = "mapbox://styles/mapbox/satellite-v8";
|
||||
|
||||
/**
|
||||
* Satellite Streets: Global satellite and aerial imagery with unobtrusive labels.
|
||||
*/
|
||||
public static final String SATELLITE_STREETS = "mapbox://styles/mapbox/satellite-hybrid-v8";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This package provides access to several map related constants and bundled styles.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.constants;
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
import android.os.Bundle;
|
||||
import com.mapbox.mapboxsdk.views.MapView;
|
||||
|
||||
/**
|
||||
* A {@code InvalidAccessTokenException} is thrown by {@link MapView} when there is either no access
|
||||
* token set before {@link MapView#onCreate(Bundle)} or an invalid access token is set in {@link MapView#setAccessToken(String)}
|
||||
*
|
||||
* @see MapView#onCreate(Bundle)
|
||||
* @see MapView#setAccessToken(String)
|
||||
*/
|
||||
public class InvalidAccessTokenException extends RuntimeException {
|
||||
|
||||
public InvalidAccessTokenException() {
|
||||
super("Using MapView requires setting a valid access token. See the INSTALL.md");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import com.mapbox.mapboxsdk.annotations.Marker;
|
||||
import com.mapbox.mapboxsdk.annotations.Sprite;
|
||||
import com.mapbox.mapboxsdk.views.MapView;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A {@code SpriteBitmapChangedException} is thrown by {@link MapView} when a {@link Marker} is added
|
||||
* that has a {@link Sprite} with a {@link Bitmap} that has been modified.
|
||||
* </p>
|
||||
* You cannot modify a {@code Sprite} after it has been added to the map in a {@code Marker}
|
||||
*
|
||||
* @see MapView
|
||||
* @see Sprite
|
||||
* @see Marker
|
||||
*/
|
||||
public class SpriteBitmapChangedException extends RuntimeException {
|
||||
|
||||
public SpriteBitmapChangedException() {
|
||||
super("The added Marker has a Sprite with a Bitmap that has been modified. You cannot modufy" +
|
||||
"a Sprite after it has been added in a Marker.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
|
||||
import com.mapbox.mapboxsdk.annotations.Sprite;
|
||||
import com.mapbox.mapboxsdk.annotations.SpriteFactory;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* A {@code TooManySpritesException} is thrown by {@link SpriteFactory} when it
|
||||
* cannot create a {@link Sprite} because there are already too many.
|
||||
* </p>
|
||||
* You should try to reuse Sprite objects whenever possible.
|
||||
*
|
||||
* @see SpriteFactory
|
||||
*/
|
||||
public class TooManySpritesException extends RuntimeException {
|
||||
|
||||
public TooManySpritesException() {
|
||||
super("Cannot create a Sprite because there are already too many. Try reusing Sprites.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This package contains exceptions thrown in this SDK.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.exceptions;
|
||||
@@ -0,0 +1,304 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A rectangular geographical area defined in latitude and longitude units.
|
||||
*/
|
||||
public final class BoundingBox implements Parcelable, Serializable {
|
||||
|
||||
private final double mLatNorth;
|
||||
private final double mLatSouth;
|
||||
private final double mLonEast;
|
||||
private final double mLonWest;
|
||||
|
||||
private final boolean mIsValid;
|
||||
|
||||
/**
|
||||
* Construct a new bounding box based on its corners, given in NESW
|
||||
* order.
|
||||
*
|
||||
* @param northLatitude Northern Latitude
|
||||
* @param eastLongitude Eastern Longitude
|
||||
* @param southLatitude Southern Latitude
|
||||
* @param westLongitude Western Longitude
|
||||
*/
|
||||
public BoundingBox(final double northLatitude, final double eastLongitude, final double southLatitude, final double westLongitude) {
|
||||
this.mLatNorth = northLatitude;
|
||||
this.mLonEast = eastLongitude;
|
||||
this.mLatSouth = southLatitude;
|
||||
this.mLonWest = westLongitude;
|
||||
this.mIsValid = ((this.mLonWest < this.mLonEast) && (this.mLatNorth > this.mLatSouth));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new bounding box based on its corners, given in NESW order.
|
||||
*
|
||||
* @param northEast Coordinate
|
||||
* @param southWest Coordinate
|
||||
*/
|
||||
public BoundingBox(final LatLng northEast, final LatLng southWest) {
|
||||
this(northEast.getLatitude(), northEast.getLongitude(), southWest.getLatitude(), southWest.getLongitude());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bounding box from another bounding box
|
||||
*
|
||||
* @param other the other bounding box
|
||||
*/
|
||||
public BoundingBox(final BoundingBox other) {
|
||||
this.mLatNorth = other.getLatNorth();
|
||||
this.mLonEast = other.getLonEast();
|
||||
this.mLatSouth = other.getLatSouth();
|
||||
this.mLonWest = other.getLonWest();
|
||||
this.mIsValid = other.isValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BoundingBox with no size centered at 0, 0, also known as null island
|
||||
*/
|
||||
public BoundingBox() {
|
||||
this(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the centerpoint of this bounding box by simple interpolation and returns
|
||||
* it as a point. This is a non-geodesic calculation which is not the geographic center.
|
||||
*
|
||||
* @return LatLng center of this BoundingBox
|
||||
*/
|
||||
public LatLng getCenter() {
|
||||
return new LatLng((this.mLatNorth + this.mLatSouth) / 2,
|
||||
(this.mLonEast + this.mLonWest) / 2);
|
||||
}
|
||||
|
||||
public double getLatNorth() {
|
||||
return this.mLatNorth;
|
||||
}
|
||||
|
||||
public double getLatSouth() {
|
||||
return this.mLatSouth;
|
||||
}
|
||||
|
||||
public double getLonEast() {
|
||||
return this.mLonEast;
|
||||
}
|
||||
|
||||
public double getLonWest() {
|
||||
return this.mLonWest;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return this.mIsValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the area spanned by this bounding box
|
||||
*
|
||||
* @return CoordinateSpan area
|
||||
*/
|
||||
public CoordinateSpan getSpan() {
|
||||
return new CoordinateSpan(getLatitudeSpan(), getLongitudeSpan());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the absolute distance, in degrees, between the north and
|
||||
* south boundaries of this bounding box
|
||||
*
|
||||
* @return Span distance
|
||||
*/
|
||||
public double getLatitudeSpan() {
|
||||
return Math.abs(this.mLatNorth - this.mLatSouth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the absolute distance, in degrees, between the west and
|
||||
* east boundaries of this bounding box
|
||||
*
|
||||
* @return Span distance
|
||||
*/
|
||||
public double getLongitudeSpan() {
|
||||
return Math.abs(this.mLonEast - this.mLonWest);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate if bounding box is empty, determined if absolute distance is
|
||||
*
|
||||
* @return boolean indicating if span is empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return getLongitudeSpan() == 0.0 || getLatitudeSpan() == 0.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "N:" + this.mLatNorth + "; E:" + this.mLonEast + "; S:" + this.mLatSouth + "; W:" + this.mLonWest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a bounding box that contains all of a list of LatLng
|
||||
* objects. Empty lists will yield invalid bounding boxes.
|
||||
*
|
||||
* @param latLngs List of LatLng objects
|
||||
* @return BoundingBox
|
||||
*/
|
||||
public static BoundingBox fromLatLngs(final List<? extends ILatLng> latLngs) {
|
||||
double minLat = 90,
|
||||
minLon = 180,
|
||||
maxLat = -90,
|
||||
maxLon = -180;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return new BoundingBox(maxLat, maxLon, minLat, minLon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this bounding box matches another one via coordinates.
|
||||
*
|
||||
* @param o another object
|
||||
* @return a boolean indicating whether the bounding boxes are equal
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o instanceof BoundingBox) {
|
||||
BoundingBox other = (BoundingBox) o;
|
||||
return mLatNorth == other.getLatNorth()
|
||||
&& mLatSouth == other.getLatSouth()
|
||||
&& mLonEast == other.getLonEast()
|
||||
&& mLonWest == other.getLonWest();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this bounding box contains a point and the point
|
||||
* does not touch its boundary.
|
||||
*
|
||||
* @param pGeoPoint the point which may be contained
|
||||
* @return true, if the point is contained within the box.
|
||||
*/
|
||||
public boolean contains(final ILatLng pGeoPoint) {
|
||||
final double latitude = pGeoPoint.getLatitude();
|
||||
final double longitude = pGeoPoint.getLongitude();
|
||||
return ((latitude < this.mLatNorth)
|
||||
&& (latitude > this.mLatSouth))
|
||||
&& ((longitude < this.mLonEast)
|
||||
&& (longitude > this.mLonWest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new BoundingBox that stretches to contain both this and another BoundingBox.
|
||||
*
|
||||
* @param box BoundingBox to add
|
||||
* @return BoundingBox
|
||||
*/
|
||||
public BoundingBox union(BoundingBox box) {
|
||||
return union(box.getLatNorth(), box.getLonEast(), box.getLatSouth(), box.getLonWest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new BoundingBox that stretches to include another bounding box,
|
||||
* given by corner points.
|
||||
*
|
||||
* @param lonNorth Northern Longitude
|
||||
* @param latEast Eastern Latitude
|
||||
* @param lonSouth Southern Longitude
|
||||
* @param latWest Western Longitude
|
||||
* @return BoundingBox
|
||||
*/
|
||||
public BoundingBox union(final double lonNorth, final double latEast, final double lonSouth, final double latWest) {
|
||||
return new BoundingBox((this.mLatNorth < lonNorth) ? lonNorth : this.mLatNorth,
|
||||
(this.mLonEast < latEast) ? latEast : this.mLonEast,
|
||||
(this.mLatSouth > lonSouth) ? lonSouth : this.mLatSouth,
|
||||
(this.mLonWest > latWest) ? latWest : this.mLonWest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new BoundingBox that is the intersection of this with another box
|
||||
*
|
||||
* @param box BoundingBox to intersect with
|
||||
* @return BoundingBox
|
||||
*/
|
||||
public BoundingBox intersect(BoundingBox box) {
|
||||
double minLatWest = Math.max(getLonWest(), box.getLonWest());
|
||||
double maxLatEast = Math.min(getLonEast(), box.getLonEast());
|
||||
if (maxLatEast > minLatWest) {
|
||||
double minLonSouth = Math.max(getLatSouth(), box.getLatSouth());
|
||||
double maxLonNorth = Math.min(getLatNorth(), box.getLatNorth());
|
||||
if (maxLonNorth > minLonSouth) {
|
||||
return new BoundingBox(maxLonNorth, maxLatEast, minLonSouth, minLatWest);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new BoundingBox that is the intersection of this with another box
|
||||
*
|
||||
* @param northLongitude Northern Longitude
|
||||
* @param eastLatitude Eastern Latitude
|
||||
* @param southLongitude Southern Longitude
|
||||
* @param westLatitude Western Latitude
|
||||
* @return BoundingBox
|
||||
*/
|
||||
public BoundingBox intersect(double northLongitude, double eastLatitude, double southLongitude, double westLatitude) {
|
||||
return intersect(new BoundingBox(northLongitude, eastLatitude, southLongitude, westLatitude));
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<BoundingBox> CREATOR =
|
||||
new Parcelable.Creator<BoundingBox>() {
|
||||
@Override
|
||||
public BoundingBox createFromParcel(final Parcel in) {
|
||||
return readFromParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundingBox[] newArray(final int size) {
|
||||
return new BoundingBox[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) ((mLatNorth + 90)
|
||||
+ ((mLatSouth + 90) * 1000)
|
||||
+ ((mLonEast + 180) * 1000000)
|
||||
+ ((mLonEast + 180) * 1000000000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel out, final int arg1) {
|
||||
out.writeDouble(this.mLatNorth);
|
||||
out.writeDouble(this.mLonEast);
|
||||
out.writeDouble(this.mLatSouth);
|
||||
out.writeDouble(this.mLonWest);
|
||||
}
|
||||
|
||||
private static BoundingBox readFromParcel(final Parcel in) {
|
||||
final double lonNorth = in.readDouble();
|
||||
final double latEast = in.readDouble();
|
||||
final double lonSouth = in.readDouble();
|
||||
final double latWest = in.readDouble();
|
||||
return new BoundingBox(lonNorth, latEast, lonSouth, latWest);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
/**
|
||||
* Implementation of iOS MGLCoordinateBounds
|
||||
*/
|
||||
public class CoordinateBounds {
|
||||
|
||||
private LatLng southWest;
|
||||
private LatLng northEast;
|
||||
|
||||
public CoordinateBounds(LatLng southWest, LatLng northEast) {
|
||||
this.southWest = southWest;
|
||||
this.northEast = northEast;
|
||||
}
|
||||
|
||||
public LatLng getSouthWest() {
|
||||
return southWest;
|
||||
}
|
||||
|
||||
public void setSouthWest(LatLng southWest) {
|
||||
this.southWest = southWest;
|
||||
}
|
||||
|
||||
public LatLng getNorthEast() {
|
||||
return northEast;
|
||||
}
|
||||
|
||||
public void setNorthEast(LatLng northEast) {
|
||||
this.northEast = northEast;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = southWest.hashCode();
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
temp = northEast.hashCode();
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o instanceof CoordinateBounds) {
|
||||
CoordinateBounds other = (CoordinateBounds) o;
|
||||
return getNorthEast().equals(other.getNorthEast())
|
||||
&& getSouthWest().equals(other.getSouthWest());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CoordinateBounds [northEast[" + getNorthEast() + "], southWest[]" + getSouthWest() + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
/**
|
||||
* Implementation of iOS MKCoordinateRegion
|
||||
*/
|
||||
public class CoordinateRegion {
|
||||
private LatLng center;
|
||||
private CoordinateSpan span;
|
||||
|
||||
public CoordinateRegion(final LatLng center, final CoordinateSpan span) {
|
||||
this.center = center;
|
||||
this.span = span;
|
||||
}
|
||||
|
||||
public LatLng getCenter() {
|
||||
return center;
|
||||
}
|
||||
|
||||
public void setCenter(final LatLng center) {
|
||||
this.center = center;
|
||||
}
|
||||
|
||||
public CoordinateSpan getSpan() {
|
||||
return span;
|
||||
}
|
||||
|
||||
public void setSpan(final CoordinateSpan span) {
|
||||
this.span = span;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
/**
|
||||
* Implementation of iOS MKCoordinateSpan
|
||||
*/
|
||||
public class CoordinateSpan {
|
||||
|
||||
private double latitudeSpan;
|
||||
private double longitudeSpan;
|
||||
|
||||
public CoordinateSpan(final double latitudeSpan, final double longitudeSpan) {
|
||||
this.latitudeSpan = latitudeSpan;
|
||||
this.longitudeSpan = longitudeSpan;
|
||||
}
|
||||
|
||||
public double getLatitudeSpan() {
|
||||
return latitudeSpan;
|
||||
}
|
||||
|
||||
public void setLatitudeSpan(final double latitudeSpan) {
|
||||
this.latitudeSpan = latitudeSpan;
|
||||
}
|
||||
|
||||
public double getLongitudeSpan() {
|
||||
return longitudeSpan;
|
||||
}
|
||||
|
||||
public void setLongitudeSpan(final double longitudeSpan) {
|
||||
this.longitudeSpan = longitudeSpan;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o instanceof CoordinateSpan) {
|
||||
CoordinateSpan other = (CoordinateSpan) o;
|
||||
return longitudeSpan == other.getLongitudeSpan()
|
||||
&& latitudeSpan == other.getLatitudeSpan();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
/**
|
||||
* A Latitude, Longitude point.
|
||||
*/
|
||||
public interface ILatLng {
|
||||
double getLatitude();
|
||||
|
||||
double getLongitude();
|
||||
|
||||
double getAltitude();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
public interface IProjectedMeters {
|
||||
double getNorthing();
|
||||
|
||||
double getEasting();
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.mapbox.mapboxsdk.constants.GeoConstants;
|
||||
import com.mapbox.mapboxsdk.constants.MathConstants;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This class is container for a single latitude, longitude pair, with
|
||||
* optional altitude. Latitude and longitude are expressed as decimal degrees
|
||||
* in the WGS84 datum. By default, altitude is 0.0, or sea level.
|
||||
* </p>
|
||||
* <p>
|
||||
* Mapbox GL displays maps in the Mercator Projection and projects geographical
|
||||
* data automatically, so all data enters in the WGS84 datum.
|
||||
* </p>
|
||||
*/
|
||||
public class LatLng implements ILatLng, Parcelable, Serializable {
|
||||
|
||||
public static final Parcelable.Creator<LatLng> CREATOR = new Parcelable.Creator<LatLng>() {
|
||||
public LatLng createFromParcel(Parcel in) {
|
||||
return new LatLng(in);
|
||||
}
|
||||
|
||||
public LatLng[] newArray(int size) {
|
||||
return new LatLng[size];
|
||||
}
|
||||
};
|
||||
|
||||
private double latitude;
|
||||
private double longitude;
|
||||
private double altitude = 0.0;
|
||||
|
||||
/**
|
||||
* Construct a new latitude, longitude point at (0, 0)
|
||||
*/
|
||||
public LatLng() {
|
||||
this.latitude = 0.0;
|
||||
this.longitude = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new latitude, longitude point given float arguments
|
||||
* @param latitude Latitude in degrees
|
||||
* @param longitude Longitude in degrees
|
||||
*/
|
||||
public LatLng(double latitude, double longitude) {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new latitude, longitude, altitude point given float arguments
|
||||
* @param latitude Latitude in degrees
|
||||
* @param longitude Longitude in degress
|
||||
* @param altitude Altitude in meters
|
||||
*/
|
||||
public LatLng(double latitude, double longitude, double altitude) {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
this.altitude = altitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a Location into a LatLng point
|
||||
* @param location Android Location
|
||||
*/
|
||||
public LatLng(Location location) {
|
||||
this(location.getLatitude(), location.getLongitude(), location.getAltitude());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone an existing latitude longitude point
|
||||
* @param aLatLng LatLng
|
||||
*/
|
||||
public LatLng(LatLng aLatLng) {
|
||||
this.latitude = aLatLng.latitude;
|
||||
this.longitude = aLatLng.longitude;
|
||||
this.altitude = aLatLng.altitude;
|
||||
}
|
||||
|
||||
protected LatLng(Parcel in) {
|
||||
latitude = in.readDouble();
|
||||
longitude = in.readDouble();
|
||||
altitude = in.readDouble();
|
||||
}
|
||||
|
||||
public void setLatitude(double latitude) {
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public void setLongitude(double longitude) {
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
public void setAltitude(double altitude) {
|
||||
this.altitude = altitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAltitude() {
|
||||
return altitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LatLng latLng = (LatLng) o;
|
||||
|
||||
return Double.compare(latLng.altitude, altitude) == 0 && Double.compare(latLng.latitude, latitude) == 0 && Double.compare(latLng.longitude, longitude) == 0;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(latitude);
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(longitude);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(altitude);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LatLng [longitude=" + longitude + ", latitude=" + latitude + ", altitude=" + altitude + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeDouble(latitude);
|
||||
out.writeDouble(longitude);
|
||||
out.writeDouble(altitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate distance between two points
|
||||
* @param other Other LatLng to compare to
|
||||
* @return distance in meters
|
||||
*/
|
||||
public double distanceTo(LatLng other) {
|
||||
|
||||
final double a1 = MathConstants.DEG2RAD * this.latitude;
|
||||
final double a2 = MathConstants.DEG2RAD * this.longitude;
|
||||
final double b1 = MathConstants.DEG2RAD * other.getLatitude();
|
||||
final double b2 = MathConstants.DEG2RAD * other.getLongitude();
|
||||
|
||||
final double cosa1 = Math.cos(a1);
|
||||
final double cosb1 = Math.cos(b1);
|
||||
|
||||
final double t1 = cosa1 * Math.cos(a2) * cosb1 * Math.cos(b2);
|
||||
final double t2 = cosa1 * Math.sin(a2) * cosb1 * Math.sin(b2);
|
||||
final double t3 = Math.sin(a1) * Math.sin(b1);
|
||||
final double tt = Math.acos(t1 + t2 + t3);
|
||||
|
||||
return GeoConstants.RADIUS_EARTH_METERS * tt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class LatLngZoom extends LatLng implements Parcelable, Serializable {
|
||||
|
||||
public static final Parcelable.Creator<LatLngZoom> CREATOR = new Parcelable.Creator<LatLngZoom>() {
|
||||
public LatLngZoom createFromParcel(Parcel in) {
|
||||
return new LatLngZoom(in);
|
||||
}
|
||||
|
||||
public LatLngZoom[] newArray(int size) {
|
||||
return new LatLngZoom[size];
|
||||
}
|
||||
};
|
||||
|
||||
private double zoom;
|
||||
|
||||
public LatLngZoom() {
|
||||
super();
|
||||
this.zoom = 0.0;
|
||||
}
|
||||
|
||||
public LatLngZoom(double latitude, double longitude, double zoom) {
|
||||
super(latitude, longitude);
|
||||
this.zoom = zoom;
|
||||
}
|
||||
|
||||
public LatLngZoom(LatLng latLng, double zoom) {
|
||||
super(latLng.getLatitude(), latLng.getLongitude());
|
||||
this.zoom = zoom;
|
||||
}
|
||||
|
||||
private LatLngZoom(Parcel in) {
|
||||
super(in);
|
||||
zoom = in.readDouble();
|
||||
}
|
||||
|
||||
public double getZoom() {
|
||||
return zoom;
|
||||
}
|
||||
|
||||
public void setZoom(double zoom) {
|
||||
this.zoom = zoom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LatLngZoom that = (LatLngZoom) o;
|
||||
|
||||
return Double.compare(that.zoom, zoom) == 0;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(zoom);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LatLngZoom [latitude=" + super.getLatitude() + ", longitude=" + super.getLongitude() + ", altitude=" + super.getAltitude() + ", zoom=" + zoom + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeDouble(zoom);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* ProjectedMeters is an internal representation of longitude, latitude points that
|
||||
* have been projected into Mapbox GL's Mercator projection. Instead of decimal
|
||||
* degrees, it uses Mercator meters (which are notably not equivalent to SI meters)
|
||||
* except at the equator.
|
||||
*/
|
||||
public class ProjectedMeters implements IProjectedMeters, Parcelable, Serializable {
|
||||
|
||||
public static final Creator<ProjectedMeters> CREATOR = new Creator<ProjectedMeters>() {
|
||||
public ProjectedMeters createFromParcel(Parcel in) {
|
||||
return new ProjectedMeters(in);
|
||||
}
|
||||
|
||||
public ProjectedMeters[] newArray(int size) {
|
||||
return new ProjectedMeters[size];
|
||||
}
|
||||
};
|
||||
|
||||
private double northing;
|
||||
private double easting;
|
||||
|
||||
public ProjectedMeters(double northing, double easting) {
|
||||
this.northing = northing;
|
||||
this.easting = easting;
|
||||
}
|
||||
|
||||
public ProjectedMeters(ProjectedMeters aProjectedMeters) {
|
||||
this.northing = aProjectedMeters.northing;
|
||||
this.easting = aProjectedMeters.easting;
|
||||
}
|
||||
|
||||
protected ProjectedMeters(Parcel in) {
|
||||
northing = in.readDouble();
|
||||
easting = in.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNorthing() {
|
||||
return northing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEasting() {
|
||||
return easting;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProjectedMeters projectedMeters = (ProjectedMeters) o;
|
||||
|
||||
return Double.compare(projectedMeters.easting, easting) == 0 && Double.compare(projectedMeters.northing, northing) == 0;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(easting);
|
||||
result = (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(northing);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProjectedMeters [northing=" + northing + ", easting=" + easting + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeDouble(northing);
|
||||
out.writeDouble(easting);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* This package contains classes that deal with geometry and map coordinates. Many SDK functions
|
||||
* accept or return these classes.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.geometry;
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
import com.squareup.okhttp.Call;
|
||||
import com.squareup.okhttp.Callback;
|
||||
import com.squareup.okhttp.Interceptor;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
class HTTPContext {
|
||||
|
||||
private static final int CONNECTION_ERROR = 0;
|
||||
private static final int TEMPORARY_ERROR = 1;
|
||||
private static final int PERMANENT_ERROR = 2;
|
||||
private static final int CANCELED_ERROR = 3;
|
||||
|
||||
private static HTTPContext mInstance = null;
|
||||
|
||||
private OkHttpClient mClient;
|
||||
|
||||
private HTTPContext() {
|
||||
super();
|
||||
mClient = new OkHttpClient();
|
||||
//mClient.interceptors().add(new LoggingInterceptor());
|
||||
}
|
||||
|
||||
public static HTTPContext getInstance() {
|
||||
if (mInstance == null) {
|
||||
mInstance = new HTTPContext();
|
||||
}
|
||||
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public HTTPRequest createRequest(long nativePtr, String resourceUrl, String userAgent, String etag, String modified) {
|
||||
return new HTTPRequest(nativePtr, resourceUrl, userAgent, etag, modified);
|
||||
}
|
||||
|
||||
public class HTTPRequest implements Callback {
|
||||
private final String LOG_TAG = HTTPRequest.class.getName();
|
||||
|
||||
private long mNativePtr = 0;
|
||||
|
||||
private Call mCall;
|
||||
private Request mRequest;
|
||||
|
||||
private native void nativeOnFailure(long nativePtr, int type, String message);
|
||||
private native void nativeOnResponse(long nativePtr, int code, String message, String etag, String modified, String cacheControl, String expires, byte[] body);
|
||||
|
||||
private HTTPRequest(long nativePtr, String resourceUrl, String userAgent, String etag, String modified) {
|
||||
mNativePtr = nativePtr;
|
||||
Request.Builder builder = new Request.Builder().url(resourceUrl).tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE)).addHeader("User-Agent", userAgent);
|
||||
if (etag.length() > 0) {
|
||||
builder = builder.addHeader("If-None-Match", etag);
|
||||
} else if (modified.length() > 0) {
|
||||
builder = builder.addHeader("If-Modified-Since", modified);
|
||||
}
|
||||
mRequest = builder.build();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
mCall = HTTPContext.getInstance().mClient.newCall(mRequest);
|
||||
mCall.enqueue(this);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
mCall.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Request request, IOException e) {
|
||||
Log.d(LOG_TAG, "onFailure: " + e.getMessage());
|
||||
|
||||
int type = PERMANENT_ERROR;
|
||||
if ((e instanceof UnknownHostException) || (e instanceof SocketException) || (e instanceof ProtocolException) || (e instanceof SSLException)) {
|
||||
type = CONNECTION_ERROR;
|
||||
} else if ((e instanceof InterruptedIOException)) {
|
||||
type = TEMPORARY_ERROR;
|
||||
} else if (mCall.isCanceled()) {
|
||||
type = CANCELED_ERROR;
|
||||
}
|
||||
|
||||
nativeOnFailure(mNativePtr, type, e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Response response) throws IOException {
|
||||
Log.d(LOG_TAG, "onResponse");
|
||||
|
||||
byte[] body;
|
||||
try {
|
||||
body = response.body().bytes();
|
||||
} catch (IOException e) {
|
||||
onFailure(null, e);
|
||||
//throw e;
|
||||
return;
|
||||
} finally {
|
||||
response.body().close();
|
||||
}
|
||||
|
||||
nativeOnResponse(mNativePtr, response.code(), response.message(), response.header("ETag"), response.header("Last-Modified"), response.header("Cache-Control"), response.header("Expires"), body);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Application interceptor that logs the outgoing request and the incoming response.
|
||||
* Based on https://github.com/square/okhttp/wiki/Interceptors
|
||||
*/
|
||||
|
||||
class LoggingInterceptor implements Interceptor {
|
||||
|
||||
private final static String LOG_TAG = "LoggingInterceptor";
|
||||
|
||||
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
|
||||
Request request = chain.request();
|
||||
|
||||
long t1 = System.nanoTime();
|
||||
Log.i(LOG_TAG, String.format("Sending request %s on %s%n%s",
|
||||
request.url(), chain.connection(), request.headers()));
|
||||
|
||||
Response response = chain.proceed(request);
|
||||
|
||||
long t2 = System.nanoTime();
|
||||
Log.i(LOG_TAG, String.format("Received response for %s in %.1fms%n%s",
|
||||
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Do not use this package. Used internally by the SDK.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.mapbox.mapboxsdk.layers;
|
||||
|
||||
public class CustomLayer {
|
||||
public CustomLayer(String id,
|
||||
long context,
|
||||
long initializeFunction,
|
||||
long renderFunction,
|
||||
long deinitializeFunction) {
|
||||
this.mID = id;
|
||||
this.mContext = context;
|
||||
this.mInitializeFunction = initializeFunction;
|
||||
this.mRenderFunction = renderFunction;
|
||||
this.mDeinitializeFunction = deinitializeFunction;
|
||||
}
|
||||
|
||||
public String mID;
|
||||
public long mContext;
|
||||
public long mInitializeFunction;
|
||||
public long mRenderFunction;
|
||||
public long mDeinitializeFunction;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This package contains classes to add and manage layers in your map.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.layers;
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
public interface LocationListener {
|
||||
|
||||
/**
|
||||
* Callback method for receiving location updates from LocationServices.
|
||||
* @param location The new Location data
|
||||
*/
|
||||
public void onLocationChanged(Location location);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.support.annotation.NonNull;
|
||||
import com.mapzen.android.lost.api.LocationRequest;
|
||||
import com.mapzen.android.lost.api.LostApiClient;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LocationServices implements com.mapzen.android.lost.api.LocationListener {
|
||||
|
||||
private static LocationServices instance = null;
|
||||
|
||||
private LostApiClient mLocationClient;
|
||||
private LocationRequest mLocationRequest;
|
||||
|
||||
private Location lastLocation = null;
|
||||
|
||||
private List<LocationListener> locationListeners = null;
|
||||
|
||||
private boolean isGPSEnabled = false;
|
||||
|
||||
/**
|
||||
* Private constructor for singleton LocationServices
|
||||
*/
|
||||
private LocationServices(Context context) {
|
||||
super();
|
||||
// Setup location services
|
||||
mLocationClient = new LostApiClient.Builder(context).build();
|
||||
locationListeners = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary (singleton) access method for LocationServices
|
||||
* @param context Context
|
||||
* @return LocationServices
|
||||
*/
|
||||
public static LocationServices getLocationServices(@NonNull final Context context) {
|
||||
if (instance == null) {
|
||||
if (context == null) {
|
||||
throw new NullPointerException("Context required for accessing LocationServices");
|
||||
}
|
||||
instance = new LocationServices(context.getApplicationContext());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enabled / Disable GPS focused location tracking
|
||||
*
|
||||
* @param enableGPS true if GPS is to be enabled, false if GPS is to be disabled
|
||||
*/
|
||||
public void toggleGPS(boolean enableGPS) {
|
||||
|
||||
if (enableGPS) {
|
||||
|
||||
if (mLocationClient.isConnected()) {
|
||||
// Disconnect first to ensure that the new requests are GPS
|
||||
com.mapzen.android.lost.api.LocationServices.FusedLocationApi.removeLocationUpdates(this);
|
||||
mLocationClient.disconnect();
|
||||
}
|
||||
|
||||
// Setup Fresh
|
||||
mLocationClient.connect();
|
||||
Location lastLocation = com.mapzen.android.lost.api.LocationServices.FusedLocationApi.getLastLocation();
|
||||
if (lastLocation != null) {
|
||||
this.lastLocation = lastLocation;
|
||||
}
|
||||
|
||||
// LocationRequest Tuned for GPS
|
||||
mLocationRequest = LocationRequest.create()
|
||||
.setFastestInterval(1000)
|
||||
.setSmallestDisplacement(3.0f)
|
||||
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
|
||||
|
||||
com.mapzen.android.lost.api.LocationServices.FusedLocationApi.requestLocationUpdates(mLocationRequest, this);
|
||||
|
||||
} else {
|
||||
|
||||
// Disconnect
|
||||
if (mLocationClient.isConnected()) {
|
||||
// Disconnect first to ensure that the new requests are GPS
|
||||
com.mapzen.android.lost.api.LocationServices.FusedLocationApi.removeLocationUpdates(this);
|
||||
mLocationClient.disconnect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
isGPSEnabled = enableGPS;
|
||||
}
|
||||
|
||||
public boolean isGPSEnabled() {
|
||||
return isGPSEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
this.lastLocation = location;
|
||||
|
||||
// Update Listeners
|
||||
for (LocationListener listener : this.locationListeners) {
|
||||
listener.onLocationChanged(location);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Last known location
|
||||
* @return Last known location data
|
||||
*/
|
||||
public Location getLastLocation() {
|
||||
return lastLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a LocationListener to receive location updates
|
||||
* @param locationListener LocationListener
|
||||
*/
|
||||
public void addLocationListener(@NonNull LocationListener locationListener) {
|
||||
this.locationListeners.add(locationListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a LocationListener to stop receiving location updates
|
||||
* @param locationListener LocationListener to remove
|
||||
* @return True if LocationListener was found and removed, False if it was not
|
||||
*/
|
||||
public boolean removeLocationListener(@NonNull LocationListener locationListener) {
|
||||
return this.locationListeners.remove(locationListener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* This package contains the {@link com.mapbox.mapboxsdk.MapFragment} class. {@code MapFragment}
|
||||
* provides a quick and easy way to add a map to your app.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk;
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.mapbox.mapboxsdk.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
|
||||
/**
|
||||
* {@code ApiAccess} provides a method to load the Mapbox access token.
|
||||
*/
|
||||
public final class ApiAccess {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the Mapbox access token set in the app resources.
|
||||
* </p>
|
||||
* It will first search the application manifest for a {@link MapboxConstants#KEY_META_DATA_MANIFEST}
|
||||
* meta-data value. If not found it will then attempt to load the access token from the
|
||||
* {@code res/raw/token.txt} development file.
|
||||
*
|
||||
* @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}.
|
||||
* @return The Mapbox access token or null if not found.
|
||||
* @see MapboxConstants#KEY_META_DATA_MANIFEST
|
||||
*/
|
||||
@Nullable
|
||||
public static String getToken(@NonNull Context context) {
|
||||
try {
|
||||
// read out AndroidManifest
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
|
||||
String token = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_MANIFEST);
|
||||
if (token == null || token.isEmpty()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return token;
|
||||
} catch (Exception e) {
|
||||
// use fallback on string resource, used for development
|
||||
int tokenResId = context.getResources().getIdentifier("access_token", "string", context.getPackageName());
|
||||
return tokenResId != 0 ? context.getString(tokenResId) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* This package contains the {@link com.mapbox.mapboxsdk.utils.ApiAccess} class. {@code ApiAccess}
|
||||
* provides a methods to load a Mapbox access token.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.utils;
|
||||
@@ -0,0 +1,156 @@
|
||||
package com.mapbox.mapboxsdk.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* CompassView is a UI element overlaid on a map that shows the map's bearing
|
||||
* when it isn't true north (0.0). Tapping the compass resets the bearing to true
|
||||
* north and hides the compass.
|
||||
*/
|
||||
final class CompassView extends ImageView {
|
||||
|
||||
private Timer mNorthTimer;
|
||||
private double mDirection = 0.0f;
|
||||
private ViewPropertyAnimatorCompat mFadeAnimator;
|
||||
|
||||
public CompassView(Context context) {
|
||||
super(context);
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
public CompassView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
public CompassView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
private void initialize(Context context) {
|
||||
|
||||
// View configuration
|
||||
setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.compass));
|
||||
setContentDescription(getResources().getString(R.string.compassContentDescription));
|
||||
setEnabled(false);
|
||||
|
||||
// Layout params
|
||||
float mScreenDensity = context.getResources().getDisplayMetrics().density;
|
||||
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams((int) (48 * mScreenDensity), (int) (48 * mScreenDensity));
|
||||
setLayoutParams(lp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
if (enabled) {
|
||||
if (mDirection != 0.0) {
|
||||
if (mNorthTimer != null){
|
||||
mNorthTimer.cancel();
|
||||
mNorthTimer = null;
|
||||
}
|
||||
if (mFadeAnimator != null) {
|
||||
mFadeAnimator.cancel();
|
||||
}
|
||||
mFadeAnimator = null;
|
||||
setAlpha(1.0f);
|
||||
setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
if (mNorthTimer != null){
|
||||
mNorthTimer.cancel();
|
||||
mNorthTimer = null;
|
||||
}
|
||||
if (mFadeAnimator != null) {
|
||||
mFadeAnimator.cancel();
|
||||
}
|
||||
mFadeAnimator = null;
|
||||
setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(double direction) {
|
||||
mDirection = direction;
|
||||
setRotation((float) direction);
|
||||
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction == 0.0) {
|
||||
if (getVisibility() == View.INVISIBLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mNorthTimer == null) {
|
||||
if (mFadeAnimator != null) {
|
||||
mFadeAnimator.cancel();
|
||||
}
|
||||
mFadeAnimator = null;
|
||||
|
||||
mNorthTimer = new Timer("CompassView North timer");
|
||||
mNorthTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setAlpha(1.0f);
|
||||
mFadeAnimator = ViewCompat.animate(CompassView.this).alpha(0.0f).setDuration(1000).withLayer();
|
||||
mFadeAnimator.setListener(new ViewPropertyAnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(View view) {
|
||||
setVisibility(View.INVISIBLE);
|
||||
mNorthTimer = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
if (mNorthTimer != null){
|
||||
mNorthTimer.cancel();
|
||||
mNorthTimer = null;
|
||||
}
|
||||
if (mFadeAnimator != null) {
|
||||
mFadeAnimator.cancel();
|
||||
}
|
||||
setAlpha(1.0f);
|
||||
setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CompassClickListener implements View.OnClickListener {
|
||||
|
||||
private WeakReference<MapView> mMapView;
|
||||
|
||||
public CompassClickListener(final MapView mapView) {
|
||||
mMapView = new WeakReference<>(mapView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final MapView mapView = mMapView.get();
|
||||
if (mapView != null) {
|
||||
mapView.resetNorth();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,639 @@
|
||||
package com.mapbox.mapboxsdk.views;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.RectF;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.mapbox.mapboxsdk.annotations.Marker;
|
||||
import com.mapbox.mapboxsdk.annotations.Polygon;
|
||||
import com.mapbox.mapboxsdk.annotations.Polyline;
|
||||
import com.mapbox.mapboxsdk.geometry.BoundingBox;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLngZoom;
|
||||
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
|
||||
import com.mapbox.mapboxsdk.layers.CustomLayer;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
// Class that wraps the native methods for convenience
|
||||
final class NativeMapView {
|
||||
|
||||
//
|
||||
// Static members
|
||||
//
|
||||
|
||||
//
|
||||
// Instance members
|
||||
//
|
||||
|
||||
boolean mDestroyed = false;
|
||||
|
||||
// Holds the pointer to JNI NativeMapView
|
||||
private long mNativeMapViewPtr = 0;
|
||||
|
||||
// Used for callbacks
|
||||
private WeakReference<MapView> mMapView;
|
||||
|
||||
//
|
||||
// Static methods
|
||||
//
|
||||
|
||||
static {
|
||||
System.loadLibrary("mapbox-gl");
|
||||
}
|
||||
|
||||
//
|
||||
// Constructors
|
||||
//
|
||||
|
||||
public NativeMapView(MapView mapView, String cachePath, String dataPath, String apkPath, float pixelRatio, int availableProcessors, long totalMemory) {
|
||||
if (availableProcessors < 0) {
|
||||
throw new IllegalArgumentException("availableProcessors cannot be negative.");
|
||||
}
|
||||
|
||||
if (totalMemory < 0) {
|
||||
throw new IllegalArgumentException("totalMemory cannot be negative.");
|
||||
}
|
||||
|
||||
mMapView = new WeakReference<>(mapView);
|
||||
|
||||
// Create the NativeMapView
|
||||
mNativeMapViewPtr = nativeCreate(cachePath, dataPath, apkPath, pixelRatio, availableProcessors, totalMemory);
|
||||
}
|
||||
|
||||
//
|
||||
// Methods
|
||||
//
|
||||
|
||||
public void destroy() {
|
||||
nativeDestroy(mNativeMapViewPtr);
|
||||
mNativeMapViewPtr = 0;
|
||||
mMapView = null;
|
||||
mDestroyed = true;
|
||||
}
|
||||
|
||||
public boolean wasDestroyed() {
|
||||
return mDestroyed;
|
||||
}
|
||||
|
||||
public void initializeDisplay() {
|
||||
nativeInitializeDisplay(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void terminateDisplay() {
|
||||
nativeTerminateDisplay(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void initializeContext() {
|
||||
nativeInitializeContext(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void terminateContext() {
|
||||
nativeTerminateContext(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void createSurface(Surface surface) {
|
||||
nativeCreateSurface(mNativeMapViewPtr, surface);
|
||||
}
|
||||
|
||||
public void destroySurface() {
|
||||
nativeDestroySurface(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
nativePause(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public boolean isPaused() {
|
||||
return nativeIsPaused(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
nativeResume(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
nativeUpdate(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void renderSync() {
|
||||
nativeRenderSync(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void resizeView(int width, int height) {
|
||||
if (width < 0) {
|
||||
throw new IllegalArgumentException("width cannot be negative.");
|
||||
}
|
||||
|
||||
if (height < 0) {
|
||||
throw new IllegalArgumentException("height cannot be negative.");
|
||||
}
|
||||
|
||||
if (width > 65535) {
|
||||
throw new IllegalArgumentException(
|
||||
"width cannot be greater than 65535.");
|
||||
}
|
||||
|
||||
if (height > 65535) {
|
||||
throw new IllegalArgumentException(
|
||||
"height cannot be greater than 65535.");
|
||||
}
|
||||
nativeViewResize(mNativeMapViewPtr, width, height);
|
||||
}
|
||||
|
||||
public void resizeFramebuffer(int fbWidth, int fbHeight) {
|
||||
if (fbWidth < 0) {
|
||||
throw new IllegalArgumentException("fbWidth cannot be negative.");
|
||||
}
|
||||
|
||||
if (fbHeight < 0) {
|
||||
throw new IllegalArgumentException("fbHeight cannot be negative.");
|
||||
}
|
||||
|
||||
if (fbWidth > 65535) {
|
||||
throw new IllegalArgumentException(
|
||||
"fbWidth cannot be greater than 65535.");
|
||||
}
|
||||
|
||||
if (fbHeight > 65535) {
|
||||
throw new IllegalArgumentException(
|
||||
"fbHeight cannot be greater than 65535.");
|
||||
}
|
||||
nativeFramebufferResize(mNativeMapViewPtr, fbWidth, fbHeight);
|
||||
}
|
||||
|
||||
public void addClass(String clazz) {
|
||||
nativeAddClass(mNativeMapViewPtr, clazz);
|
||||
}
|
||||
|
||||
public void removeClass(String clazz) {
|
||||
nativeRemoveClass(mNativeMapViewPtr, clazz);
|
||||
}
|
||||
|
||||
public boolean hasClass(String clazz) {
|
||||
return nativeHasClass(mNativeMapViewPtr, clazz);
|
||||
}
|
||||
|
||||
public void setClasses(List<String> classes) {
|
||||
nativeSetClasses(mNativeMapViewPtr, classes);
|
||||
}
|
||||
|
||||
public List<String> getClasses() {
|
||||
return nativeGetClasses(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setDefaultTransitionDuration() {
|
||||
setDefaultTransitionDuration(0);
|
||||
}
|
||||
|
||||
public long getDefaultTransitionDuration() {
|
||||
return nativeGetDefaultTransitionDuration(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setDefaultTransitionDuration(long milliseconds) {
|
||||
if (milliseconds < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"milliseconds cannot be negative.");
|
||||
}
|
||||
|
||||
nativeSetDefaultTransitionDuration(mNativeMapViewPtr,
|
||||
milliseconds);
|
||||
}
|
||||
|
||||
public void setStyleUrl(String url) {
|
||||
nativeSetStyleUrl(mNativeMapViewPtr, url);
|
||||
}
|
||||
|
||||
public void setStyleJson(String newStyleJson) {
|
||||
setStyleJson(newStyleJson, "");
|
||||
}
|
||||
|
||||
public void setStyleJson(String newStyleJson, String base) {
|
||||
nativeSetStyleJson(mNativeMapViewPtr, newStyleJson, base);
|
||||
}
|
||||
|
||||
public String getStyleJson() {
|
||||
return nativeGetStyleJson(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
nativeSetAccessToken(mNativeMapViewPtr, accessToken);
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return nativeGetAccessToken(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void cancelTransitions() {
|
||||
nativeCancelTransitions(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setGestureInProgress(boolean inProgress) {
|
||||
nativeSetGestureInProgress(mNativeMapViewPtr, inProgress);
|
||||
}
|
||||
|
||||
public void moveBy(double dx, double dy) {
|
||||
moveBy(dx, dy, 0);
|
||||
}
|
||||
|
||||
public void moveBy(double dx, double dy, long duration) {
|
||||
nativeMoveBy(mNativeMapViewPtr, dx, dy, duration);
|
||||
}
|
||||
|
||||
public void setLatLng(LatLng latLng) {
|
||||
setLatLng(latLng, 0);
|
||||
}
|
||||
|
||||
public void setLatLng(LatLng latLng, long duration) {
|
||||
nativeSetLatLng(mNativeMapViewPtr, latLng, duration);
|
||||
}
|
||||
|
||||
public LatLng getLatLng() {
|
||||
return nativeGetLatLng(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void resetPosition() {
|
||||
nativeResetPosition(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public double getPitch() {
|
||||
return nativeGetPitch(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setPitch(double pitch, long duration) {
|
||||
nativeSetPitch(mNativeMapViewPtr, pitch, duration);
|
||||
}
|
||||
|
||||
public void scaleBy(double ds) {
|
||||
scaleBy(ds, -1.0, -1.0);
|
||||
}
|
||||
|
||||
public void scaleBy(double ds, double cx, double cy) {
|
||||
scaleBy(ds, cx, cy, 0);
|
||||
}
|
||||
|
||||
public void scaleBy(double ds, double cx, double cy, long duration) {
|
||||
nativeScaleBy(mNativeMapViewPtr, ds, cx, cy, duration);
|
||||
}
|
||||
|
||||
public void setScale(double scale) {
|
||||
setScale(scale, -1.0, -1.0);
|
||||
}
|
||||
|
||||
public void setScale(double scale, double cx, double cy) {
|
||||
setScale(scale, cx, cy, 0);
|
||||
}
|
||||
|
||||
public void setScale(double scale, double cx, double cy, long duration) {
|
||||
nativeSetScale(mNativeMapViewPtr, scale, cx, cy, duration);
|
||||
}
|
||||
|
||||
public double getScale() {
|
||||
return nativeGetScale(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setZoom(double zoom) {
|
||||
setZoom(zoom, 0);
|
||||
}
|
||||
|
||||
public void setZoom(double zoom, long duration) {
|
||||
nativeSetZoom(mNativeMapViewPtr, zoom, duration);
|
||||
}
|
||||
|
||||
public double getZoom() {
|
||||
return nativeGetZoom(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setLatLngZoom(LatLngZoom latLngZoom) {
|
||||
setLatLngZoom(latLngZoom, 0);
|
||||
}
|
||||
|
||||
public void setLatLngZoom(LatLngZoom latLngZoom, long duration) {
|
||||
nativeSetLatLngZoom(mNativeMapViewPtr, latLngZoom, duration);
|
||||
}
|
||||
|
||||
public LatLngZoom getLatLngZoom() {
|
||||
return nativeGetLatLngZoom(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void resetZoom() {
|
||||
nativeResetZoom(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public double getMinZoom() {
|
||||
return nativeGetMinZoom(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public double getMaxZoom() {
|
||||
return nativeGetMaxZoom(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void rotateBy(double sx, double sy, double ex, double ey) {
|
||||
rotateBy(sx, sy, ex, ey, 0);
|
||||
}
|
||||
|
||||
public void rotateBy(double sx, double sy, double ex, double ey,
|
||||
long duration) {
|
||||
nativeRotateBy(mNativeMapViewPtr, sx, sy, ex, ey, duration);
|
||||
}
|
||||
|
||||
public void setBearing(double degrees) {
|
||||
setBearing(degrees, 0);
|
||||
}
|
||||
|
||||
public void setBearing(double degrees, long duration) {
|
||||
nativeSetBearing(mNativeMapViewPtr, degrees, duration);
|
||||
}
|
||||
|
||||
public void setBearing(double degrees, double cx, double cy) {
|
||||
nativeSetBearing(mNativeMapViewPtr, degrees, cx, cy);
|
||||
}
|
||||
|
||||
public double getBearing() {
|
||||
return nativeGetBearing(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void resetNorth() {
|
||||
nativeResetNorth(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public long addMarker(Marker marker) {
|
||||
return nativeAddMarker(mNativeMapViewPtr, marker);
|
||||
}
|
||||
|
||||
public long[] addMarkers(List<Marker> markers) {
|
||||
return nativeAddMarkers(mNativeMapViewPtr, markers);
|
||||
}
|
||||
|
||||
public long addPolyline(Polyline polyline) {
|
||||
return nativeAddPolyline(mNativeMapViewPtr, polyline);
|
||||
}
|
||||
|
||||
public long[] addPolylines(List<Polyline> polylines) {
|
||||
return nativeAddPolylines(mNativeMapViewPtr, polylines);
|
||||
}
|
||||
|
||||
public long addPolygon(Polygon polygon) {
|
||||
return nativeAddPolygon(mNativeMapViewPtr, polygon);
|
||||
}
|
||||
|
||||
public long[] addPolygons(List<Polygon> polygon) {
|
||||
return nativeAddPolygons(mNativeMapViewPtr, polygon);
|
||||
}
|
||||
|
||||
public void removeAnnotation(long id) {
|
||||
nativeRemoveAnnotation(mNativeMapViewPtr, id);
|
||||
}
|
||||
|
||||
public void removeAnnotations(long[] ids) {
|
||||
nativeRemoveAnnotations(mNativeMapViewPtr, ids);
|
||||
}
|
||||
|
||||
public long[] getAnnotationsInBounds(BoundingBox bbox) {
|
||||
return nativeGetAnnotationsInBounds(mNativeMapViewPtr, bbox);
|
||||
}
|
||||
|
||||
public void addAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels) {
|
||||
nativeAddAnnotationIcon(mNativeMapViewPtr, symbol, width, height, scale, pixels);
|
||||
}
|
||||
|
||||
public void setVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration) {
|
||||
nativeSetVisibleCoordinateBounds(mNativeMapViewPtr, coordinates, padding, direction, duration);
|
||||
}
|
||||
|
||||
public void onLowMemory() {
|
||||
nativeOnLowMemory(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
nativeSetDebug(mNativeMapViewPtr, debug);
|
||||
}
|
||||
|
||||
public void cycleDebugOptions() {
|
||||
nativeToggleDebug(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public boolean getDebug() {
|
||||
return nativeGetDebug(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public boolean isFullyLoaded() {
|
||||
return nativeIsFullyLoaded(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setReachability(boolean status) {
|
||||
nativeSetReachability(mNativeMapViewPtr, status);
|
||||
}
|
||||
|
||||
public double getMetersPerPixelAtLatitude(double lat, double zoom) {
|
||||
return nativeGetMetersPerPixelAtLatitude(mNativeMapViewPtr, lat, zoom);
|
||||
}
|
||||
|
||||
public ProjectedMeters projectedMetersForLatLng(LatLng latLng) {
|
||||
return nativeProjectedMetersForLatLng(mNativeMapViewPtr, latLng);
|
||||
}
|
||||
|
||||
public LatLng latLngForProjectedMeters(ProjectedMeters projectedMeters) {
|
||||
return nativeLatLngForProjectedMeters(mNativeMapViewPtr, projectedMeters);
|
||||
}
|
||||
|
||||
public PointF pixelForLatLng(LatLng latLng) {
|
||||
return nativePixelForLatLng(mNativeMapViewPtr, latLng);
|
||||
}
|
||||
|
||||
public LatLng latLngForPixel(PointF pixel) {
|
||||
return nativeLatLngForPixel(mNativeMapViewPtr, pixel);
|
||||
}
|
||||
|
||||
public double getTopOffsetPixelsForAnnotationSymbol(String symbolName) {
|
||||
return nativeGetTopOffsetPixelsForAnnotationSymbol(mNativeMapViewPtr, symbolName);
|
||||
}
|
||||
|
||||
public void addCustomLayer(CustomLayer customLayer, String before) {
|
||||
nativeAddCustomLayer(mNativeMapViewPtr, customLayer, before);
|
||||
}
|
||||
|
||||
//
|
||||
// Callbacks
|
||||
//
|
||||
|
||||
protected void onInvalidate() {
|
||||
mMapView.get().onInvalidate();
|
||||
}
|
||||
|
||||
protected void onMapChanged(int rawChange) {
|
||||
mMapView.get().onMapChanged(rawChange);
|
||||
}
|
||||
|
||||
protected void onFpsChanged(double fps) {
|
||||
mMapView.get().onFpsChanged(fps);
|
||||
}
|
||||
|
||||
//
|
||||
// JNI methods
|
||||
//
|
||||
|
||||
private native long nativeCreate(String cachePath, String dataPath, String apkPath, float pixelRatio, int availableProcessors, long totalMemory);
|
||||
|
||||
private native void nativeDestroy(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeInitializeDisplay(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeTerminateDisplay(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeInitializeContext(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeTerminateContext(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeCreateSurface(long nativeMapViewPtr,
|
||||
Surface surface);
|
||||
|
||||
private native void nativeDestroySurface(long nativeMapViewPtr);
|
||||
|
||||
private native void nativePause(long nativeMapViewPtr);
|
||||
|
||||
private native boolean nativeIsPaused(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeResume(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeUpdate(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeRenderSync(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeViewResize(long nativeMapViewPtr, int width, int height);
|
||||
|
||||
private native void nativeFramebufferResize(long nativeMapViewPtr, int fbWidth, int fbHeight);
|
||||
|
||||
private native void nativeAddClass(long nativeMapViewPtr, String clazz);
|
||||
|
||||
private native void nativeRemoveClass(long nativeMapViewPtr, String clazz);
|
||||
|
||||
private native boolean nativeHasClass(long nativeMapViewPtr, String clazz);
|
||||
|
||||
private native void nativeSetClasses(long nativeMapViewPtr,
|
||||
List<String> classes);
|
||||
|
||||
private native List<String> nativeGetClasses(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetDefaultTransitionDuration(
|
||||
long nativeMapViewPtr, long duration);
|
||||
|
||||
private native long nativeGetDefaultTransitionDuration(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetStyleUrl(long nativeMapViewPtr, String url);
|
||||
|
||||
private native void nativeSetStyleJson(long nativeMapViewPtr,
|
||||
String newStyleJson, String base);
|
||||
|
||||
private native String nativeGetStyleJson(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetAccessToken(long nativeMapViewPtr, String accessToken);
|
||||
|
||||
private native String nativeGetAccessToken(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeCancelTransitions(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetGestureInProgress(long nativeMapViewPtr, boolean inProgress);
|
||||
|
||||
private native void nativeMoveBy(long nativeMapViewPtr, double dx,
|
||||
double dy, long duration);
|
||||
|
||||
private native void nativeSetLatLng(long nativeMapViewPtr, LatLng latLng,
|
||||
long duration);
|
||||
|
||||
private native LatLng nativeGetLatLng(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeResetPosition(long nativeMapViewPtr);
|
||||
|
||||
private native double nativeGetPitch(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetPitch(long nativeMapViewPtr, double pitch, long duration);
|
||||
|
||||
private native void nativeScaleBy(long nativeMapViewPtr, double ds,
|
||||
double cx, double cy, long duration);
|
||||
|
||||
private native void nativeSetScale(long nativeMapViewPtr, double scale,
|
||||
double cx, double cy, long duration);
|
||||
|
||||
private native double nativeGetScale(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetZoom(long nativeMapViewPtr, double zoom,
|
||||
long duration);
|
||||
|
||||
private native double nativeGetZoom(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetLatLngZoom(long nativeMapViewPtr,
|
||||
LatLngZoom lonLatZoom, long duration);
|
||||
|
||||
private native LatLngZoom nativeGetLatLngZoom(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeResetZoom(long nativeMapViewPtr);
|
||||
|
||||
private native double nativeGetMinZoom(long nativeMapViewPtr);
|
||||
|
||||
private native double nativeGetMaxZoom(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeRotateBy(long nativeMapViewPtr, double sx,
|
||||
double sy, double ex, double ey, long duration);
|
||||
|
||||
private native void nativeSetBearing(long nativeMapViewPtr, double degrees,
|
||||
long duration);
|
||||
|
||||
private native void nativeSetBearing(long nativeMapViewPtr, double degrees,
|
||||
double cx, double cy);
|
||||
|
||||
private native double nativeGetBearing(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeResetNorth(long nativeMapViewPtr);
|
||||
|
||||
private native long nativeAddMarker(long nativeMapViewPtr, Marker marker);
|
||||
|
||||
private native long[] nativeAddMarkers(long nativeMapViewPtr, List<Marker> markers);
|
||||
|
||||
private native long nativeAddPolyline(long nativeMapViewPtr, Polyline polyline);
|
||||
|
||||
private native long[] nativeAddPolylines(long mNativeMapViewPtr, List<Polyline> polygon);
|
||||
|
||||
private native long nativeAddPolygon(long mNativeMapViewPtr, Polygon polygon);
|
||||
|
||||
private native long[] nativeAddPolygons(long mNativeMapViewPtr, List<Polygon> polygon);
|
||||
|
||||
private native void nativeRemoveAnnotation(long nativeMapViewPtr, long id);
|
||||
|
||||
private native void nativeRemoveAnnotations(long nativeMapViewPtr, long[] id);
|
||||
|
||||
private native long[] nativeGetAnnotationsInBounds(long mNativeMapViewPtr, BoundingBox bbox);
|
||||
|
||||
private native void nativeAddAnnotationIcon(long nativeMapViewPtr, String symbol,
|
||||
int width, int height, float scale, byte[] pixels);
|
||||
|
||||
private native void nativeSetVisibleCoordinateBounds(long mNativeMapViewPtr, LatLng[] coordinates,
|
||||
RectF padding, double direction, long duration);
|
||||
|
||||
private native void nativeOnLowMemory(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetDebug(long nativeMapViewPtr, boolean debug);
|
||||
|
||||
private native void nativeToggleDebug(long nativeMapViewPtr);
|
||||
|
||||
private native boolean nativeGetDebug(long nativeMapViewPtr);
|
||||
|
||||
private native boolean nativeIsFullyLoaded(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetReachability(long nativeMapViewPtr, boolean status);
|
||||
|
||||
private native double nativeGetMetersPerPixelAtLatitude(long nativeMapViewPtr, double lat, double zoom);
|
||||
|
||||
private native ProjectedMeters nativeProjectedMetersForLatLng(long nativeMapViewPtr, LatLng latLng);
|
||||
|
||||
private native LatLng nativeLatLngForProjectedMeters(long nativeMapViewPtr, ProjectedMeters projectedMeters);
|
||||
|
||||
private native PointF nativePixelForLatLng(long nativeMapViewPtr, LatLng latLng);
|
||||
|
||||
private native LatLng nativeLatLngForPixel(long nativeMapViewPtr, PointF pixel);
|
||||
|
||||
private native double nativeGetTopOffsetPixelsForAnnotationSymbol(long nativeMapViewPtr, String symbolName);
|
||||
|
||||
private native void nativeAddCustomLayer(long nativeMapViewPtr, CustomLayer customLayer, String before);
|
||||
}
|
||||
@@ -0,0 +1,707 @@
|
||||
package com.mapbox.mapboxsdk.views;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.location.Location;
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
|
||||
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.location.LocationListener;
|
||||
import com.mapbox.mapboxsdk.location.LocationServices;
|
||||
|
||||
/**
|
||||
* This view shows the user's location, as determined from GPS, on the map
|
||||
* as a dot annotation.
|
||||
*/
|
||||
final class UserLocationView extends View implements LocationListener {
|
||||
|
||||
private MapView mMapView;
|
||||
|
||||
private float mDensity;
|
||||
|
||||
private boolean mShowMarker;
|
||||
private boolean mShowDirection;
|
||||
private boolean mShowAccuracy;
|
||||
private boolean mStaleMarker;
|
||||
|
||||
private PointF mMarkerScreenPoint;
|
||||
private Matrix mMarkerScreenMatrix;
|
||||
|
||||
private Paint mAccuracyPaintFill;
|
||||
private Paint mAccuracyPaintStroke;
|
||||
private Path mAccuracyPath;
|
||||
private RectF mAccuracyBounds;
|
||||
|
||||
private Drawable mUserLocationDrawable;
|
||||
private RectF mUserLocationDrawableBoundsF;
|
||||
private Rect mUserLocationDrawableBounds;
|
||||
|
||||
private Drawable mUserLocationBearingDrawable;
|
||||
private RectF mUserLocationBearingDrawableBoundsF;
|
||||
private Rect mUserLocationBearingDrawableBounds;
|
||||
|
||||
private Drawable mUserLocationStaleDrawable;
|
||||
private RectF mUserLocationStaleDrawableBoundsF;
|
||||
private Rect mUserLocationStaleDrawableBounds;
|
||||
|
||||
private Rect mDirtyRect;
|
||||
private RectF mDirtyRectF;
|
||||
|
||||
private LatLng mMarkerCoordinate;
|
||||
private ValueAnimator mMarkerCoordinateAnimator;
|
||||
private float mGpsMarkerDirection;
|
||||
private float mCompassMarkerDirection;
|
||||
private ObjectAnimator mMarkerDirectionAnimator;
|
||||
private float mMarkerAccuracy;
|
||||
private ObjectAnimator mMarkerAccuracyAnimator;
|
||||
|
||||
private boolean mPaused = false;
|
||||
private Location mUserLocation;
|
||||
|
||||
MapView.OnMyLocationChangeListener mOnMyLocationChangeListener;
|
||||
|
||||
@MyLocationTracking.Mode
|
||||
private int mMyLocationTrackingMode;
|
||||
|
||||
@MyBearingTracking.Mode
|
||||
private int mMyBearingTrackingMode;
|
||||
|
||||
// Compass data
|
||||
private MyBearingListener mBearingChangeListener;
|
||||
|
||||
public UserLocationView(Context context) {
|
||||
super(context);
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
public UserLocationView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
public UserLocationView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
private void initialize(Context context) {
|
||||
// View configuration
|
||||
setEnabled(false);
|
||||
setWillNotDraw(false);
|
||||
|
||||
// Layout params
|
||||
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
setLayoutParams(lp);
|
||||
|
||||
// Setup sensors
|
||||
mBearingChangeListener = new MyBearingListener(context);
|
||||
|
||||
// Setup the custom paint
|
||||
Resources resources = context.getResources();
|
||||
int accuracyColor = resources.getColor(R.color.my_location_ring);
|
||||
|
||||
mDensity = resources.getDisplayMetrics().density;
|
||||
mMarkerCoordinate = new LatLng(0.0, 0.0);
|
||||
mMarkerScreenPoint = new PointF();
|
||||
mMarkerScreenMatrix = new Matrix();
|
||||
|
||||
mAccuracyPaintFill = new Paint();
|
||||
mAccuracyPaintFill.setAntiAlias(true);
|
||||
mAccuracyPaintFill.setStyle(Paint.Style.FILL);
|
||||
mAccuracyPaintFill.setColor(accuracyColor);
|
||||
mAccuracyPaintFill.setAlpha((int) (255 * 0.25f));
|
||||
|
||||
mAccuracyPaintStroke = new Paint();
|
||||
mAccuracyPaintStroke.setAntiAlias(true);
|
||||
mAccuracyPaintStroke.setStyle(Paint.Style.STROKE);
|
||||
mAccuracyPaintStroke.setStrokeWidth(0.5f * mDensity);
|
||||
mAccuracyPaintStroke.setColor(accuracyColor);
|
||||
mAccuracyPaintStroke.setAlpha((int) (255 * 0.5f));
|
||||
|
||||
mAccuracyPath = new Path();
|
||||
mAccuracyBounds = new RectF();
|
||||
|
||||
mUserLocationDrawable = ContextCompat.getDrawable(getContext(), R.drawable.my_location);
|
||||
mUserLocationDrawableBounds = new Rect(
|
||||
-mUserLocationDrawable.getIntrinsicWidth() / 2,
|
||||
-mUserLocationDrawable.getIntrinsicHeight() / 2,
|
||||
mUserLocationDrawable.getIntrinsicWidth() / 2,
|
||||
mUserLocationDrawable.getIntrinsicHeight() / 2);
|
||||
mUserLocationDrawableBoundsF = new RectF(
|
||||
-mUserLocationDrawable.getIntrinsicWidth() / 2,
|
||||
-mUserLocationDrawable.getIntrinsicHeight() / 2,
|
||||
mUserLocationDrawable.getIntrinsicWidth() / 2,
|
||||
mUserLocationDrawable.getIntrinsicHeight() / 2);
|
||||
mUserLocationDrawable.setBounds(mUserLocationDrawableBounds);
|
||||
|
||||
mUserLocationBearingDrawable = ContextCompat.getDrawable(getContext(), R.drawable.my_location_bearing);
|
||||
mUserLocationBearingDrawableBounds = new Rect(
|
||||
-mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
|
||||
-mUserLocationBearingDrawable.getIntrinsicHeight() / 2,
|
||||
mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
|
||||
mUserLocationBearingDrawable.getIntrinsicHeight() / 2);
|
||||
mUserLocationBearingDrawableBoundsF = new RectF(
|
||||
-mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
|
||||
-mUserLocationBearingDrawable.getIntrinsicHeight() / 2,
|
||||
mUserLocationBearingDrawable.getIntrinsicWidth() / 2,
|
||||
mUserLocationBearingDrawable.getIntrinsicHeight() / 2);
|
||||
mUserLocationBearingDrawable.setBounds(mUserLocationBearingDrawableBounds);
|
||||
|
||||
mUserLocationStaleDrawable = ContextCompat.getDrawable(getContext(), R.drawable.my_location_stale);
|
||||
mUserLocationStaleDrawableBounds = new Rect(
|
||||
-mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
|
||||
-mUserLocationStaleDrawable.getIntrinsicHeight() / 2,
|
||||
mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
|
||||
mUserLocationStaleDrawable.getIntrinsicHeight() / 2);
|
||||
mUserLocationStaleDrawableBoundsF = new RectF(
|
||||
-mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
|
||||
-mUserLocationStaleDrawable.getIntrinsicHeight() / 2,
|
||||
mUserLocationStaleDrawable.getIntrinsicWidth() / 2,
|
||||
mUserLocationStaleDrawable.getIntrinsicHeight() / 2);
|
||||
mUserLocationStaleDrawable.setBounds(mUserLocationStaleDrawableBounds);
|
||||
}
|
||||
|
||||
public void setMapView(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
|
||||
mBearingChangeListener.onStart(getContext());
|
||||
}
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
mBearingChangeListener.onStop();
|
||||
cancelAnimations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
if (!mShowMarker) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvas.concat(mMarkerScreenMatrix);
|
||||
|
||||
Drawable dotDrawable = mShowDirection ? mUserLocationBearingDrawable : mUserLocationDrawable;
|
||||
dotDrawable = mStaleMarker ? mUserLocationStaleDrawable : dotDrawable;
|
||||
// IMPORTANT also update in update()
|
||||
RectF dotBounds = mShowDirection ? mUserLocationBearingDrawableBoundsF : mUserLocationDrawableBoundsF;
|
||||
dotBounds = mStaleMarker ? mUserLocationStaleDrawableBoundsF : dotBounds;
|
||||
|
||||
boolean willDraw = true;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN || !canvas.isHardwareAccelerated()) {
|
||||
willDraw = mShowAccuracy && !mStaleMarker && !canvas.quickReject(mAccuracyPath, Canvas.EdgeType.AA);
|
||||
}
|
||||
willDraw |= !canvas.quickReject(dotBounds, Canvas.EdgeType.AA);
|
||||
|
||||
if (willDraw) {
|
||||
if (mShowAccuracy && !mStaleMarker) {
|
||||
canvas.drawPath(mAccuracyPath, mAccuracyPaintFill);
|
||||
canvas.drawPath(mAccuracyPath, mAccuracyPaintStroke);
|
||||
}
|
||||
dotDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
|
||||
mMyLocationTrackingMode = myLocationTrackingMode;
|
||||
|
||||
if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && mUserLocation != null) {
|
||||
// center map directly if we have a location fix
|
||||
mMapView.setCenterCoordinate(new LatLng(mUserLocation));
|
||||
}
|
||||
}
|
||||
|
||||
@MyLocationTracking.Mode
|
||||
public int getMyLocationTrackingMode() {
|
||||
return mMyLocationTrackingMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
|
||||
toggleGps(enabled);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if (isEnabled() && mShowMarker) {
|
||||
setVisibility(View.VISIBLE);
|
||||
|
||||
mStaleMarker = isStale(mUserLocation);
|
||||
|
||||
// compute new marker position
|
||||
// TODO add JNI method that takes existing pointf
|
||||
if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
|
||||
mMarkerScreenPoint = mMapView.toScreenLocation(mMarkerCoordinate);
|
||||
mMarkerScreenMatrix.reset();
|
||||
mMarkerScreenMatrix.setTranslate(
|
||||
mMarkerScreenPoint.x,
|
||||
mMarkerScreenPoint.y);
|
||||
} else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
|
||||
mMarkerScreenMatrix.setTranslate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
|
||||
mMapView.setCenterCoordinate(mMarkerCoordinate, true);
|
||||
}
|
||||
|
||||
// rotate so arrow in points to bearing
|
||||
if (mShowDirection) {
|
||||
if (mMyBearingTrackingMode == MyBearingTracking.COMPASS && mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
|
||||
mMarkerScreenMatrix.preRotate(mCompassMarkerDirection + (float) mMapView.getDirection());
|
||||
} else if (mMyBearingTrackingMode == MyBearingTracking.GPS) {
|
||||
if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
|
||||
mMarkerScreenMatrix.preRotate(mGpsMarkerDirection + (float) mMapView.getDirection());
|
||||
} else {
|
||||
mMarkerScreenMatrix.preRotate(mGpsMarkerDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjust accuracy circle
|
||||
if (mShowAccuracy && !mStaleMarker) {
|
||||
mAccuracyPath.reset();
|
||||
mAccuracyPath.addCircle(0.0f, 0.0f,
|
||||
(float) (mMarkerAccuracy / mMapView.getMetersPerPixelAtLatitude(
|
||||
mMarkerCoordinate.getLatitude())),
|
||||
Path.Direction.CW);
|
||||
|
||||
mAccuracyPath.computeBounds(mAccuracyBounds, false);
|
||||
mAccuracyBounds.inset(-1.0f, -1.0f);
|
||||
}
|
||||
|
||||
// invalidate changed pixels
|
||||
if (mDirtyRect == null) {
|
||||
mDirtyRect = new Rect();
|
||||
mDirtyRectF = new RectF();
|
||||
} else {
|
||||
// the old marker location
|
||||
invalidate(mDirtyRect);
|
||||
}
|
||||
|
||||
RectF dotBounds = mShowDirection ? mUserLocationBearingDrawableBoundsF : mUserLocationDrawableBoundsF;
|
||||
dotBounds = mStaleMarker ? mUserLocationStaleDrawableBoundsF : dotBounds;
|
||||
RectF largerBounds = mShowAccuracy && !mStaleMarker && mAccuracyBounds.contains(dotBounds)
|
||||
? mAccuracyBounds : dotBounds;
|
||||
mMarkerScreenMatrix.mapRect(mDirtyRectF, largerBounds);
|
||||
mDirtyRectF.roundOut(mDirtyRect);
|
||||
invalidate(mDirtyRect); // the new marker location
|
||||
} else {
|
||||
setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return mUserLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enabled / Disable GPS location updates along with updating the UI
|
||||
*
|
||||
* @param enableGps true if GPS is to be enabled, false if GPS is to be disabled
|
||||
*/
|
||||
private void toggleGps(boolean enableGps) {
|
||||
|
||||
LocationServices locationServices = LocationServices.getLocationServices(getContext());
|
||||
|
||||
if (enableGps) {
|
||||
// Set an initial location if one available
|
||||
Location lastLocation = locationServices.getLastLocation();
|
||||
if (lastLocation != null) {
|
||||
setLocation(lastLocation);
|
||||
}
|
||||
|
||||
// Register for Location Updates
|
||||
locationServices.addLocationListener(this);
|
||||
} else {
|
||||
// Disable location and user dot
|
||||
setLocation(null);
|
||||
|
||||
// Deregister for Location Updates
|
||||
locationServices.removeLocationListener(this);
|
||||
}
|
||||
|
||||
locationServices.toggleGPS(enableGps);
|
||||
}
|
||||
|
||||
public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
|
||||
mMyBearingTrackingMode = myBearingTrackingMode;
|
||||
|
||||
if (myBearingTrackingMode == MyBearingTracking.COMPASS) {
|
||||
mShowAccuracy = false;
|
||||
mShowDirection = false;
|
||||
mBearingChangeListener.onStart(getContext());
|
||||
} else {
|
||||
mBearingChangeListener.onStop();
|
||||
if (myBearingTrackingMode == MyBearingTracking.GPS) {
|
||||
mShowDirection = (mUserLocation != null) && mUserLocation.hasBearing();
|
||||
} else {
|
||||
mShowDirection = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MyBearingTracking.Mode
|
||||
public int getMyBearingTrackingMode() {
|
||||
return mMyBearingTrackingMode;
|
||||
}
|
||||
|
||||
private class MyBearingListener implements SensorEventListener {
|
||||
|
||||
private SensorManager mSensorManager;
|
||||
private Sensor mAccelerometer;
|
||||
private Sensor mMagnetometer;
|
||||
private float[] mLastAccelerometer = new float[3];
|
||||
private float[] mLastMagnetometer = new float[3];
|
||||
private boolean mLastAccelerometerSet = false;
|
||||
private boolean mLastMagnetometerSet = false;
|
||||
private float[] mR = new float[9];
|
||||
private float[] mOrientation = new float[3];
|
||||
private float mCurrentDegree = 0f;
|
||||
|
||||
// Controls the sensor update rate in milliseconds
|
||||
private static final int UPDATE_RATE_MS = 300;
|
||||
|
||||
// Compass data
|
||||
private float mCompassBearing;
|
||||
private long mCompassUpdateNextTimestamp = 0;
|
||||
|
||||
public MyBearingListener(Context context) {
|
||||
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
||||
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
|
||||
}
|
||||
|
||||
public void onStart(Context context) {
|
||||
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
|
||||
mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME);
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
mSensorManager.unregisterListener(this, mAccelerometer);
|
||||
mSensorManager.unregisterListener(this, mMagnetometer);
|
||||
}
|
||||
|
||||
public float getCompassBearing() {
|
||||
return mCompassBearing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (mPaused) {
|
||||
return;
|
||||
}
|
||||
|
||||
long currentTime = SystemClock.elapsedRealtime();
|
||||
if (currentTime < mCompassUpdateNextTimestamp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.sensor == mAccelerometer) {
|
||||
System.arraycopy(event.values, 0, mLastAccelerometer, 0, event.values.length);
|
||||
mLastAccelerometerSet = true;
|
||||
} else if (event.sensor == mMagnetometer) {
|
||||
System.arraycopy(event.values, 0, mLastMagnetometer, 0, event.values.length);
|
||||
mLastMagnetometerSet = true;
|
||||
}
|
||||
|
||||
if (mLastAccelerometerSet && mLastMagnetometerSet) {
|
||||
SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer);
|
||||
SensorManager.getOrientation(mR, mOrientation);
|
||||
float azimuthInRadians = mOrientation[0];
|
||||
float azimuthInDegress = (float) (Math.toDegrees(azimuthInRadians) + 360) % 360;
|
||||
mCompassBearing = mCurrentDegree;
|
||||
mCurrentDegree = -azimuthInDegress;
|
||||
}
|
||||
mCompassUpdateNextTimestamp = currentTime + UPDATE_RATE_MS;
|
||||
setCompass(mCompassBearing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO add accuracy to the equiation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method for receiving location updates from LocationServices.
|
||||
*
|
||||
* @param location The new Location data
|
||||
*/
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
if (mPaused) {
|
||||
return;
|
||||
}
|
||||
setLocation(location);
|
||||
}
|
||||
|
||||
private boolean isStale(Location location) {
|
||||
if (location != null) {
|
||||
long ageInNanos;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
ageInNanos = SystemClock.elapsedRealtimeNanos() -
|
||||
location.getElapsedRealtimeNanos();
|
||||
} else {
|
||||
ageInNanos = (System.currentTimeMillis() - location.getTime()) * 1000 * 1000;
|
||||
}
|
||||
final long oneMinuteInNanos = 60L * 1000 * 1000 * 1000;
|
||||
return ageInNanos > oneMinuteInNanos;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handles location updates from GPS
|
||||
private void setLocation(Location location) {
|
||||
// if null we should hide the marker
|
||||
if (location == null) {
|
||||
mShowMarker = false;
|
||||
mShowDirection = false;
|
||||
mShowAccuracy = false;
|
||||
|
||||
cancelAnimations();
|
||||
|
||||
mUserLocation = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mMarkerCoordinateAnimator != null) {
|
||||
mMarkerCoordinateAnimator.end();
|
||||
mMarkerCoordinateAnimator = null;
|
||||
}
|
||||
|
||||
if (mMarkerDirectionAnimator != null) {
|
||||
mMarkerDirectionAnimator.end();
|
||||
mMarkerDirectionAnimator = null;
|
||||
}
|
||||
|
||||
if (mMarkerAccuracyAnimator != null) {
|
||||
mMarkerAccuracyAnimator.end();
|
||||
mMarkerAccuracyAnimator = null;
|
||||
}
|
||||
|
||||
mShowMarker = true;
|
||||
|
||||
LatLng previousCoordinate;
|
||||
if (mUserLocation == null) {
|
||||
previousCoordinate = new LatLng(location);
|
||||
} else {
|
||||
previousCoordinate = new LatLng(mUserLocation);
|
||||
}
|
||||
|
||||
if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
|
||||
// moving marker above map
|
||||
mMarkerCoordinateAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
|
||||
mMarkerCoordinateAnimator.setDuration(1000);
|
||||
mMarkerCoordinateAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(
|
||||
previousCoordinate, new LatLng(location)
|
||||
));
|
||||
mMarkerCoordinateAnimator.start();
|
||||
} else {
|
||||
// moving map under the tracker
|
||||
mMarkerCoordinate = new LatLng(location);
|
||||
mMapView.setCenterCoordinate(mMarkerCoordinate, true);
|
||||
}
|
||||
|
||||
if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE && mMyBearingTrackingMode == MyBearingTracking.GPS) {
|
||||
// show GPS direction
|
||||
mShowDirection = location.hasBearing();
|
||||
if (mShowDirection) {
|
||||
if (mUserLocation != null && mUserLocation.hasBearing()) {
|
||||
mGpsMarkerDirection = mUserLocation.getBearing();
|
||||
}
|
||||
float oldDir = mGpsMarkerDirection;
|
||||
float newDir = location.getBearing();
|
||||
float diff = oldDir - newDir;
|
||||
if (diff > 180.0f) {
|
||||
newDir += 360.0f;
|
||||
} else if (diff < -180.0f) {
|
||||
newDir -= 360.f;
|
||||
}
|
||||
mMarkerDirectionAnimator = ObjectAnimator.ofFloat(this, "direction", oldDir, newDir);
|
||||
mMarkerDirectionAnimator.setDuration(1000);
|
||||
mMarkerDirectionAnimator.start();
|
||||
}
|
||||
} else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW && mMyBearingTrackingMode == MyBearingTracking.GPS) {
|
||||
// always show north & rotate map below
|
||||
mShowDirection = true;
|
||||
mGpsMarkerDirection = 0;
|
||||
if (location.hasBearing()) {
|
||||
mMapView.setBearing(location.getBearing());
|
||||
}
|
||||
}
|
||||
|
||||
mShowAccuracy = location.hasAccuracy();
|
||||
if (mShowAccuracy) {
|
||||
if (mUserLocation != null && mUserLocation.hasAccuracy()) {
|
||||
mMarkerAccuracy = mUserLocation.getAccuracy();
|
||||
}
|
||||
mMarkerAccuracyAnimator = ObjectAnimator.ofFloat(this, "accuracy", location.getAccuracy());
|
||||
mMarkerAccuracyAnimator.setDuration(1000);
|
||||
mMarkerAccuracyAnimator.start();
|
||||
}
|
||||
|
||||
mUserLocation = location;
|
||||
updateOnNextFrame();
|
||||
|
||||
if (mOnMyLocationChangeListener != null) {
|
||||
mOnMyLocationChangeListener.onMyLocationChange(location);
|
||||
}
|
||||
}
|
||||
|
||||
// handles compass sensor updates
|
||||
private void setCompass(float bearing) {
|
||||
if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
|
||||
// animate marker
|
||||
mShowDirection = true;
|
||||
float oldDir = mCompassMarkerDirection;
|
||||
float newDir = bearing;
|
||||
float diff = oldDir - newDir;
|
||||
if (diff > 180.0f) {
|
||||
newDir += 360.0f;
|
||||
} else if (diff < -180.0f) {
|
||||
newDir -= 360.f;
|
||||
}
|
||||
mMarkerDirectionAnimator = ObjectAnimator.ofFloat(this, "direction", oldDir, newDir);
|
||||
mMarkerDirectionAnimator.setDuration(1000);
|
||||
mMarkerDirectionAnimator.start();
|
||||
mCompassMarkerDirection = bearing;
|
||||
|
||||
} else if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
|
||||
cancelAnimations();
|
||||
if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
|
||||
// always show north & change map direction
|
||||
mShowDirection = true;
|
||||
mGpsMarkerDirection = 0;
|
||||
mCompassMarkerDirection = 0;
|
||||
mMapView.setBearing(bearing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateOnNextFrame() {
|
||||
mMapView.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from MapView.onPause()
|
||||
*/
|
||||
public void pause() {
|
||||
mPaused = true;
|
||||
toggleGps(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from MapView.onResume()
|
||||
*/
|
||||
public void resume() {
|
||||
mPaused = false;
|
||||
if (isEnabled()) {
|
||||
toggleGps(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnMyLocationChangeListener(@Nullable MapView.OnMyLocationChangeListener listener) {
|
||||
mOnMyLocationChangeListener = listener;
|
||||
}
|
||||
|
||||
// public for animator only
|
||||
public float getDirection() {
|
||||
if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
|
||||
return mCompassMarkerDirection;
|
||||
}
|
||||
return mGpsMarkerDirection;
|
||||
}
|
||||
|
||||
// public for animator only
|
||||
public void setDirection(float direction) {
|
||||
if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) {
|
||||
mCompassMarkerDirection = direction % 360.0f;
|
||||
} else {
|
||||
mGpsMarkerDirection = direction % 360.0f;
|
||||
}
|
||||
updateOnNextFrame();
|
||||
}
|
||||
|
||||
// public for animator only
|
||||
public float getAccuracy() {
|
||||
return mMarkerAccuracy;
|
||||
}
|
||||
|
||||
// public for animator only
|
||||
public void setAccuracy(float accuracy) {
|
||||
mMarkerAccuracy = accuracy;
|
||||
updateOnNextFrame();
|
||||
}
|
||||
|
||||
private class MarkerCoordinateAnimatorListener implements ValueAnimator.AnimatorUpdateListener {
|
||||
|
||||
private double mFromLat;
|
||||
private double mFromLng;
|
||||
private double mToLat;
|
||||
private double mToLng;
|
||||
|
||||
private MarkerCoordinateAnimatorListener(LatLng from, LatLng to) {
|
||||
mFromLat = from.getLatitude();
|
||||
mFromLng = from.getLongitude();
|
||||
mToLat = to.getLatitude();
|
||||
mToLng = to.getLongitude();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
float frac = animation.getAnimatedFraction();
|
||||
double latitude = mFromLat + (mToLat - mFromLat) * frac;
|
||||
double longitude = mFromLng + (mToLng - mFromLng) * frac;
|
||||
mMarkerCoordinate.setLatitude(latitude);
|
||||
mMarkerCoordinate.setLongitude(longitude);
|
||||
updateOnNextFrame();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelAnimations() {
|
||||
if (mMarkerCoordinateAnimator != null) {
|
||||
mMarkerCoordinateAnimator.cancel();
|
||||
mMarkerCoordinateAnimator = null;
|
||||
}
|
||||
|
||||
if (mMarkerDirectionAnimator != null) {
|
||||
mMarkerDirectionAnimator.cancel();
|
||||
mMarkerDirectionAnimator = null;
|
||||
}
|
||||
|
||||
if (mMarkerAccuracyAnimator != null) {
|
||||
mMarkerAccuracyAnimator.cancel();
|
||||
mMarkerAccuracyAnimator = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* This package contains the {@link com.mapbox.mapboxsdk.views.MapView} and related classes.
|
||||
* {@code MapView} is the core component of this SDK and adds a map to your app.
|
||||
*/
|
||||
package com.mapbox.mapboxsdk.views;
|
||||
@@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<body>
|
||||
An open source OpenGL-based vector map solution for Android with full styling capabilities.
|
||||
<p/>
|
||||
For more information, check out <a href="https://www.mapbox.com/android-sdk/">our online overview</a>.
|
||||
<p/>
|
||||
<!--TODO: Need an Android image-->
|
||||
<!--<img src="https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/ios/screenshot.png"/>-->
|
||||
<!--<h2>Changelog</h2>
|
||||
<h3>2.1.0</h3>
|
||||
Initial release.
|
||||
<h3>2.0.0</h3>
|
||||
Initial preview release.
|
||||
<h3>0.1.0</h3>
|
||||
<p>Initial beta release.</p>-->
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user