[android] Move into platform subdirectory

This commit is contained in:
John Firebaugh
2015-12-11 12:39:43 -08:00
parent 0db9f229f2
commit 13d5b18eba
227 changed files with 28 additions and 23 deletions

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,4 @@
/**
* Do not use this package. Used internally by the SDK.
*/
package com.almeros.android.multitouch.gesturedetectors;

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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() + "]]";
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,4 @@
/**
* This package contains classes to add and manage annotations and markers in your map.
*/
package com.mapbox.mapboxsdk.annotations;

View File

@@ -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;
}

View File

@@ -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";
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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";
}

View File

@@ -0,0 +1,4 @@
/**
* This package provides access to several map related constants and bundled styles.
*/
package com.mapbox.mapboxsdk.constants;

View File

@@ -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");
}
}

View File

@@ -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.");
}
}

View File

@@ -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.");
}
}

View File

@@ -0,0 +1,4 @@
/**
* This package contains exceptions thrown in this SDK.
*/
package com.mapbox.mapboxsdk.exceptions;

View File

@@ -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);
}
}

View File

@@ -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() + "]";
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,12 @@
package com.mapbox.mapboxsdk.geometry;
/**
* A Latitude, Longitude point.
*/
public interface ILatLng {
double getLatitude();
double getLongitude();
double getAltitude();
}

View File

@@ -0,0 +1,7 @@
package com.mapbox.mapboxsdk.geometry;
public interface IProjectedMeters {
double getNorthing();
double getEasting();
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,4 @@
/**
* Do not use this package. Used internally by the SDK.
*/
package com.mapbox.mapboxsdk.http;

View File

@@ -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;
}

View File

@@ -0,0 +1,4 @@
/**
* This package contains classes to add and manage layers in your map.
*/
package com.mapbox.mapboxsdk.layers;

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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();
}
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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>