summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidSDK/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main')
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java38
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java41
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java28
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java44
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java (renamed from platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdate.java)2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java (renamed from platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdateFactory.java)54
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TelemetryServiceNotConfiguredException.java18
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationListener.java7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationService.java166
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java133
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java20
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java795
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java694
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java116
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java27
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java20
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java121
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java207
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java36
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java (renamed from platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CompassView.java)20
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java (renamed from platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UserLocationView.java)88
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEvent.java59
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java532
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java71
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java145
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ApiAccess.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/attribution_telemetry_view.xml21
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/infowindow_content.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml8
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/arrays.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml28
43 files changed, 2561 insertions, 1063 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
index 1b8b54c86f..0307d462c8 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
@@ -5,5 +5,6 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
</manifest>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java
new file mode 100644
index 0000000000..5b1eebdb1b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java
@@ -0,0 +1,38 @@
+package com.mapbox.mapboxsdk.annotations;
+
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+public abstract class BaseMarkerOptions<U extends Marker, T extends BaseMarkerOptions<U, T>> implements Parcelable {
+
+ protected LatLng position;
+ protected String snippet;
+ protected String title;
+ protected Icon icon;
+
+ public T position(LatLng position) {
+ this.position = position;
+ return getThis();
+ }
+
+ public T snippet(String snippet) {
+ this.snippet = snippet;
+ return getThis();
+ }
+
+ public T title(String title) {
+ this.title = title;
+ return getThis();
+ }
+
+ public T icon(Icon icon) {
+ this.icon = icon;
+ return getThis();
+ }
+
+ public abstract T getThis();
+
+ public abstract U getMarker();
+
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java
index bee4ec47c5..d7d41b98be 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java
@@ -150,4 +150,8 @@ public final class IconFactory {
}
return fromInputStream(is);
}
+
+ public static Icon recreate(@NonNull String iconId, @NonNull Bitmap bitmap) {
+ return new Icon(iconId, bitmap);
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java
index 9c79ffcbd9..d8763e321d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java
@@ -53,22 +53,35 @@ public class InfoWindow {
mIsVisible = false;
mView = new WeakReference<>(view);
- // default behavior: close it when clicking on the tooltip:
- view.setOnTouchListener(new View.OnTouchListener() {
+ view.setOnClickListener(new View.OnClickListener() {
@Override
- public boolean onTouch(View v, MotionEvent e) {
- if (e.getAction() == MotionEvent.ACTION_UP) {
+ public void onClick(View v) {
+ MapboxMap mapboxMap = mMapboxMap.get();
+ if (mapboxMap != null) {
+ MapboxMap.OnInfoWindowClickListener onInfoWindowClickListener = mapboxMap.getOnInfoWindowClickListener();
boolean handledDefaultClick = false;
- MapboxMap.OnInfoWindowClickListener onInfoWindowClickListener =
- mMapboxMap.get().getOnInfoWindowClickListener();
if (onInfoWindowClickListener != null) {
- handledDefaultClick = onInfoWindowClickListener.onMarkerClick(getBoundMarker());
+ handledDefaultClick = onInfoWindowClickListener.onInfoWindowClick(getBoundMarker());
}
if (!handledDefaultClick) {
+ // default behavior: close it when clicking on the tooltip:
close();
}
}
+ }
+ });
+
+ view.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ MapboxMap mapboxMap = mMapboxMap.get();
+ if (mapboxMap != null) {
+ MapboxMap.OnInfoWindowLongClickListener listener = mapboxMap.getOnInfoWindowLongClickListener();
+ if (listener != null) {
+ listener.onInfoWindowLongClick(getBoundMarker());
+ }
+ }
return true;
}
});
@@ -179,11 +192,10 @@ public class InfoWindow {
if (mIsVisible) {
mIsVisible = false;
View view = mView.get();
- if (view != null) {
+ if (view != null && view.getParent() != null) {
((ViewGroup) view.getParent()).removeView(view);
- setBoundMarker(null);
- onClose();
}
+ onClose();
}
return this;
}
@@ -210,7 +222,12 @@ public class InfoWindow {
private void onClose() {
MapboxMap mapboxMap = mMapboxMap.get();
if (mapboxMap != null) {
+ MapboxMap.OnInfoWindowCloseListener listener = mapboxMap.getOnInfoWindowCloseListener();
+ if (listener != null) {
+ listener.onInfoWindowClose(getBoundMarker());
+ }
mapboxMap.deselectMarker(getBoundMarker());
+ setBoundMarker(null);
}
}
@@ -237,4 +254,8 @@ public class InfoWindow {
}
}
+ boolean isVisible() {
+ return mIsVisible;
+ }
+
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java
index 27c9c03697..c2683cbb56 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java
@@ -15,7 +15,7 @@ import com.mapbox.mapboxsdk.maps.MapView;
* An {@link InfoWindow} can be shown when a Marker is pressed
* <p/>
*/
-public final class Marker extends Annotation {
+public class Marker extends Annotation {
private LatLng position;
private String snippet;
@@ -32,6 +32,13 @@ public final class Marker extends Annotation {
super();
}
+ public Marker(BaseMarkerOptions baseMarkerOptions) {
+ position = baseMarkerOptions.position;
+ snippet = baseMarkerOptions.snippet;
+ icon = baseMarkerOptions.icon;
+ title = baseMarkerOptions.title;
+ }
+
public LatLng getPosition() {
return position;
}
@@ -61,8 +68,17 @@ public final class Marker extends Annotation {
return infoWindowShown;
}
- void setPosition(LatLng position) {
+ /**
+ * Sets the position.
+ *
+ * @param position new position
+ */
+ public void setPosition(LatLng position) {
this.position = position;
+ MapboxMap map = getMapboxMap();
+ if (map != null) {
+ map.updateMarker(this);
+ }
}
void setSnippet(String snippet) {
@@ -70,10 +86,16 @@ public final class Marker extends Annotation {
}
/**
- * Do not use this method. Used internally by the SDK.
+ * Sets the icon.
+ *
+ * @param icon The icon to be used as Marker image
*/
public void setIcon(@Nullable Icon icon) {
this.icon = icon;
+ MapboxMap map = getMapboxMap();
+ if (map != null) {
+ map.updateMarker(this);
+ }
}
public Icon getIcon() {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java
index a83a6991b2..5cc54cd1ca 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java
@@ -12,9 +12,9 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
* <p>
* Builder for composing {@link com.mapbox.mapboxsdk.annotations.Marker} objects.
* </p>
- *
+ * <p/>
* <h3>Example</h3>
- *
+ * <p/>
* <pre>
* mMapView.addMarker(new MarkerOptions()
* .title("Intersection")
@@ -22,7 +22,7 @@ import com.mapbox.mapboxsdk.geometry.LatLng;
* .position(new LatLng(38.9002073, -77.03364419)));
* </pre>
*/
-public final class MarkerOptions implements Parcelable {
+public final class MarkerOptions extends BaseMarkerOptions<Marker, MarkerOptions> implements Parcelable {
public static final Parcelable.Creator<MarkerOptions> CREATOR
= new Parcelable.Creator<MarkerOptions>() {
@@ -47,6 +47,11 @@ public final class MarkerOptions implements Parcelable {
}
@Override
+ public MarkerOptions getThis() {
+ return this;
+ }
+
+ @Override
public int describeContents() {
return 0;
}
@@ -72,43 +77,27 @@ public final class MarkerOptions implements Parcelable {
* @return Marker The build marker
*/
public Marker getMarker() {
+ marker.setPosition(position);
+ marker.setSnippet(snippet);
+ marker.setTitle(title);
+ marker.setIcon(icon);
return marker;
}
public LatLng getPosition() {
- return marker.getPosition();
+ return position;
}
public String getSnippet() {
- return marker.getSnippet();
+ return snippet;
}
public String getTitle() {
- return marker.getTitle();
+ return title;
}
public Icon 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 Icon icon) {
- marker.setIcon(icon);
- return this;
- }
-
- public MarkerOptions title(String title) {
- marker.setTitle(title);
- return this;
+ return icon;
}
@Override
@@ -125,7 +114,6 @@ public final class MarkerOptions implements Parcelable {
if (getIcon() != null ? !getIcon().equals(marker.getIcon()) : marker.getIcon() != null)
return false;
return !(getTitle() != null ? !getTitle().equals(marker.getTitle()) : marker.getTitle() != null);
-
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
index 004e7f5f84..ec2f0eb316 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java
@@ -9,7 +9,6 @@ import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.MathConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.CameraUpdateFactory;
import com.mapbox.mapboxsdk.utils.MathUtils;
public final class CameraPosition implements Parcelable {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdate.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java
index 0f3e710134..c6852624ef 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdate.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.maps;
+package com.mapbox.mapboxsdk.camera;
import android.support.annotation.NonNull;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdateFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java
index cad5152316..df156961a0 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraUpdateFactory.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.maps;
+package com.mapbox.mapboxsdk.camera;
import android.graphics.Point;
import android.graphics.PointF;
@@ -6,15 +6,17 @@ import android.graphics.RectF;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.Projection;
+import com.mapbox.mapboxsdk.maps.UiSettings;
import com.mapbox.mapboxsdk.utils.MathUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-public class CameraUpdateFactory {
+public final class CameraUpdateFactory {
/**
* Returns a CameraUpdate that moves the camera to a specified CameraPosition.
@@ -144,7 +146,7 @@ public class CameraUpdateFactory {
// CameraUpdate types
//
- public static class CameraPositionUpdate implements CameraUpdate {
+ static final class CameraPositionUpdate implements CameraUpdate {
private final float bearing;
private final LatLng target;
@@ -189,7 +191,7 @@ public class CameraUpdateFactory {
}
}
- public static class CameraBoundsUpdate implements CameraUpdate {
+ static final class CameraBoundsUpdate implements CameraUpdate {
private LatLngBounds bounds;
private RectF padding;
@@ -217,39 +219,40 @@ public class CameraUpdateFactory {
@Override
public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) {
- MapView mapView = mapboxMap.getMapView();
+ // Get required objects
+ Projection projection = mapboxMap.getProjection();
+ UiSettings uiSettings = mapboxMap.getUiSettings();
RectF padding = getPadding();
- // Calculate the bounds of the possibly rotated shape with respect to the viewport.
+ // Calculate the bounds of the possibly rotated shape with respect to the viewport
PointF nePixel = new PointF(-10000, -10000);
- PointF swPixel = new PointF(1000, 10000);
- float viewportHeight = mapView.getHeight();
+ PointF swPixel = new PointF(10000, 10000);
+ float viewportHeight = uiSettings.getHeight();
for (LatLng latLng : getBounds().toLatLngs()) {
- PointF pixel = mapView.toScreenLocation(latLng);
+ PointF pixel = projection.toScreenLocation(latLng);
swPixel.x = Math.min(swPixel.x, pixel.x);
nePixel.x = Math.max(nePixel.x, pixel.x);
swPixel.y = Math.min(swPixel.y, viewportHeight - pixel.y);
nePixel.y = Math.max(nePixel.y, viewportHeight - pixel.y);
}
+ // Calculate wid=th/height
float width = nePixel.x - swPixel.x;
float height = nePixel.y - swPixel.y;
- // Calculate the zoom level.
- float scaleX = (mapView.getWidth() - padding.left - padding.right) / width;
- float scaleY = (mapView.getHeight() - padding.top - padding.bottom) / height;
+ // Calculate the zoom level
+ float scaleX = (uiSettings.getWidth() - padding.left - padding.right) / width;
+ float scaleY = (uiSettings.getHeight() - padding.top - padding.bottom) / height;
float minScale = scaleX < scaleY ? scaleX : scaleY;
- double zoom = Math.log(mapView.getScale() * minScale) / Math.log(2);
- zoom = MathUtils.clamp(zoom, (float) mapView.getMinZoom(), (float) mapView.getMaxZoom());
+ double zoom = projection.calculateZoom(minScale);
+ zoom = MathUtils.clamp(zoom, (float) uiSettings.getMinZoom(), (float) uiSettings.getMaxZoom());
- // Calculate the center point of a virtual bounds that is extended in all directions by padding.
+ // Calculate the center point
PointF paddedNEPixel = new PointF(nePixel.x + padding.right / minScale, nePixel.y + padding.top / minScale);
PointF paddedSWPixel = new PointF(swPixel.x - padding.left / minScale, swPixel.y - padding.bottom / minScale);
PointF centerPixel = new PointF((paddedNEPixel.x + paddedSWPixel.x) / 2, (paddedNEPixel.y + paddedSWPixel.y) / 2);
-
centerPixel.y = viewportHeight - centerPixel.y;
-
- LatLng center = mapboxMap.getProjection().fromScreenLocation(centerPixel);
+ LatLng center = projection.fromScreenLocation(centerPixel);
return new CameraPosition.Builder()
.target(center)
@@ -260,7 +263,7 @@ public class CameraUpdateFactory {
}
}
- public static class CameraMoveUpdate implements CameraUpdate {
+ static final class CameraMoveUpdate implements CameraUpdate {
private float x;
private float y;
@@ -272,15 +275,16 @@ public class CameraUpdateFactory {
@Override
public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) {
- MapView mapView = mapboxMap.getMapView();
+ UiSettings uiSettings = mapboxMap.getUiSettings();
+ Projection projection = mapboxMap.getProjection();
// Calculate the new center point
- float viewPortWidth = mapView.getWidth();
- float viewPortHeight = mapView.getHeight();
+ float viewPortWidth = uiSettings.getWidth();
+ float viewPortHeight = uiSettings.getHeight();
PointF targetPoint = new PointF(viewPortWidth / 2 + x, viewPortHeight / 2 + y);
// Convert point to LatLng
- LatLng latLng = mapView.fromScreenLocation(targetPoint);
+ LatLng latLng = projection.fromScreenLocation(targetPoint);
CameraPosition cameraPosition = mapboxMap.getCameraPosition();
return new CameraPosition.Builder()
@@ -292,7 +296,7 @@ public class CameraUpdateFactory {
}
}
- public static class ZoomUpdate implements CameraUpdate {
+ static final class ZoomUpdate implements CameraUpdate {
@IntDef({ZOOM_IN, ZOOM_OUT, ZOOM_BY, ZOOM_TO, ZOOM_TO_POINT})
@Retention(RetentionPolicy.SOURCE)
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
index 33ebfca2fa..f04087818b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
@@ -18,6 +18,16 @@ public class MapboxConstants {
public static final String KEY_META_DATA_MANIFEST = "com.mapbox.AccessToken";
/**
+ * Key used to store staging data server url in AndroidManifest.xml
+ */
+ public static final String KEY_META_DATA_STAGING_SERVER = "com.mapbox.TestEventsServer";
+
+ /**
+ * Key used to store staging data server access token in AndroidManifest.xml
+ */
+ public static final String KEY_META_DATA_STAGING_ACCESS_TOKEN = "com.mapbox.TestEventsAccessToken";
+
+ /**
* Default animation time
*/
public static final int ANIMATION_DURATION = 300;
@@ -58,6 +68,7 @@ public class MapboxConstants {
public static final String FRAG_ARG_MAPBOXMAPOPTIONS = "MapboxMapOptions";
// Save instance state keys
+ public static final String STATE_HAS_SAVED_STATE = "savedState";
public static final String STATE_CAMERA_POSITION = "cameraPosition";
public static final String STATE_ZOOM_ENABLED = "zoomEnabled";
public static final String STATE_SCROLL_ENABLED = "scrollEnabled";
@@ -91,4 +102,8 @@ public class MapboxConstants {
public static final String STATE_ATTRIBUTION_ENABLED = "atrrEnabled";
public static final String TAG = "MapboxMap";
+
+ public static final String MAPBOX_SHARED_PREFERENCES_FILE = "MapboxSharedPreferences";
+ public static final String MAPBOX_SHARED_PREFERENCE_KEY_VENDORID = "mapboxVendorId";
+ public static final String MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_ENABLED = "mapboxTelemetryEnabled";
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
index 2374a98fc1..383a85417c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
@@ -3,7 +3,7 @@ package com.mapbox.mapboxsdk.constants;
import android.support.annotation.IntDef;
import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.UserLocationView;
+import com.mapbox.mapboxsdk.maps.widgets.UserLocationView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java
index a8008d3742..9b0ae7285e 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java
@@ -3,7 +3,7 @@ package com.mapbox.mapboxsdk.constants;
import android.support.annotation.IntDef;
import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.UserLocationView;
+import com.mapbox.mapboxsdk.maps.widgets.UserLocationView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TelemetryServiceNotConfiguredException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TelemetryServiceNotConfiguredException.java
new file mode 100644
index 0000000000..54908aa58b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TelemetryServiceNotConfiguredException.java
@@ -0,0 +1,18 @@
+package com.mapbox.mapboxsdk.exceptions;
+
+import android.os.Bundle;
+import com.mapbox.mapboxsdk.maps.MapView;
+
+/**
+ * A {@code TelemetryServiceNotConfiguredException} is thrown by {@link MapView} when it checks and finds that
+ * TelemetryService has not been configured in the app's AndroidManifest.xml {@link MapView#onCreate(Bundle)}
+ *
+ * @see MapView#onCreate(Bundle)
+ */
+public class TelemetryServiceNotConfiguredException extends RuntimeException {
+
+ public TelemetryServiceNotConfiguredException() {
+ super("Using Mapbox Android SDK requires configuring TelemetryService. See the INSTALL.md");
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java
index 349120d1f3..de562c3632 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java
@@ -67,6 +67,7 @@ public class VisibleRegion implements Parcelable {
* If the other object is actually a pointer to this object,
* or if all four corners and the bounds of the two objects are the same,
* this method returns true. Otherwise, this method returns false.
+ *
* @param o The Object to compare with.
* @return true if both objects are the same object.
*/
@@ -89,7 +90,15 @@ public class VisibleRegion implements Parcelable {
@Override
public String toString() {
- return "[farLeft [" + farLeft + "], farRight [" + farRight + "], nearLeft [" + nearLeft + "], nearRight [" + nearRight + "], latLngBounds ["+latLngBounds+"]]";
+ return "[farLeft [" + farLeft + "], farRight [" + farRight + "], nearLeft [" + nearLeft + "], nearRight [" + nearRight + "], latLngBounds [" + latLngBounds + "]]";
+ }
+
+ @Override
+ public int hashCode() {
+ return ((farLeft.hashCode() + 90)
+ + ((farRight.hashCode() + 90) * 1000)
+ + ((nearLeft.hashCode() + 180) * 1000000)
+ + ((nearRight.hashCode() + 180) * 1000000000));
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationListener.java
index c385820423..f787085d2f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationListener.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationListener.java
@@ -2,12 +2,15 @@ package com.mapbox.mapboxsdk.location;
import android.location.Location;
+/**
+ * Callback interface for when a location change occurs.
+ */
public interface LocationListener {
/**
- * Callback method for receiving location updates from LocationServices.
+ * Callback method for receiving location updates from LocationService.
* @param location The new Location data
*/
- public void onLocationChanged(Location location);
+ void onLocationChanged(Location location);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationService.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationService.java
new file mode 100644
index 0000000000..f459b5ad53
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationService.java
@@ -0,0 +1,166 @@
+package com.mapbox.mapboxsdk.location;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.location.Location;
+import android.location.LocationManager;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+
+import com.mapbox.mapboxsdk.telemetry.TelemetryLocationReceiver;
+import com.mapzen.android.lost.api.LocationRequest;
+import com.mapzen.android.lost.api.LostApiClient;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LocationService implements com.mapzen.android.lost.api.LocationListener {
+
+ private static final String TAG = "LocationService";
+
+ private static LocationService instance;
+
+ private Context context;
+ private LostApiClient locationClient;
+ private Location lastLocation;
+
+ private List<LocationListener> locationListeners;
+
+ private boolean isGPSEnabled;
+
+ /**
+ * Private constructor for singleton LocationService
+ */
+ private LocationService(Context context) {
+ super();
+ this.context = context;
+ // Setup location services
+ locationClient = new LostApiClient.Builder(context).build();
+ locationListeners = new ArrayList<>();
+ }
+
+ /**
+ * Primary (singleton) access method for LocationService
+ *
+ * @param context Context
+ * @return LocationService
+ */
+ public static LocationService getInstance(@NonNull final Context context) {
+ if (instance == null) {
+ instance = new LocationService(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 ((ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) &&
+ (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
+ Log.w(TAG, "Location Permissions Not Granted Yet. Try again after requesting.");
+ return;
+ }
+
+ // Disconnect
+ if (locationClient.isConnected()) {
+ // Disconnect first to ensure that the new requests are GPS
+ com.mapzen.android.lost.api.LocationServices.FusedLocationApi.removeLocationUpdates(this);
+ locationClient.disconnect();
+ }
+
+ // Setup Fresh
+ locationClient.connect();
+ Location lastLocation = com.mapzen.android.lost.api.LocationServices.FusedLocationApi.getLastLocation();
+ if (lastLocation != null) {
+ this.lastLocation = lastLocation;
+ }
+
+ LocationRequest locationRequest;
+
+ if (enableGPS) {
+ // LocationRequest Tuned for GPS
+ locationRequest = LocationRequest.create()
+ .setFastestInterval(1000)
+ .setSmallestDisplacement(3.0f)
+ .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
+
+ com.mapzen.android.lost.api.LocationServices.FusedLocationApi.requestLocationUpdates(locationRequest, this);
+ } else {
+ // LocationRequest Tuned for PASSIVE
+ locationRequest = LocationRequest.create()
+ .setFastestInterval(1000)
+ .setSmallestDisplacement(3.0f)
+ .setPriority(LocationRequest.PRIORITY_NO_POWER);
+
+ com.mapzen.android.lost.api.LocationServices.FusedLocationApi.requestLocationUpdates(locationRequest, this);
+ }
+
+ isGPSEnabled = enableGPS;
+ }
+
+ /**
+ * Returns if the GPS sensor is currently enabled
+ *
+ * @return active state of the GPS
+ */
+ public boolean isGPSEnabled() {
+ return isGPSEnabled;
+ }
+
+ /**
+ * Called when the location has changed.
+ *
+ * @param location The updated location
+ */
+ @Override
+ public void onLocationChanged(Location location) {
+ Log.i(TAG, "onLocationChanged()..." + location);
+ this.lastLocation = location;
+
+ // Update Listeners
+ for (LocationListener listener : this.locationListeners) {
+ listener.onLocationChanged(location);
+ }
+
+ // Update the Telemetry Receiver
+ Intent locIntent = new Intent(TelemetryLocationReceiver.INTENT_STRING);
+ locIntent.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
+ context.sendBroadcast(locIntent);
+ }
+
+ /**
+ * 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) {
+ if (!this.locationListeners.contains(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);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java
deleted file mode 100644
index ac2ab64076..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationServices.java
+++ /dev/null
@@ -1,133 +0,0 @@
-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) {
- if(!this.locationListeners.contains(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);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java
index 2789e85ed8..4b5aa0cbaf 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java
@@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.maps;
import android.app.Fragment;
import android.os.Bundle;
+import android.os.Handler;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
@@ -29,18 +30,10 @@ public class MapFragment extends Fragment {
private MapView mMap;
- public static MapFragment newInstance(){
+ public static MapFragment newInstance() {
return new MapFragment();
}
- public static MapFragment newInstance(MapboxMapOptions mapboxMapOptions) {
- final MapFragment mapFragment = new MapFragment();
- Bundle bundle = new Bundle();
- bundle.putParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS, mapboxMapOptions);
- mapFragment.setArguments(bundle);
- return mapFragment;
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
@@ -94,7 +87,12 @@ public class MapFragment extends Fragment {
}
@NonNull
- public void getMapAsync(@NonNull OnMapReadyCallback onMapReadyCallback){
- mMap.getMapAsync(onMapReadyCallback);
+ public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ mMap.getMapAsync(onMapReadyCallback);
+ }
+ });
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
index bd0a010900..cb9459d5f2 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
@@ -10,7 +10,9 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -27,8 +29,8 @@ import android.support.annotation.FloatRange;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.annotation.RequiresPermission;
import android.support.annotation.UiThread;
+import android.support.v4.content.ContextCompat;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.ScaleGestureDetectorCompat;
import android.support.v7.app.AlertDialog;
@@ -47,44 +49,50 @@ import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewConfiguration;
+import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.ListView;
import android.widget.ZoomButtonsController;
-
import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector;
import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector;
import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.InfoWindow;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.annotations.Polygon;
-import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.annotations.Polyline;
-import com.mapbox.mapboxsdk.annotations.PolylineOptions;
-import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.exceptions.IconBitmapChangedException;
import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException;
+import com.mapbox.mapboxsdk.exceptions.TelemetryServiceNotConfiguredException;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.layers.CustomLayer;
+import com.mapbox.mapboxsdk.maps.widgets.CompassView;
+import com.mapbox.mapboxsdk.maps.widgets.UserLocationView;
+import com.mapbox.mapboxsdk.telemetry.MapboxEvent;
+import com.mapbox.mapboxsdk.telemetry.MapboxEventManager;
import com.mapbox.mapboxsdk.utils.ApiAccess;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* <p>
@@ -111,7 +119,6 @@ public class MapView extends FrameLayout {
private static final float DIMENSION_SEVENTYSIX_DP = 76f;
private MapboxMap mMapboxMap;
- private List<Annotation> mAnnotations;
private List<Icon> mIcons;
private NativeMapView mNativeMapView;
@@ -120,7 +127,7 @@ public class MapView extends FrameLayout {
private ImageView mAttributionsView;
private UserLocationView mUserLocationView;
- private List<OnMapChangedListener> mOnMapChangedListener;
+ private CopyOnWriteArrayList<OnMapChangedListener> mOnMapChangedListener;
private ZoomButtonsController mZoomButtonsController;
private ConnectivityReceiver mConnectivityReceiver;
private float mScreenDensity = 1.0f;
@@ -133,13 +140,12 @@ public class MapView extends FrameLayout {
private boolean mTwoTap = false;
private boolean mZoomStarted = false;
private boolean mQuickZoom = false;
+ private boolean mScrollInProgress = false;
- /*
private int mContentPaddingLeft;
private int mContentPaddingTop;
private int mContentPaddingRight;
private int mContentPaddingBottom;
-*/
@UiThread
public MapView(@NonNull Context context) {
@@ -160,11 +166,9 @@ public class MapView extends FrameLayout {
}
private void initialize(@NonNull Context context, @Nullable AttributeSet attrs) {
- mOnMapChangedListener = new ArrayList<>();
+ mOnMapChangedListener = new CopyOnWriteArrayList<>();
mMapboxMap = new MapboxMap(this);
- mAnnotations = new ArrayList<>();
mIcons = new ArrayList<>();
-
View view = LayoutInflater.from(context).inflate(R.layout.mapview_internal, this);
if (!isInEditMode()) {
@@ -200,7 +204,7 @@ public class MapView extends FrameLayout {
// Shows the zoom controls
if (!context.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)) {
- mMapboxMap.setZoomControlsEnabled(true);
+ mMapboxMap.getUiSettings().setZoomControlsEnabled(true);
}
mZoomButtonsController = new ZoomButtonsController(this);
mZoomButtonsController.setZoomSpeed(MapboxConstants.ANIMATION_DURATION);
@@ -210,9 +214,9 @@ public class MapView extends FrameLayout {
onConnectivityChanged(isConnected());
mUserLocationView = (UserLocationView) view.findViewById(R.id.userLocationView);
- mUserLocationView.setMapView(this);
+ mUserLocationView.setMapboxMap(mMapboxMap);
mCompassView = (CompassView) view.findViewById(R.id.compassView);
- mCompassView.setOnClickListener(new CompassView.CompassClickListener(this));
+ mCompassView.setOnClickListener(new CompassView.CompassClickListener(mMapboxMap));
mLogoView = (ImageView) view.findViewById(R.id.logoView);
// Setup Attributions control
@@ -232,13 +236,15 @@ public class MapView extends FrameLayout {
mMapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
// Access token
- if (typedArray.getString(R.styleable.MapView_access_token) != null) {
+ String accessToken = typedArray.getString(R.styleable.MapView_access_token);
+ if (accessToken != null) {
setAccessToken(typedArray.getString(R.styleable.MapView_access_token));
}
// Style url
- if (typedArray.getString(R.styleable.MapView_style_url) != null) {
- mMapboxMap.setStyleUrl(typedArray.getString(R.styleable.MapView_style_url));
+ String styleUrl = typedArray.getString(R.styleable.MapView_style_url);
+ if (styleUrl != null) {
+ mMapboxMap.setStyleUrl(styleUrl);
}
// Enable gestures
@@ -249,6 +255,10 @@ public class MapView extends FrameLayout {
uiSettings.setTiltGesturesEnabled(typedArray.getBoolean(R.styleable.MapView_tilt_enabled, true));
uiSettings.setZoomControlsEnabled(typedArray.getBoolean(R.styleable.MapView_zoom_controls_enabled, false));
+ // Zoom
+ uiSettings.setMaxZoom(typedArray.getFloat(R.styleable.MapView_zoom_max, (float) MapboxConstants.MAXIMUM_ZOOM));
+ uiSettings.setMinZoom(typedArray.getFloat(R.styleable.MapView_zoom_min, (float) MapboxConstants.MINIMUM_ZOOM));
+
// Compass
uiSettings.setCompassEnabled(typedArray.getBoolean(R.styleable.MapView_compass_enabled, true));
uiSettings.setCompassGravity(typedArray.getInt(R.styleable.MapView_compass_gravity, Gravity.TOP | Gravity.END));
@@ -274,13 +284,7 @@ public class MapView extends FrameLayout {
, (int) (typedArray.getDimension(R.styleable.MapView_attribution_margin_bottom, DIMENSION_SEVEN_DP) * mScreenDensity));
// User location
- try {
- //noinspection ResourceType
- mMapboxMap.setMyLocationEnabled(typedArray.getBoolean(R.styleable.MapView_my_location_enabled, false));
- } catch (SecurityException ignore) {
- // User did not accept location permissions
- }
-
+ mMapboxMap.setMyLocationEnabled(typedArray.getBoolean(R.styleable.MapView_my_location_enabled, false));
} finally {
typedArray.recycle();
}
@@ -303,7 +307,7 @@ public class MapView extends FrameLayout {
*/
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
- if (savedInstanceState != null) {
+ if (savedInstanceState != null && savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) {
// Get previous camera position
CameraPosition cameraPosition = savedInstanceState.getParcelable(MapboxConstants.STATE_CAMERA_POSITION);
@@ -357,10 +361,17 @@ public class MapView extends FrameLayout {
// User did not accept location permissions
}
+ TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
//noinspection ResourceType
- mMapboxMap.setMyLocationTrackingMode(savedInstanceState.getInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, MyLocationTracking.TRACKING_NONE));
+ trackingSettings.setMyLocationTrackingMode(savedInstanceState.getInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, MyLocationTracking.TRACKING_NONE));
//noinspection ResourceType
- mMapboxMap.setMyBearingTrackingMode(savedInstanceState.getInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, MyBearingTracking.NONE));
+ trackingSettings.setMyBearingTrackingMode(savedInstanceState.getInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, MyBearingTracking.NONE));
+ } else {
+ // Force a check for Telemetry
+ validateTelemetryServiceConfigured();
+
+ // Start Telemetry (authorization determined in initial MapboxEventManager constructor)
+ MapboxEventManager.getMapboxEventManager(getContext()).isTelemetryEnabled();
}
// Force a check for an access token
@@ -381,6 +392,16 @@ public class MapView extends FrameLayout {
}
}
});
+
+ // Fire MapLoad
+ if (savedInstanceState == null) {
+ Hashtable<String, Object> evt = new Hashtable<>();
+ evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_LOAD);
+ evt.put(MapboxEvent.KEY_LATITUDE, mMapboxMap.getCameraPosition().target.getLatitude());
+ evt.put(MapboxEvent.KEY_LONGITUDE, mMapboxMap.getCameraPosition().target.getLongitude());
+ evt.put(MapboxEvent.KEY_ZOOM, mMapboxMap.getCameraPosition().zoom);
+ MapboxEventManager.getMapboxEventManager(getContext()).pushEvent(evt);
+ }
}
/**
@@ -392,14 +413,19 @@ public class MapView extends FrameLayout {
@UiThread
public void onSaveInstanceState(@NonNull Bundle outState) {
+ outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true);
outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, mMapboxMap.getCameraPosition());
outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, mMapboxMap.isDebugActive());
outState.putString(MapboxConstants.STATE_STYLE_URL, mMapboxMap.getStyleUrl());
outState.putString(MapboxConstants.STATE_ACCESS_TOKEN, mMapboxMap.getAccessToken());
outState.putLong(MapboxConstants.STATE_DEFAULT_TRANSITION_DURATION, mNativeMapView.getDefaultTransitionDuration());
outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED, mMapboxMap.isMyLocationEnabled());
- outState.putInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, mMapboxMap.getMyLocationTrackingMode());
- outState.putInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, mMapboxMap.getMyBearingTrackingMode());
+
+
+ // TrackingSettings
+ TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
+ outState.putInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, trackingSettings.getMyLocationTrackingMode());
+ outState.putInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, trackingSettings.getMyBearingTrackingMode());
// UiSettings
UiSettings uiSettings = mMapboxMap.getUiSettings();
@@ -554,13 +580,13 @@ public class MapView extends FrameLayout {
}
//
- // Rotation
+ // Direction
//
/**
- * Returns the current heading of the map relative to true north.
+ * Returns the current direction of the map relative to true north.
*
- * @return The current heading measured in degrees.
+ * @return The current direction measured in degrees.
*/
@UiThread
@FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, to = MapboxConstants.MAXIMUM_DIRECTION)
@@ -633,6 +659,46 @@ public class MapView extends FrameLayout {
}
//
+ // Content padding
+ //
+
+ /**
+ * Return The current content padding left of the map view viewport.
+ *
+ * @return The current content padding left
+ */
+ int getContentPaddingLeft() {
+ return mContentPaddingLeft;
+ }
+
+ /**
+ * Return The current content padding left of the map view viewport.
+ *
+ * @return The current content padding left
+ */
+ int getContentPaddingTop() {
+ return mContentPaddingTop;
+ }
+
+ /**
+ * Return The current content padding left of the map view viewport.
+ *
+ * @return The current content padding right
+ */
+ int getContentPaddingRight() {
+ return mContentPaddingRight;
+ }
+
+ /**
+ * Return The current content padding left of the map view viewport.
+ *
+ * @return The current content padding bottom
+ */
+ int getContentPaddingBottom() {
+ return mContentPaddingBottom;
+ }
+
+ //
// Zoom
//
@@ -647,51 +713,6 @@ public class MapView extends FrameLayout {
return mNativeMapView.getZoom();
}
-// /**
-// * Return The current content padding left of the map view viewport.
-// *
-// * @return The current content padding left
-// */
-///*
-// public int getContentPaddingLeft() {
-// return mContentPaddingLeft;
-// }
-//*/
-//
-// /**
-// * Return The current content padding left of the map view viewport.
-// *
-// * @return The current content padding left
-// */
-///*
-// public int getContentPaddingTop() {
-// return mContentPaddingTop;
-// }
-//*/
-//
-// /**
-// * Return The current content padding left of the map view viewport.
-// *
-// * @return The current content padding left
-// */
-///*
-// public int getContentPaddingRight() {
-// return mContentPaddingRight;
-// }
-//*/
-//
-// /**
-// * Return The current content padding left of the map view viewport.
-// *
-// * @param zoomLevel The new zoom level.
-// * @param animated If true, animates the change. If false, immediately changes the map.
-// * @see MapboxMap#MAXIMUM_ZOOM
-// */
-///*
-// public int getContentPaddingBottom() {
-// return mContentPaddingBottom;
-//*/
-
/**
* <p>
* Sets the minimum zoom level the map can be displayed at.
@@ -712,7 +733,7 @@ public class MapView extends FrameLayout {
* @return The minimum zoom level.
*/
@UiThread
- public double getMinZoom() {
+ double getMinZoom() {
return mNativeMapView.getMinZoom();
}
@@ -724,7 +745,7 @@ public class MapView extends FrameLayout {
* @param maxZoom The new maximum zoom level.
*/
@UiThread
- public void setMaxZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) {
+ void setMaxZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) {
mNativeMapView.setMaxZoom(maxZoom);
}
@@ -736,7 +757,7 @@ public class MapView extends FrameLayout {
* @return The maximum zoom level.
*/
@UiThread
- public double getMaxZoom() {
+ double getMaxZoom() {
return mNativeMapView.getMaxZoom();
}
@@ -866,6 +887,28 @@ public class MapView extends FrameLayout {
}
}
+ // Checks that TelemetryService has been configured by developer
+ private void validateTelemetryServiceConfigured() {
+
+ try {
+ // Check Implementing app's AndroidManifest.xml
+ PackageInfo packageInfo = getContext().getPackageManager().getPackageInfo(getContext().getPackageName(), PackageManager.GET_SERVICES);
+
+ if (packageInfo.services != null) {
+
+ for (ServiceInfo service : packageInfo.services) {
+ if (TextUtils.equals("com.mapbox.mapboxsdk.telemetry.TelemetryService", service.name)) {
+ return;
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ Log.w(TAG, "Error checking for Telemetry Service Config: " + e);
+ }
+ throw new TelemetryServiceNotConfiguredException();
+ }
+
/**
* <p>
* Sets the current Mapbox access token used to load map styles and tiles.
@@ -942,7 +985,25 @@ public class MapView extends FrameLayout {
// Annotations
//
- private void loadIcon(Icon icon) {
+ Icon loadIconForMarker(Marker marker) {
+ Icon icon = marker.getIcon();
+ if (icon == null) {
+ icon = IconFactory.getInstance(getContext()).defaultMarker();
+ marker.setIcon(icon);
+ }
+ if (!mIcons.contains(icon)) {
+ mIcons.add(icon);
+ loadIcon(icon);
+ } else {
+ Icon oldIcon = mIcons.get(mIcons.indexOf(icon));
+ if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) {
+ throw new IconBitmapChangedException();
+ }
+ }
+ return icon;
+ }
+
+ void loadIcon(Icon icon) {
Bitmap bitmap = icon.getBitmap();
String id = icon.getId();
if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) {
@@ -964,7 +1025,7 @@ public class MapView extends FrameLayout {
scale, buffer.array());
}
- private void reloadIcons() {
+ void reloadIcons() {
int count = mIcons.size();
for (int i = 0; i < count; i++) {
Icon icon = mIcons.get(i);
@@ -972,8 +1033,35 @@ public class MapView extends FrameLayout {
}
}
+ /**
+ * <p>
+ * Updates a marker on this map. Does nothing if the marker is already added.
+ * </p>
+ *
+ * @param updatedMarker An updated marker object.
+ */
+ @UiThread
+ void updateMarker(@NonNull Marker updatedMarker) {
+ if (updatedMarker == null) {
+ Log.w(TAG, "marker was null, doing nothing");
+ return;
+ }
+
+ if (updatedMarker.getId() == -1) {
+ Log.w(TAG, "marker has an id of -1, possibly was not added yet, doing nothing");
+ }
+
+ ensureIconLoaded(updatedMarker);
+ mNativeMapView.updateMarker(updatedMarker);
+ }
+
private Marker prepareMarker(MarkerOptions markerOptions) {
Marker marker = markerOptions.getMarker();
+ ensureIconLoaded(marker);
+ return marker;
+ }
+
+ private void ensureIconLoaded(Marker marker) {
Icon icon = marker.getIcon();
if (icon == null) {
icon = IconFactory.getInstance(getContext()).defaultMarker();
@@ -988,270 +1076,48 @@ public class MapView extends FrameLayout {
throw new IconBitmapChangedException();
}
}
- marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon));
- return marker;
- }
- /**
- * <p>
- * Adds a marker to this map.
- * </p>
- * The marker's icon is rendered on the map at the location {@code Marker.position}.
- * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet.
- *
- * @param markerOptions A marker options object that defines how to render the marker.
- * @return The {@code Marker} that was added to the map.
- */
- @UiThread
- @NonNull
- Marker addMarker(@NonNull MarkerOptions markerOptions) {
- if (markerOptions == null) {
- Log.w(TAG, "markerOptions was null, so just returning null");
- return null;
+ // this seems to be a costly operation according to the profiler so I'm trying to save some calls
+ Marker previousMarker = marker.getId() != -1 ? (Marker) mMapboxMap.getAnnotation(marker.getId()) : null;
+ if (previousMarker == null || previousMarker.getIcon() == null || previousMarker.getIcon() != marker.getIcon()) {
+ marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon));
}
-
- Marker marker = prepareMarker(markerOptions);
- long id = mNativeMapView.addMarker(marker);
- marker.setId(id); // the annotation needs to know its id
- marker.setMapboxMap(mMapboxMap); // the annotation needs to know which map view it is in
- mAnnotations.add(marker);
- return marker;
}
- /**
- * <p>
- * Adds multiple markers to this map.
- * </p>
- * The marker's icon is rendered on the map at the location {@code Marker.position}.
- * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet.
- *
- * @param markerOptionsList A list of marker options objects that defines how to render the markers.
- * @return A list of the {@code Marker}s that were added to the map.
- */
- @UiThread
- @NonNull
- List<Marker> addMarkers(@NonNull List<MarkerOptions> markerOptionsList) {
- if (markerOptionsList == null) {
- Log.w(TAG, "markerOptionsList was null, so just returning null");
- return null;
- }
-
- int count = markerOptionsList.size();
- List<Marker> markers = new ArrayList<>(count);
- for (int i = 0; i < count; i++) {
- MarkerOptions markerOptions = markerOptionsList.get(i);
- Marker marker = prepareMarker(markerOptions);
- markers.add(marker);
- }
-
- long[] ids = mNativeMapView.addMarkers(markers);
- Marker m;
- for (int i = 0; i < count; i++) {
- m = markers.get(i);
- m.setId(ids[i]);
- m.setMapboxMap(mMapboxMap);
- mAnnotations.add(m);
+ long addMarker(@NonNull Marker marker) {
+ if (mNativeMapView == null) {
+ return 0l;
}
-
- return new ArrayList<>(markers);
+ return mNativeMapView.addMarker(marker);
}
- /**
- * Adds a polyline to this map.
- *
- * @param polylineOptions A polyline options object that defines how to render the polyline.
- * @return The {@code Polyine} that was added to the map.
- */
- @UiThread
- @NonNull
- Polyline addPolyline(@NonNull PolylineOptions polylineOptions) {
- if (polylineOptions == null) {
- Log.w(TAG, "polylineOptions was null, so just returning null");
- return null;
- }
-
- Polyline polyline = polylineOptions.getPolyline();
- long id = mNativeMapView.addPolyline(polyline);
- polyline.setId(id);
- polyline.setMapboxMap(mMapboxMap);
- mAnnotations.add(polyline);
- return polyline;
+ long[] addMarkers(@NonNull List<Marker> markerList) {
+ return mNativeMapView.addMarkers(markerList);
}
- /**
- * Adds multiple polylines to this map.
- *
- * @param polylineOptionsList A list of polyline options objects that defines how to render the polylines.
- * @return A list of the {@code Polyline}s that were added to the map.
- */
- @UiThread
- @NonNull
- List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList) {
- if (polylineOptionsList == null) {
- Log.w(TAG, "polylineOptionsList was null, so just returning null");
- return null;
- }
-
- int count = polylineOptionsList.size();
- List<Polyline> polylines = new ArrayList<>(count);
- for (PolylineOptions options : polylineOptionsList) {
- polylines.add(options.getPolyline());
- }
-
- long[] ids = mNativeMapView.addPolylines(polylines);
-
- Polyline p;
- for (int i = 0; i < count; i++) {
- p = polylines.get(i);
- p.setId(ids[i]);
- p.setMapboxMap(mMapboxMap);
- mAnnotations.add(p);
- }
-
- return new ArrayList<>(polylines);
+ long addPolyline(@NonNull Polyline polyline) {
+ return mNativeMapView.addPolyline(polyline);
}
- /**
- * Adds a polygon to this map.
- *
- * @param polygonOptions A polygon options object that defines how to render the polygon.
- * @return The {@code Polygon} that was added to the map.
- */
- @UiThread
- @NonNull
- Polygon addPolygon(@NonNull PolygonOptions polygonOptions) {
- if (polygonOptions == null) {
- Log.w(TAG, "polygonOptions was null, so just returning null");
- return null;
- }
-
- Polygon polygon = polygonOptions.getPolygon();
- long id = mNativeMapView.addPolygon(polygon);
- polygon.setId(id);
- polygon.setMapboxMap(mMapboxMap);
- mAnnotations.add(polygon);
- return polygon;
+ long[] addPolylines(@NonNull List<Polyline> polylines) {
+ return mNativeMapView.addPolylines(polylines);
}
-
- /**
- * Adds multiple polygons to this map.
- *
- * @param polygonOptionsList A list of polygon options objects that defines how to render the polygons.
- * @return A list of the {@code Polygon}s that were added to the map.
- */
- @UiThread
- @NonNull
- List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList) {
- if (polygonOptionsList == null) {
- Log.w(TAG, "polygonOptionsList was null, so just returning null");
- return null;
- }
-
- int count = polygonOptionsList.size();
- List<Polygon> polygons = new ArrayList<>(count);
- for (PolygonOptions polygonOptions : polygonOptionsList) {
- polygons.add(polygonOptions.getPolygon());
- }
-
- long[] ids = mNativeMapView.addPolygons(polygons);
-
- Polygon p;
- for (int i = 0; i < count; i++) {
- p = polygons.get(i);
- p.setId(ids[i]);
- p.setMapboxMap(mMapboxMap);
- mAnnotations.add(p);
- }
-
- return new ArrayList<>(polygons);
+ long addPolygon(@NonNull Polygon polygon) {
+ return mNativeMapView.addPolygon(polygon);
}
-
- /**
- * <p>
- * Convenience method for removing a Marker from the map.
- * </p>
- * Calls removeAnnotation() internally
- *
- * @param marker Marker to remove
- */
- @UiThread
- void removeMarker(@NonNull Marker marker) {
- removeAnnotation(marker);
+ long[] addPolygons(@NonNull List<Polygon> polygons) {
+ return mNativeMapView.addPolygons(polygons);
}
- /**
- * Removes an annotation from the map.
- *
- * @param annotation The annotation object to remove.
- */
- @UiThread
- void removeAnnotation(@NonNull Annotation annotation) {
- if (annotation == null) {
- Log.w(TAG, "annotation was null, so just returning");
- return;
- }
-
- if (annotation instanceof Marker) {
- ((Marker) annotation).hideInfoWindow();
- }
- long id = annotation.getId();
+ void removeAnnotation(long id) {
mNativeMapView.removeAnnotation(id);
- mAnnotations.remove(annotation);
- }
-
- /**
- * Removes multiple annotations from the map.
- *
- * @param annotationList A list of annotation objects to remove.
- */
- @UiThread
- void removeAnnotations(@NonNull List<? extends Annotation> annotationList) {
- if (annotationList == null) {
- Log.w(TAG, "annotationList was null, so just returning");
- return;
- }
-
- int count = annotationList.size();
- long[] ids = new long[count];
- for (int i = 0; i < count; i++) {
- ids[i] = annotationList.get(i).getId();
- }
- mNativeMapView.removeAnnotations(ids);
}
- /**
- * Removes all annotations from the map.
- */
- @UiThread
- void removeAllAnnotations() {
- int count = mAnnotations.size();
- long[] ids = new long[mAnnotations.size()];
-
- for (int i = 0; i < count; i++) {
- Annotation annotation = mAnnotations.get(i);
- long id = annotation.getId();
- ids[i] = id;
- if (annotation instanceof Marker) {
- ((Marker) annotation).hideInfoWindow();
- }
- }
-
+ void removeAnnotations(@NonNull long[] ids) {
mNativeMapView.removeAnnotations(ids);
- mAnnotations.clear();
- }
-
- /**
- * Returns a list of all the annotations on the map.
- *
- * @return A list of all the annotation objects. The returned object is a copy so modifying this
- * list will not update the map.
- */
- @NonNull
- List<Annotation> getAllAnnotations() {
- return new ArrayList<>(mAnnotations);
}
private List<Marker> getMarkersInBounds(@NonNull LatLngBounds bbox) {
@@ -1269,9 +1135,10 @@ public class MapView extends FrameLayout {
}
List<Marker> annotations = new ArrayList<>(ids.length);
- int count = mAnnotations.size();
+ List<Annotation> annotationList = mMapboxMap.getAnnotations();
+ int count = annotationList.size();
for (int i = 0; i < count; i++) {
- Annotation annotation = mAnnotations.get(i);
+ Annotation annotation = annotationList.get(i);
if (annotation instanceof Marker && idsList.contains(annotation.getId())) {
annotations.add((Marker) annotation);
}
@@ -1280,7 +1147,7 @@ public class MapView extends FrameLayout {
return new ArrayList<>(annotations);
}
- private int getTopOffsetPixelsForIcon(Icon icon) {
+ int getTopOffsetPixelsForIcon(Icon icon) {
// This method will dead lock if map paused. Causes a freeze if you add a marker in an
// activity's onCreate()
if (mNativeMapView.isPaused()) {
@@ -1292,6 +1159,35 @@ public class MapView extends FrameLayout {
}
/**
+ * Sets the distance from the edges of the map view’s frame to the edges of the map
+ * view’s logical viewport.
+ * <p/>
+ * When the value of this property is equal to {0,0,0,0}, viewport
+ * properties such as `centerCoordinate` assume a viewport that matches the map
+ * view’s frame. Otherwise, those properties are inset, excluding part of the
+ * frame from the viewport. For instance, if the only the top edge is inset, the
+ * map center is effectively shifted downward.
+ *
+ * @param left The left margin in pixels.
+ * @param top The top margin in pixels.
+ * @param right The right margin in pixels.
+ * @param bottom The bottom margin in pixels.
+ */
+ @UiThread
+ void setContentPadding(int left, int top, int right, int bottom) {
+ if (left == mContentPaddingLeft && top == mContentPaddingTop && right == mContentPaddingRight && bottom == mContentPaddingBottom) {
+ return;
+ }
+
+ mContentPaddingLeft = left;
+ mContentPaddingTop = top;
+ mContentPaddingRight = right;
+ mContentPaddingBottom = bottom;
+
+ mNativeMapView.setContentPadding(top / mScreenDensity, left / mScreenDensity, bottom / mScreenDensity, right / mScreenDensity);
+ }
+
+ /**
* <p>
* Returns the distance spanned by one pixel at the specified latitude and current zoom level.
* </p>
@@ -1400,9 +1296,10 @@ public class MapView extends FrameLayout {
}
private void adjustTopOffsetPixels() {
- int count = mAnnotations.size();
+ List<Annotation> annotations = mMapboxMap.getAnnotations();
+ int count = annotations.size();
for (int i = 0; i < count; i++) {
- Annotation annotation = mAnnotations.get(i);
+ Annotation annotation = annotations.get(i);
if (annotation instanceof Marker) {
Marker marker = (Marker) annotation;
marker.setTopOffsetPixels(
@@ -1419,9 +1316,10 @@ public class MapView extends FrameLayout {
}
private void reloadMarkers() {
- int count = mAnnotations.size();
+ List<Annotation> annotations = mMapboxMap.getAnnotations();
+ int count = annotations.size();
for (int i = 0; i < count; i++) {
- Annotation annotation = mAnnotations.get(i);
+ Annotation annotation = annotations.get(i);
if (annotation instanceof Marker) {
Marker marker = (Marker) annotation;
mNativeMapView.removeAnnotation(annotation.getId());
@@ -1572,6 +1470,26 @@ public class MapView extends FrameLayout {
// Touch events
//
+ /**
+ * Helper method for tracking gesture events
+ * @param gestureId Type of Gesture See {@see MapboxEvent#GESTURE_SINGLETAP MapboxEvent#GESTURE_DOUBLETAP MapboxEvent#GESTURE_TWO_FINGER_SINGLETAP MapboxEvent#GESTURE_QUICK_ZOOM MapboxEvent#GESTURE_PAN_START MapboxEvent#GESTURE_PINCH_START MapboxEvent#GESTURE_ROTATION_START MapboxEvent#GESTURE_PITCH_START}
+ * @param xCoordinate Original x screen coordinate at start of gesture
+ * @param yCoordinate Original y screen cooridnate at start of gesture
+ */
+ private void trackGestureEvent(@NonNull String gestureId, @NonNull float xCoordinate, float yCoordinate) {
+
+ LatLng tapLatLng = fromScreenLocation(new PointF(xCoordinate, yCoordinate));
+
+ Hashtable<String, Object> evt = new Hashtable<>();
+ evt.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_MAP_CLICK);
+ evt.put(MapboxEvent.KEY_GESTURE_ID, gestureId);
+ evt.put(MapboxEvent.KEY_LATITUDE, tapLatLng.getLatitude());
+ evt.put(MapboxEvent.KEY_LONGITUDE, tapLatLng.getLongitude());
+ evt.put(MapboxEvent.KEY_ZOOM, mMapboxMap.getCameraPosition().zoom);
+
+ MapboxEventManager.getMapboxEventManager(getContext()).pushEvent(evt);
+ }
+
// Called when user touches the screen, all positions are absolute
@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
@@ -1596,6 +1514,10 @@ public class MapView extends FrameLayout {
case MotionEvent.ACTION_POINTER_DOWN:
// Second pointer down
mTwoTap = event.getPointerCount() == 2;
+ if (mTwoTap) {
+ // Confirmed 2nd Finger Down
+ trackGestureEvent(MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, event.getX(), event.getY());
+ }
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -1617,6 +1539,12 @@ public class MapView extends FrameLayout {
return true;
}
+ // Scroll / Pan Has Stopped
+ if (mScrollInProgress) {
+ trackGestureEvent(MapboxEvent.TYPE_MAP_DRAGEND, event.getX(), event.getY());
+ mScrollInProgress = false;
+ }
+
mTwoTap = false;
mNativeMapView.setGestureInProgress(false);
break;
@@ -1632,8 +1560,7 @@ public class MapView extends FrameLayout {
}
// This class handles one finger gestures
- private class GestureListener extends
- GestureDetector.SimpleOnGestureListener {
+ private class GestureListener extends GestureDetector.SimpleOnGestureListener {
// Must always return true otherwise all events are ignored
@Override
@@ -1665,16 +1592,20 @@ public class MapView extends FrameLayout {
}
// Single finger double tap
- if (mUserLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE) {
+ if (mMapboxMap.getTrackingSettings().isLocationTrackingDisabled()) {
// Zoom in on gesture
zoom(true, e.getX(), e.getY());
+ trackGestureEvent(MapboxEvent.GESTURE_QUICK_ZOOM, e.getX(), e.getY());
} else {
- // Zoom in on center map
- zoom(true, getWidth() / 2, getHeight() / 2);
+ // Zoom in on user location view
+ PointF centerPoint = mUserLocationView.getMarkerScreenPoint();
+ zoom(true, centerPoint.x, centerPoint.y);
}
break;
}
+ trackGestureEvent(MapboxEvent.GESTURE_DOUBLETAP, e.getX(), e.getY());
+
return true;
}
@@ -1725,9 +1656,10 @@ public class MapView extends FrameLayout {
}
if (newSelectedMarkerId >= 0) {
- int count = mAnnotations.size();
+ List<Annotation> annotations = mMapboxMap.getAnnotations();
+ int count = annotations.size();
for (int i = 0; i < count; i++) {
- Annotation annotation = mAnnotations.get(i);
+ Annotation annotation = annotations.get(i);
if (annotation instanceof Marker) {
if (annotation.getId() == newSelectedMarkerId) {
if (selectedMarkers.isEmpty() || !selectedMarkers.contains(annotation)) {
@@ -1749,6 +1681,8 @@ public class MapView extends FrameLayout {
}
}
+ trackGestureEvent(MapboxEvent.GESTURE_SINGLETAP, e.getX(), e.getY());
+
return true;
}
@@ -1771,7 +1705,9 @@ public class MapView extends FrameLayout {
}
// reset tracking modes if gesture occurs
- resetTrackingModes();
+ if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
+ resetTrackingModes();
+ }
// Fling the map
float ease = 0.25f;
@@ -1793,18 +1729,25 @@ public class MapView extends FrameLayout {
listener.onFling();
}
+ trackGestureEvent(MapboxEvent.GESTURE_PAN_START, e1.getX(), e1.getY());
return true;
}
// Called for drags
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ Log.i(TAG, "onScroll() started");
+ if (!mScrollInProgress) {
+ mScrollInProgress = true;
+ }
if (!mMapboxMap.getUiSettings().isScrollGesturesEnabled()) {
return false;
}
- // reset tracking modes if gesture occurs
- resetTrackingModes();
+ if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
+ // reset tracking modes if gesture occurs
+ resetTrackingModes();
+ }
// Cancel any animation
mNativeMapView.cancelTransitions();
@@ -1817,6 +1760,7 @@ public class MapView extends FrameLayout {
listener.onScroll();
}
+ Log.i(TAG, "onScroll() done");
return true;
}
}
@@ -1834,10 +1778,13 @@ public class MapView extends FrameLayout {
return false;
}
- // reset tracking modes if gesture occurs
- resetTrackingModes();
+ if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
+ // reset tracking modes if gesture occurs
+ resetTrackingModes();
+ }
mBeginTime = detector.getEventTime();
+ trackGestureEvent(MapboxEvent.GESTURE_PINCH_START, detector.getFocusX(), detector.getFocusY());
return true;
}
@@ -1853,7 +1800,8 @@ public class MapView extends FrameLayout {
// Called for pinch zooms and quickzooms/quickscales
@Override
public boolean onScale(ScaleGestureDetector detector) {
- if (!mMapboxMap.getUiSettings().isZoomGesturesEnabled()) {
+ UiSettings uiSettings = mMapboxMap.getUiSettings();
+ if (!uiSettings.isZoomGesturesEnabled()) {
return false;
}
@@ -1881,13 +1829,21 @@ public class MapView extends FrameLayout {
// Gesture is a quickzoom if there aren't two fingers
mQuickZoom = !mTwoTap;
+ TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
+
// Scale the map
- if (mMapboxMap.getUiSettings().isScrollGesturesEnabled() && !mQuickZoom && mUserLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE) {
+ if (uiSettings.isScrollGesturesEnabled() && !mQuickZoom && trackingSettings.isLocationTrackingDisabled()) {
// around gesture
mNativeMapView.scaleBy(detector.getScaleFactor(), detector.getFocusX() / mScreenDensity, detector.getFocusY() / mScreenDensity);
} else {
- // around center map
- mNativeMapView.scaleBy(detector.getScaleFactor(), (getWidth() / 2) / mScreenDensity, (getHeight() / 2) / mScreenDensity);
+ if(trackingSettings.isLocationTrackingDisabled()) {
+ // around center map
+ mNativeMapView.scaleBy(detector.getScaleFactor(), (getWidth() / 2) / mScreenDensity, (getHeight() / 2) / mScreenDensity);
+ }else {
+ // around user location view
+ PointF centerPoint = mUserLocationView.getMarkerScreenPoint();
+ mNativeMapView.scaleBy(detector.getScaleFactor(), centerPoint.x / mScreenDensity, centerPoint.y / mScreenDensity);
+ }
}
return true;
}
@@ -1907,10 +1863,13 @@ public class MapView extends FrameLayout {
return false;
}
- // reset tracking modes if gesture occurs
- resetTrackingModes();
+ if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
+ // reset tracking modes if gesture occurs
+ resetTrackingModes();
+ }
mBeginTime = detector.getEventTime();
+ trackGestureEvent(MapboxEvent.GESTURE_ROTATION_START, detector.getFocusX(), detector.getFocusY());
return true;
}
@@ -1957,16 +1916,15 @@ public class MapView extends FrameLayout {
bearing += detector.getRotationDegreesDelta();
// Rotate the map
- if (mUserLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE) {
+ if (mMapboxMap.getTrackingSettings().isLocationTrackingDisabled()) {
// around gesture
mNativeMapView.setBearing(bearing,
detector.getFocusX() / mScreenDensity,
detector.getFocusY() / mScreenDensity);
} else {
- // around center map
- mNativeMapView.setBearing(bearing,
- (getWidth() / 2) / mScreenDensity,
- (getHeight() / 2) / mScreenDensity);
+ // around center userlocation
+ PointF centerPoint = mUserLocationView.getMarkerScreenPoint();
+ mNativeMapView.setBearing(bearing, centerPoint.x / mScreenDensity, centerPoint.y / mScreenDensity);
}
return true;
}
@@ -1986,10 +1944,13 @@ public class MapView extends FrameLayout {
return false;
}
- // reset tracking modes if gesture occurs
- resetTrackingModes();
+ if (mMapboxMap.getTrackingSettings().isDismissTrackingOnGesture()) {
+ // reset tracking modes if gesture occurs
+ resetTrackingModes();
+ }
mBeginTime = detector.getEventTime();
+ trackGestureEvent(MapboxEvent.GESTURE_PITCH_START, detector.getFocusX(), detector.getFocusY());
return true;
}
@@ -2056,8 +2017,6 @@ public class MapView extends FrameLayout {
if (!mMapboxMap.getUiSettings().isZoomGesturesEnabled()) {
return;
}
-
- // Zoom in or out
zoom(zoomIn);
}
}
@@ -2408,9 +2367,11 @@ public class MapView extends FrameLayout {
// Forward to any listeners
protected void onMapChanged(int mapChange) {
if (mOnMapChangedListener != null) {
- int count = mOnMapChangedListener.size();
- for (int i = 0; i < count; i++) {
- mOnMapChangedListener.get(i).onMapChanged(mapChange);
+ OnMapChangedListener listener;
+ final Iterator<OnMapChangedListener> iterator = mOnMapChangedListener.iterator();
+ while (iterator.hasNext()) {
+ listener = iterator.next();
+ listener.onMapChanged(mapChange);
}
}
}
@@ -2419,6 +2380,11 @@ public class MapView extends FrameLayout {
// User location
//
+ boolean isPermissionsAccepted() {
+ return (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) ||
+ ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
+ }
+
/**
* <p>
* Enables or disables the my-location layer.
@@ -2433,9 +2399,6 @@ public class MapView extends FrameLayout {
* @throws SecurityException if no suitable permission is present
*/
@UiThread
- @RequiresPermission(anyOf = {
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION})
void setMyLocationEnabled(boolean enabled) {
mUserLocationView.setEnabled(enabled);
}
@@ -2473,19 +2436,13 @@ public class MapView extends FrameLayout {
* See {@link MyLocationTracking} for different values.
*
* @param myLocationTrackingMode The location tracking mode to be used.
- * @throws SecurityException if no suitable permission is present
* @see MyLocationTracking
*/
@UiThread
- @RequiresPermission(anyOf = {
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION})
void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && !mMapboxMap.isMyLocationEnabled()) {
- //noinspection ResourceType
mMapboxMap.setMyLocationEnabled(true);
}
-
mUserLocationView.setMyLocationTrackingMode(myLocationTrackingMode);
MapboxMap.OnMyLocationTrackingModeChangeListener listener = mMapboxMap.getOnMyLocationTrackingModeChangeListener();
if (listener != null) {
@@ -2494,19 +2451,6 @@ public class MapView extends FrameLayout {
}
/**
- * Returns the current user location tracking mode.
- *
- * @return The current user location tracking mode.
- * One of the values from {@link MyLocationTracking.Mode}.
- * @see MyLocationTracking.Mode
- */
- @UiThread
- @MyLocationTracking.Mode
- int getMyLocationTrackingMode() {
- return mUserLocationView.getMyLocationTrackingMode();
- }
-
- /**
* <p>
* Set the current my bearing tracking mode.
* </p>
@@ -2518,16 +2462,11 @@ public class MapView extends FrameLayout {
* See {@link MyBearingTracking} for different values.
*
* @param myBearingTrackingMode The bearing tracking mode to be used.
- * @throws SecurityException if no suitable permission is present
* @see MyBearingTracking
*/
@UiThread
- @RequiresPermission(anyOf = {
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION})
void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
if (myBearingTrackingMode != MyBearingTracking.NONE && !mMapboxMap.isMyLocationEnabled()) {
- //noinspection ResourceType
mMapboxMap.setMyLocationEnabled(true);
}
mUserLocationView.setMyBearingTrackingMode(myBearingTrackingMode);
@@ -2537,26 +2476,11 @@ public class MapView extends FrameLayout {
}
}
- /**
- * Returns the current user bearing tracking mode.
- * See {@link MyBearingTracking} for possible return values.
- *
- * @return the current user bearing tracking mode.
- * @see MyBearingTracking
- */
- @UiThread
- @MyLocationTracking.Mode
- int getMyBearingTrackingMode() {
- //noinspection ResourceType
- return mUserLocationView.getMyBearingTrackingMode();
- }
-
private void resetTrackingModes() {
try {
- //noinspection ResourceType
- setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
- //noinspection ResourceType
- setMyBearingTrackingMode(MyBearingTracking.NONE);
+ TrackingSettings trackingSettings = mMapboxMap.getTrackingSettings();
+ trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
+ trackingSettings.setMyBearingTrackingMode(MyBearingTracking.NONE);
} catch (SecurityException ignore) {
// User did not accept location permissions
}
@@ -2806,6 +2730,10 @@ public class MapView extends FrameLayout {
private void setWidgetMargins(@NonNull final View view, int left, int top, int right, int bottom) {
LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
+ left += mContentPaddingLeft;
+ top += mContentPaddingTop;
+ right += mContentPaddingRight;
+ bottom += mContentPaddingBottom;
layoutParams.setMargins(left, top, right, bottom);
view.setLayoutParams(layoutParams);
}
@@ -2813,9 +2741,11 @@ public class MapView extends FrameLayout {
private static class AttributionOnClickListener implements View.OnClickListener, DialogInterface.OnClickListener {
private static final int ATTRIBUTION_INDEX_IMPROVE_THIS_MAP = 2;
+ private static final int ATTRIBUTION_INDEX_TELEMETRY_SETTINGS = 3;
private MapView mMapView;
public AttributionOnClickListener(MapView mapView) {
+ super();
mMapView = mapView;
}
@@ -2825,7 +2755,7 @@ public class MapView extends FrameLayout {
Context context = v.getContext();
String[] items = context.getResources().getStringArray(R.array.attribution_names);
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AttributionAlertDialogStyle);
- builder.setTitle(R.string.attributionsDialogTitle);
+ builder.setTitle(R.string.mapbox_attributionsDialogTitle);
builder.setAdapter(new ArrayAdapter<>(context, R.layout.attribution_list_item, items), this);
builder.show();
}
@@ -2833,7 +2763,49 @@ public class MapView extends FrameLayout {
// Called when someone selects an attribution, 'Improve this map' adds location data to the url
@Override
public void onClick(DialogInterface dialog, int which) {
- Context context = ((Dialog) dialog).getContext();
+ final Context context = ((Dialog) dialog).getContext();
+ if (which == ATTRIBUTION_INDEX_TELEMETRY_SETTINGS) {
+
+ int array = R.array.attribution_telemetry_options;
+ if (MapboxEventManager.getMapboxEventManager(context).isTelemetryEnabled()) {
+ array = R.array.attribution_telemetry_options_already_participating;
+ }
+ String[] items = context.getResources().getStringArray(array);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AttributionAlertDialogStyle);
+ builder.setTitle(R.string.mapbox_attributionTelemetryTitle);
+ LayoutInflater factory = LayoutInflater.from(context);
+ View content = factory.inflate(R.layout.attribution_telemetry_view, null);
+
+ ListView lv = (ListView) content.findViewById(R.id.telemetryOptionsList);
+ lv.setAdapter(new ArrayAdapter<String>(context, R.layout.attribution_list_item, items));
+ lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+
+ builder.setView(content);
+ final AlertDialog telemDialog = builder.show();
+ lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ switch (position) {
+ case 0:
+ String url = context.getResources().getStringArray(R.array.attribution_links)[3];
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(url));
+ context.startActivity(intent);
+ telemDialog.cancel();
+ return;
+ case 1:
+ MapboxEventManager.getMapboxEventManager(context).setTelemetryEnabled(false);
+ telemDialog.cancel();
+ return;
+ case 2:
+ MapboxEventManager.getMapboxEventManager(context).setTelemetryEnabled(true);
+ telemDialog.cancel();
+ return;
+ }
+ }
+ });
+ return;
+ }
String url = context.getResources().getStringArray(R.array.attribution_links)[which];
if (which == ATTRIBUTION_INDEX_IMPROVE_THIS_MAP) {
LatLng latLng = mMapView.getMapboxMap().getCameraPosition().target;
@@ -3044,4 +3016,5 @@ public class MapView extends FrameLayout {
void onMapChanged(@MapChange int change);
}
+
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
index 757a9bc3d9..40b75d3ce1 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
@@ -1,20 +1,21 @@
package com.mapbox.mapboxsdk.maps;
-import android.Manifest;
import android.content.Context;
import android.location.Location;
import android.os.Bundle;
-import android.support.annotation.FloatRange;
+import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.annotation.RequiresPermission;
import android.support.annotation.UiThread;
+import android.support.v4.util.LongSparseArray;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import com.mapbox.mapboxsdk.annotations.Annotation;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.InfoWindow;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
@@ -23,6 +24,8 @@ import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdate;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
@@ -39,10 +42,12 @@ public class MapboxMap {
private MapView mMapView;
private UiSettings mUiSettings;
+ private TrackingSettings mTrackingSettings;
private Projection mProjection;
private CameraPosition mCameraPosition;
private boolean mInvalidCameraPosition;
private String mStyleUrl;
+ private LongSparseArray<Annotation> mAnnotations;
private List<Marker> mSelectedMarkers;
private List<InfoWindow> mInfoWindows;
private MapboxMap.InfoWindowAdapter mInfoWindowAdapter;
@@ -54,17 +59,22 @@ public class MapboxMap {
private MapboxMap.OnMapLongClickListener mOnMapLongClickListener;
private MapboxMap.OnMarkerClickListener mOnMarkerClickListener;
private MapboxMap.OnInfoWindowClickListener mOnInfoWindowClickListener;
+ private MapboxMap.OnInfoWindowLongClickListener mOnInfoWindowLongClickListener;
+ private MapboxMap.OnInfoWindowCloseListener mOnInfoWindowCloseListener;
private MapboxMap.OnFlingListener mOnFlingListener;
private MapboxMap.OnScrollListener mOnScrollListener;
private MapboxMap.OnMyLocationTrackingModeChangeListener mOnMyLocationTrackingModeChangeListener;
private MapboxMap.OnMyBearingTrackingModeChangeListener mOnMyBearingTrackingModeChangeListener;
private MapboxMap.OnFpsChangedListener mOnFpsChangedListener;
+ private MapboxMap.OnCameraChangeListener mOnCameraChangeListener;
MapboxMap(@NonNull MapView mapView) {
mMapView = mapView;
mMapView.addOnMapChangedListener(new MapChangeCameraPositionListener());
mUiSettings = new UiSettings(mapView);
+ mTrackingSettings = new TrackingSettings(mMapView, mUiSettings);
mProjection = new Projection(mapView);
+ mAnnotations = new LongSparseArray<>();
mSelectedMarkers = new ArrayList<>();
mInfoWindows = new ArrayList<>();
}
@@ -83,6 +93,19 @@ public class MapboxMap {
}
//
+ // TrackingSettings
+ //
+
+ /**
+ * Gets the tracking interface settings for the map.
+ *
+ * @return
+ */
+ public TrackingSettings getTrackingSettings() {
+ return mTrackingSettings;
+ }
+
+ //
// Projection
//
@@ -105,14 +128,7 @@ public class MapboxMap {
*/
public final CameraPosition getCameraPosition() {
if (mInvalidCameraPosition) {
- // Camera position has changed, need to regenerate position
- mCameraPosition = new CameraPosition.Builder(true)
- .bearing((float) mMapView.getBearing())
- .target(mMapView.getLatLng())
- .tilt((float) mMapView.getTilt())
- .zoom((float) mMapView.getZoom())
- .build();
- mInvalidCameraPosition = false;
+ invalidateCameraPosition();
}
return mCameraPosition;
}
@@ -137,8 +153,26 @@ public class MapboxMap {
*/
@UiThread
public final void moveCamera(CameraUpdate update) {
+ moveCamera(update, null);
+ }
+
+ /**
+ * Repositions the camera according to the instructions defined in the update.
+ * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position.
+ * See CameraUpdateFactory for a set of updates.
+ *
+ * @param update The change that should be applied to the camera.
+ */
+ @UiThread
+ public final void moveCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) {
mCameraPosition = update.getCameraPosition(this);
mMapView.jumpTo(mCameraPosition.bearing, mCameraPosition.target, mCameraPosition.tilt, mCameraPosition.zoom);
+ if (mOnCameraChangeListener != null) {
+ mOnCameraChangeListener.onCameraChange(mCameraPosition);
+ }
+ if (callback != null) {
+ callback.onFinish();
+ }
}
/**
@@ -175,7 +209,25 @@ public class MapboxMap {
@UiThread
public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) {
mCameraPosition = update.getCameraPosition(this);
- mMapView.easeTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, callback);
+ mMapView.easeTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, new CancelableCallback() {
+ @Override
+ public void onCancel() {
+ if (callback != null) {
+ callback.onCancel();
+ }
+ }
+
+ @Override
+ public void onFinish() {
+ if (mOnCameraChangeListener != null) {
+ mOnCameraChangeListener.onCameraChange(mCameraPosition);
+ }
+
+ if (callback != null) {
+ callback.onFinish();
+ }
+ }
+ });
}
/**
@@ -227,7 +279,25 @@ public class MapboxMap {
@UiThread
public final void animateCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) {
mCameraPosition = update.getCameraPosition(this);
- mMapView.flyTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, callback);
+ mMapView.flyTo(mCameraPosition.bearing, mCameraPosition.target, getDurationNano(durationMs), mCameraPosition.tilt, mCameraPosition.zoom, new CancelableCallback() {
+ @Override
+ public void onCancel() {
+ if (callback != null) {
+ callback.onCancel();
+ }
+ }
+
+ @Override
+ public void onFinish() {
+ if (mOnCameraChangeListener != null) {
+ mOnCameraChangeListener.onCameraChange(mCameraPosition);
+ }
+
+ if (callback != null) {
+ callback.onFinish();
+ }
+ }
+ });
}
// internal time layer conversion
@@ -235,75 +305,34 @@ public class MapboxMap {
return durationMs > 0 ? TimeUnit.NANOSECONDS.convert(durationMs, TimeUnit.MILLISECONDS) : 0;
}
- //
- // ZOOM
- //
-
- /**
- * <p>
- * Sets the minimum zoom level the map can be displayed at.
- * </p>
- *
- * @param minZoom The new minimum zoom level.
- */
- @UiThread
- public void setMinZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) {
- if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) {
- Log.e(MapboxConstants.TAG, "Not setting minZoom, value is in unsupported range: " + minZoom);
- return;
+ private void invalidateCameraPosition() {
+ mInvalidCameraPosition = false;
+ mCameraPosition = new CameraPosition.Builder(true)
+ .bearing((float) mMapView.getBearing())
+ .target(mMapView.getLatLng())
+ .tilt((float) mMapView.getTilt())
+ .zoom((float) mMapView.getZoom())
+ .build();
+ if (mOnCameraChangeListener != null) {
+ mOnCameraChangeListener.onCameraChange(mCameraPosition);
}
- mMapView.setMinZoom(minZoom);
}
- /**
- * <p>
- * Gets the maximum zoom level the map can be displayed at.
- * </p>
- *
- * @return The minimum zoom level.
- */
- @UiThread
- public double getMinZoom() {
- return mMapView.getMinZoom();
- }
-
- /**
- * <p>
- * Sets the maximum zoom level the map can be displayed at.
- * </p>
- *
- * @param maxZoom The new maximum zoom level.
- */
- @UiThread
- public void setMaxZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) {
- if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) {
- Log.e(MapboxConstants.TAG, "Not setting maxZoom, value is in unsupported range: " + maxZoom);
- return;
- }
- mMapView.setMaxZoom(maxZoom);
- }
+ //
+ // Reset North
+ //
/**
- * <p>
- * Gets the maximum zoom level the map can be displayed at.
- * </p>
*
- * @return The maximum zoom level.
*/
- @UiThread
- public double getMaxZoom() {
- return mMapView.getMaxZoom();
+ public void resetNorth() {
+ mMapView.resetNorth();
}
//
// Manual zoom controls
//
- // used by UiSettings
- void setZoomControlsEnabled(boolean enabled) {
- mMapView.setZoomControlsEnabled(enabled);
- }
-
//
// Debug
//
@@ -468,7 +497,17 @@ public class MapboxMap {
@UiThread
@NonNull
public Marker addMarker(@NonNull MarkerOptions markerOptions) {
- return mMapView.addMarker(markerOptions);
+ return addMarker((BaseMarkerOptions) markerOptions);
+ }
+
+ @UiThread
+ @NonNull
+ public Marker addMarker(@NonNull BaseMarkerOptions markerOptions) {
+ Marker marker = prepareMarker(markerOptions);
+ long id = mMapView.addMarker(marker);
+ marker.setId(id);
+ mAnnotations.put(id, marker);
+ return marker;
}
/**
@@ -484,7 +523,50 @@ public class MapboxMap {
@UiThread
@NonNull
public List<Marker> addMarkers(@NonNull List<MarkerOptions> markerOptionsList) {
- return mMapView.addMarkers(markerOptionsList);
+ int count = markerOptionsList.size();
+ List<Marker> markers = new ArrayList<>(count);
+ MarkerOptions markerOptions;
+ Marker marker;
+ for (int i = 0; i < count; i++) {
+ markerOptions = markerOptionsList.get(i);
+ marker = prepareMarker(markerOptions);
+ markers.add(marker);
+ }
+
+ long[] ids = mMapView.addMarkers(markers);
+ long id = 0;
+ Marker m;
+
+ for (int i = 0; i < markers.size(); i++) {
+ m = markers.get(i);
+ m.setMapboxMap(this);
+ if (ids != null) {
+ id = ids[i];
+ } else {
+ //unit test
+ id++;
+ }
+ m.setId(id);
+ mAnnotations.put(id, m);
+ }
+ return markers;
+ }
+
+ /**
+ * <p>
+ * Updates a marker on this map. Does nothing if the marker is already added.
+ * </p>
+ *
+ * @param updatedMarker An updated marker object.
+ */
+ @UiThread
+ public void updateMarker(@NonNull Marker updatedMarker) {
+ mMapView.updateMarker(updatedMarker);
+
+ int index = mAnnotations.indexOfKey(updatedMarker.getId());
+ if (index > -1) {
+ mAnnotations.setValueAt(index, updatedMarker);
+ }
}
/**
@@ -496,7 +578,14 @@ public class MapboxMap {
@UiThread
@NonNull
public Polyline addPolyline(@NonNull PolylineOptions polylineOptions) {
- return mMapView.addPolyline(polylineOptions);
+ Polyline polyline = polylineOptions.getPolyline();
+ if (!polyline.getPoints().isEmpty()) {
+ long id = mMapView.addPolyline(polyline);
+ polyline.setMapboxMap(this);
+ polyline.setId(id);
+ mAnnotations.put(id, polyline);
+ }
+ return polyline;
}
/**
@@ -508,7 +597,33 @@ public class MapboxMap {
@UiThread
@NonNull
public List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList) {
- return mMapView.addPolylines(polylineOptionsList);
+ int count = polylineOptionsList.size();
+ Polyline polyline;
+ List<Polyline> polylines = new ArrayList<>(count);
+ for (PolylineOptions options : polylineOptionsList) {
+ polyline = options.getPolyline();
+ if (!polyline.getPoints().isEmpty()) {
+ polylines.add(polyline);
+ }
+ }
+
+ long[] ids = mMapView.addPolylines(polylines);
+ long id = 0;
+ Polyline p;
+
+ for (int i = 0; i < polylines.size(); i++) {
+ p = polylines.get(i);
+ p.setMapboxMap(this);
+ if (ids != null) {
+ id = ids[i];
+ } else {
+ // unit test
+ id++;
+ }
+ p.setId(id);
+ mAnnotations.put(id, p);
+ }
+ return polylines;
}
/**
@@ -520,7 +635,14 @@ public class MapboxMap {
@UiThread
@NonNull
public Polygon addPolygon(@NonNull PolygonOptions polygonOptions) {
- return mMapView.addPolygon(polygonOptions);
+ Polygon polygon = polygonOptions.getPolygon();
+ if (!polygon.getPoints().isEmpty()) {
+ long id = mMapView.addPolygon(polygon);
+ polygon.setId(id);
+ polygon.setMapboxMap(this);
+ mAnnotations.put(id, polygon);
+ }
+ return polygon;
}
/**
@@ -532,7 +654,32 @@ public class MapboxMap {
@UiThread
@NonNull
public List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList) {
- return mMapView.addPolygons(polygonOptionsList);
+ int count = polygonOptionsList.size();
+
+ Polygon polygon;
+ List<Polygon> polygons = new ArrayList<>(count);
+ for (PolygonOptions polygonOptions : polygonOptionsList) {
+ polygon = polygonOptions.getPolygon();
+ if (!polygon.getPoints().isEmpty()) {
+ polygons.add(polygon);
+ }
+ }
+
+ long[] ids = mMapView.addPolygons(polygons);
+ long id = 0;
+ for (int i = 0; i < polygons.size(); i++) {
+ polygon = polygons.get(i);
+ polygon.setMapboxMap(this);
+ if (ids != null) {
+ id = ids[i];
+ } else {
+ // unit test
+ id++;
+ }
+ polygon.setId(id);
+ mAnnotations.put(id, polygon);
+ }
+ return polygons;
}
/**
@@ -549,13 +696,55 @@ public class MapboxMap {
}
/**
+ * <p>
+ * Convenience method for removing a Polyline from the map.
+ * </p>
+ * Calls removeAnnotation() internally
+ *
+ * @param polyline Polyline to remove
+ */
+ @UiThread
+ public void removePolyline(@NonNull Polyline polyline) {
+ removeAnnotation(polyline);
+ }
+
+ /**
+ * <p>
+ * Convenience method for removing a Polygon from the map.
+ * </p>
+ * Calls removeAnnotation() internally
+ *
+ * @param polygon Polygon to remove
+ */
+ @UiThread
+ public void removePolygon(@NonNull Polygon polygon) {
+ removeAnnotation(polygon);
+ }
+
+ /**
* Removes an annotation from the map.
*
* @param annotation The annotation object to remove.
*/
@UiThread
public void removeAnnotation(@NonNull Annotation annotation) {
- mMapView.removeAnnotation(annotation);
+ if (annotation instanceof Marker) {
+ ((Marker) annotation).hideInfoWindow();
+ }
+ long id = annotation.getId();
+ mMapView.removeAnnotation(id);
+ mAnnotations.remove(id);
+ }
+
+ /**
+ * Removes an annotation from the map
+ *
+ * @param id The identifier associated to the annotation to be removed
+ */
+ @UiThread
+ public void removeAnnotation(long id) {
+ mMapView.removeAnnotation(id);
+ mAnnotations.remove(id);
}
/**
@@ -565,15 +754,49 @@ public class MapboxMap {
*/
@UiThread
public void removeAnnotations(@NonNull List<? extends Annotation> annotationList) {
- mMapView.removeAnnotations(annotationList);
+ int count = annotationList.size();
+ long[] ids = new long[count];
+ for (int i = 0; i < count; i++) {
+ Annotation annotation = annotationList.get(i);
+ if (annotation instanceof Marker) {
+ ((Marker) annotation).hideInfoWindow();
+ }
+ ids[i] = annotationList.get(i).getId();
+ }
+ mMapView.removeAnnotations(ids);
+ for (long id : ids) {
+ mAnnotations.remove(id);
+ }
}
/**
* Removes all annotations from the map.
*/
@UiThread
- public void removeAllAnnotations() {
- mMapView.removeAllAnnotations();
+ public void removeAnnotations() {
+ Annotation annotation;
+ int count = mAnnotations.size();
+ long[] ids = new long[count];
+ for (int i = 0; i < count; i++) {
+ ids[i] = mAnnotations.keyAt(i);
+ annotation = mAnnotations.get(ids[i]);
+ if (annotation instanceof Marker) {
+ ((Marker) annotation).hideInfoWindow();
+ }
+ }
+ mMapView.removeAnnotations(ids);
+ mAnnotations.clear();
+ }
+
+ /**
+ * Return a annotation based on its id.
+ *
+ * @return An annotation with a matched id, null is returned if no match was found.
+ */
+ @UiThread
+ @Nullable
+ public Annotation getAnnotation(long id) {
+ return mAnnotations.get(id);
}
/**
@@ -583,8 +806,69 @@ public class MapboxMap {
* list will not update the map.
*/
@NonNull
- public List<Annotation> getAllAnnotations() {
- return mMapView.getAllAnnotations();
+ public List<Annotation> getAnnotations() {
+ List<Annotation> annotations = new ArrayList<>();
+ for (int i = 0; i < mAnnotations.size(); i++) {
+ annotations.add(mAnnotations.get(mAnnotations.keyAt(i)));
+ }
+ return annotations;
+ }
+
+ /**
+ * Returns a list of all the markers on the map.
+ *
+ * @return A list of all the markers objects. The returned object is a copy so modifying this
+ * list will not update the map.
+ */
+ @NonNull
+ public List<Marker> getMarkers() {
+ List<Marker> markers = new ArrayList<>();
+ Annotation annotation;
+ for (int i = 0; i < mAnnotations.size(); i++) {
+ annotation = mAnnotations.get(mAnnotations.keyAt(i));
+ if (annotation instanceof Marker) {
+ markers.add((Marker) annotation);
+ }
+ }
+ return markers;
+ }
+
+ /**
+ * Returns a list of all the polygons on the map.
+ *
+ * @return A list of all the polygon objects. The returned object is a copy so modifying this
+ * list will not update the map.
+ */
+ @NonNull
+ public List<Polygon> getPolygons() {
+ List<Polygon> polygons = new ArrayList<>();
+ Annotation annotation;
+ for (int i = 0; i < mAnnotations.size(); i++) {
+ annotation = mAnnotations.get(mAnnotations.keyAt(i));
+ if (annotation instanceof Polygon) {
+ polygons.add((Polygon) annotation);
+ }
+ }
+ return polygons;
+ }
+
+ /**
+ * Returns a list of all the polylines on the map.
+ *
+ * @return A list of all the polylines objects. The returned object is a copy so modifying this
+ * list will not update the map.
+ */
+ @NonNull
+ public List<Polyline> getPolylines() {
+ List<Polyline> polylines = new ArrayList<>();
+ Annotation annotation;
+ for (int i = 0; i < mAnnotations.size(); i++) {
+ annotation = mAnnotations.get(mAnnotations.keyAt(i));
+ if (annotation instanceof Polyline) {
+ polylines.add((Polyline) annotation);
+ }
+ }
+ return polylines;
}
/**
@@ -670,11 +954,17 @@ public class MapboxMap {
* @return The currently selected marker.
*/
@UiThread
- @Nullable
public List<Marker> getSelectedMarkers() {
return mSelectedMarkers;
}
+ private Marker prepareMarker(BaseMarkerOptions markerOptions) {
+ Marker marker = markerOptions.getMarker();
+ Icon icon = mMapView.loadIconForMarker(marker);
+ marker.setTopOffsetPixels(mMapView.getTopOffsetPixelsForIcon(icon));
+ return marker;
+ }
+
//
// InfoWindow
//
@@ -735,10 +1025,58 @@ public class MapboxMap {
}
//
+ // Padding
+ //
+
+ /**
+ * Sets the distance from the edges of the map view’s frame to the edges of the map
+ * view’s logical viewport.
+ * <p/>
+ * When the value of this property is equal to {0,0,0,0}, viewport
+ * properties such as `centerCoordinate` assume a viewport that matches the map
+ * view’s frame. Otherwise, those properties are inset, excluding part of the
+ * frame from the viewport. For instance, if the only the top edge is inset, the
+ * map center is effectively shifted downward.
+ *
+ * @param left The left margin in pixels.
+ * @param top The top margin in pixels.
+ * @param right The right margin in pixels.
+ * @param bottom The bottom margin in pixels.
+ */
+ public void setPadding(int left, int top, int right, int bottom) {
+ mMapView.setContentPadding(left, top, right, bottom);
+ mUiSettings.invalidate();
+
+ moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder(mCameraPosition).build()));
+ }
+
+ /**
+ *
+ * @return
+ */
+ public int[] getPadding() {
+ return new int[]{mMapView.getContentPaddingLeft(),
+ mMapView.getContentPaddingTop(),
+ mMapView.getContentPaddingRight(),
+ mMapView.getContentPaddingBottom()};
+ }
+
+ //
// Map events
//
/**
+ * Sets a callback that's invoked on every change in camera position.
+ *
+ * @param listener The callback that's invoked on every camera change position.
+ * To unset the callback, use null.
+ */
+ @UiThread
+ public void setOnCameraChangeListener(@Nullable OnCameraChangeListener listener) {
+ mOnCameraChangeListener = listener;
+ }
+
+ /**
* Sets a callback that's invoked on every frame rendered to the map view.
*
* @param listener The callback that's invoked on every frame rendered to the map view.
@@ -845,10 +1183,44 @@ public class MapboxMap {
*
* @return Current active InfoWindow Click Listener
*/
+ @UiThread
public OnInfoWindowClickListener getOnInfoWindowClickListener() {
return mOnInfoWindowClickListener;
}
+ /**
+ * Sets a callback that's invoked when a marker's info window is long pressed.
+ *
+ * @param listener The callback that's invoked when a marker's info window is long pressed. To unset the callback, use null.
+ */
+ @UiThread
+ public void setOnInfoWindowLongClickListener(@Nullable OnInfoWindowLongClickListener listener) {
+ mOnInfoWindowLongClickListener = listener;
+ }
+
+ /**
+ * Return the InfoWindow long click listener
+ *
+ * @return Current active InfoWindow long Click Listener
+ */
+ public OnInfoWindowLongClickListener getOnInfoWindowLongClickListener() {
+ return mOnInfoWindowLongClickListener;
+ }
+
+ public void setOnInfoWindowCloseListener(@Nullable OnInfoWindowCloseListener listener) {
+ mOnInfoWindowCloseListener = listener;
+ }
+
+ /**
+ * Return the InfoWindow close listener
+ *
+ * @return Current active InfoWindow Close Listener
+ */
+ @UiThread
+ public OnInfoWindowCloseListener getOnInfoWindowCloseListener() {
+ return mOnInfoWindowCloseListener;
+ }
+
//
// User location
//
@@ -874,13 +1246,14 @@ public class MapboxMap {
* or @link android.Manifest.permission#ACCESS_FINE_LOCATION.
*
* @param enabled True to enable; false to disable.
- * @throws SecurityException if no suitable permission is present
*/
@UiThread
- @RequiresPermission(anyOf = {
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION})
public void setMyLocationEnabled(boolean enabled) {
+ if (!mMapView.isPermissionsAccepted()) {
+ Log.e(MapboxConstants.TAG, "Could not activate user location tracking: " +
+ "user did not accept the permission or permissions were not requested.");
+ return;
+ }
mMyLocationEnabled = enabled;
mMapView.setMyLocationEnabled(enabled);
}
@@ -909,40 +1282,6 @@ public class MapboxMap {
}
/**
- * <p>
- * Set the current my location tracking mode.
- * </p>
- * <p>
- * Will enable my location if not active.
- * </p>
- * See {@link MyLocationTracking} for different values.
- *
- * @param myLocationTrackingMode The location tracking mode to be used.
- * @throws SecurityException if no suitable permission is present
- * @see MyLocationTracking
- */
- @UiThread
- @RequiresPermission(anyOf = {
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION})
- public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
- mMapView.setMyLocationTrackingMode(myLocationTrackingMode);
- }
-
- /**
- * Returns the current user location tracking mode.
- *
- * @return The current user location tracking mode.
- * One of the values from {@link MyLocationTracking.Mode}.
- * @see MyLocationTracking.Mode
- */
- @UiThread
- @MyLocationTracking.Mode
- public int getMyLocationTrackingMode() {
- return mMapView.getMyLocationTrackingMode();
- }
-
- /**
* Sets a callback that's invoked when the location tracking mode changes.
*
* @param listener The callback that's invoked when the location tracking mode changes.
@@ -959,42 +1298,6 @@ public class MapboxMap {
}
/**
- * <p>
- * Set the current my bearing tracking mode.
- * </p>
- * Shows the direction the user is heading.
- * <p>
- * When location tracking is disabled the direction of {@link UserLocationView} is rotated
- * When location tracking is enabled the {@link MapView} is rotated based on bearing value.
- * </p>
- * See {@link MyBearingTracking} for different values.
- *
- * @param myBearingTrackingMode The bearing tracking mode to be used.
- * @throws SecurityException if no suitable permission is present
- * @see MyBearingTracking
- */
- @UiThread
- @RequiresPermission(anyOf = {
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION})
- public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
- mMapView.setMyBearingTrackingMode(myBearingTrackingMode);
- }
-
- /**
- * Returns the current user bearing tracking mode.
- * See {@link MyBearingTracking} for possible return values.
- *
- * @return the current user bearing tracking mode.
- * @see MyBearingTracking
- */
- @UiThread
- @MyLocationTracking.Mode
- public int getMyBearingTrackingMode() {
- return mMapView.getMyBearingTrackingMode();
- }
-
- /**
* Sets a callback that's invoked when the bearing tracking mode changes.
*
* @param listener The callback that's invoked when the bearing tracking mode changes.
@@ -1033,9 +1336,17 @@ public class MapboxMap {
return mMapView;
}
-//
-// Interfaces
-//
+ //
+ // Invalidate
+ //
+
+ public void invalidate(){
+ mMapView.update();
+ }
+
+ //
+ // Interfaces
+ //
/**
* Interface definition for a callback to be invoked when the map is flinged.
@@ -1062,6 +1373,20 @@ public class MapboxMap {
}
/**
+ * Interface definition for a callback to be invoked for when the camera changes position.
+ */
+ public interface OnCameraChangeListener {
+ /**
+ * Called after the camera position has changed. During an animation,
+ * this listener may not be notified of intermediate camera positions.
+ * It is always called for the final position in the animation.
+ *
+ * @param position The CameraPosition at the end of the last camera change.
+ */
+ void onCameraChange(CameraPosition position);
+ }
+
+ /**
* Interface definition for a callback to be invoked on every frame rendered to the map view.
*
* @see MapboxMap#setOnFpsChangedListener(OnFpsChangedListener)
@@ -1130,7 +1455,37 @@ public class MapboxMap {
* @param marker The marker of the info window the user clicked on.
* @return If true the listener has consumed the event and the info window will not be closed.
*/
- boolean onMarkerClick(@NonNull Marker marker);
+ boolean onInfoWindowClick(@NonNull Marker marker);
+ }
+
+ /**
+ * Callback interface for when the user long presses on a marker's info window.
+ *
+ * @see MapboxMap#setOnInfoWindowClickListener(OnInfoWindowClickListener)
+ */
+ public interface OnInfoWindowLongClickListener {
+
+ /**
+ * Called when the user makes a long-press gesture on the marker's info window.
+ *
+ * @param marker The marker were the info window is attached to
+ */
+ void onInfoWindowLongClick(Marker marker);
+ }
+
+ /**
+ * Callback interface for close events on a marker's info window.
+ *
+ * @see MapboxMap#setOnInfoWindowCloseListener(OnInfoWindowCloseListener)
+ */
+ public interface OnInfoWindowCloseListener {
+
+ /**
+ * Called when the marker's info window is closed.
+ *
+ * @param marker The marker of the info window that was closed.
+ */
+ void onInfoWindowClose(Marker marker);
}
/**
@@ -1169,7 +1524,7 @@ public class MapboxMap {
/**
* Interface definition for a callback to be invoked when the the My Location tracking mode changes.
*
- * @see MapboxMap#setMyLocationTrackingMode(int)
+ * @see MapView#setMyLocationTrackingMode(int)
*/
public interface OnMyLocationTrackingModeChangeListener {
@@ -1184,7 +1539,7 @@ public class MapboxMap {
/**
* Interface definition for a callback to be invoked when the the My Location tracking mode changes.
*
- * @see MapboxMap#setMyLocationTrackingMode(int)
+ * @see MapView#setMyLocationTrackingMode(int)
*/
public interface OnMyBearingTrackingModeChangeListener {
@@ -1212,13 +1567,20 @@ public class MapboxMap {
}
private class MapChangeCameraPositionListener implements MapView.OnMapChangedListener {
+
+ private static final long UPDATE_RATE_MS = 400;
+ private long mPreviousUpdateTimestamp = 0;
+
@Override
public void onMapChanged(@MapView.MapChange int change) {
- if (!mInvalidCameraPosition && (change == MapView.REGION_DID_CHANGE
- || change == MapView.REGION_DID_CHANGE_ANIMATED
- || change == MapView.REGION_WILL_CHANGE
- || change == MapView.REGION_WILL_CHANGE_ANIMATED)) {
+ if (change >= MapView.REGION_WILL_CHANGE && change <= MapView.REGION_DID_CHANGE_ANIMATED) {
mInvalidCameraPosition = true;
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime < mPreviousUpdateTimestamp) {
+ return;
+ }
+ invalidateCameraPosition();
+ mPreviousUpdateTimestamp = currentTime + UPDATE_RATE_MS;
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
deleted file mode 100644
index cb6407986e..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package com.mapbox.mapboxsdk.maps;
-
-import android.content.Context;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-
-/**
- * Builder for composing {@link MapboxMap} objects. These options can be used when adding a
- * map to your application programmatically (as opposed to via XML). If you are using a MapFragment,
- * you can pass these options in using the static factory method newInstance(MapboxMapOptions).
- * If you are using a MapView, you can pass these options in using the constructor MapView(Context, MapboxMapOptions).
- */
-public class MapboxMapOptions implements Parcelable {
-
- private MapboxMap mMapboxMap;
- private UiSettings mUiSettings;
-
- public MapboxMapOptions(MapboxMap mapboxMap) {
- mMapboxMap = mapboxMap;
- mUiSettings = mapboxMap.getUiSettings();
- }
-
- public MapboxMapOptions(Parcel in) {
- throw new UnsupportedOperationException();
- }
-
- public MapboxMapOptions camera(CameraPosition camera) {
- mMapboxMap.setCameraPosition(camera);
- return this;
- }
-
- public CameraPosition getCamera() {
- return mMapboxMap.getCameraPosition();
- }
-
- public MapboxMapOptions compassEnabled(boolean enabled) {
- mUiSettings.setCompassEnabled(enabled);
- return this;
- }
-
- public boolean getCompassEnabled() {
- return mUiSettings.isCompassEnabled();
- }
-
- public MapboxMapOptions rotateEnabled(boolean rotateEnabled) {
- mUiSettings.setRotateGesturesEnabled(rotateEnabled);
- return this;
- }
-
- public MapboxMapOptions rotateGesturesEnabled(boolean enabled) {
- mUiSettings.setRotateGesturesEnabled(enabled);
- return this;
- }
-
- public boolean getRotateGesturesEnabled() {
- return mUiSettings.isRotateGesturesEnabled();
- }
-
- public MapboxMapOptions scrollGesturesEnabled(boolean enabled) {
- mUiSettings.setScrollGesturesEnabled(enabled);
- return this;
- }
-
- public boolean getScrollGesturesEnabled() {
- return mUiSettings.isScrollGesturesEnabled();
- }
-
- public MapboxMapOptions tiltGesturesEnabled(boolean enabled) {
- mUiSettings.setTiltGesturesEnabled(enabled);
- return this;
- }
-
- public boolean getTiltGesturesEnabled() {
- return mUiSettings.isTiltGesturesEnabled();
- }
-
- public MapboxMapOptions zoomControlsEnabled(boolean enabled) {
- mUiSettings.setZoomControlsEnabled(enabled);
- return this;
- }
-
- public boolean getZoomControlsEnabled() {
- return mUiSettings.isZoomControlsEnabled();
- }
-
- public boolean getZoomGesturesEnabled() {
- return mUiSettings.isZoomGesturesEnabled();
- }
-
- public MapboxMapOptions createFromAttributes(Context context, AttributeSet attrs) {
- throw new UnsupportedOperationException();
- }
-
- public static final Parcelable.Creator<MapboxMapOptions> CREATOR = new Parcelable.Creator<MapboxMapOptions>() {
- public MapboxMapOptions createFromParcel(Parcel in) {
- return new MapboxMapOptions(in);
- }
-
- public MapboxMapOptions[] newArray(int size) {
- return new MapboxMapOptions[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- throw new UnsupportedOperationException();
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
index 6c388d9d8a..b7f583e943 100755
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
@@ -395,6 +395,10 @@ final class NativeMapView {
return nativeAddPolygons(mNativeMapViewPtr, polygon);
}
+ public void updateMarker(Marker marker) {
+ nativeUpdateMarker(mNativeMapViewPtr, marker);
+ }
+
public void removeAnnotation(long id) {
nativeRemoveAnnotation(mNativeMapViewPtr, id);
}
@@ -619,6 +623,8 @@ final class NativeMapView {
private native long nativeAddMarker(long nativeMapViewPtr, Marker marker);
+ private native void nativeUpdateMarker(long nativeMapViewPtr, Marker marker);
+
private native long[] nativeAddMarkers(long nativeMapViewPtr, List<Marker> markers);
private native long nativeAddPolyline(long nativeMapViewPtr, Polyline polyline);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
index e53d430b69..0d5745d4c9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java
@@ -1,6 +1,7 @@
package com.mapbox.mapboxsdk.maps;
import android.graphics.PointF;
+import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import com.mapbox.mapboxsdk.geometry.LatLng;
@@ -21,6 +22,20 @@ public class Projection {
}
/**
+ * <p>
+ * Returns the distance spanned by one pixel at the specified latitude and current zoom level.
+ * </p>
+ * The distance between pixels decreases as the latitude approaches the poles.
+ * This relationship parallels the relationship between longitudinal coordinates at different latitudes.
+ *
+ * @param latitude The latitude for which to return the value.
+ * @return The distance measured in meters.
+ */
+ public double getMetersPerPixelAtLatitude(@FloatRange(from = -180, to = 180) double latitude) {
+ return mMapView.getMetersPerPixelAtLatitude(latitude);
+ }
+
+ /**
* Returns the geographic location that corresponds to a screen location.
* The screen location is specified in screen pixels (not display pixels) relative to the
* top left of the map (not the top left of the whole screen).
@@ -55,7 +70,7 @@ public class Projection {
.include(bottomRight)
.include(bottomLeft);
- return new VisibleRegion(topLeft,topRight,bottomLeft,bottomRight,builder.build());
+ return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, builder.build());
}
/**
@@ -69,4 +84,14 @@ public class Projection {
public PointF toScreenLocation(LatLng location) {
return mMapView.toScreenLocation(location);
}
+
+ /**
+ * Calculates a zoom level based on minimum scale and current scale from MapView
+ *
+ * @param minScale The minimum scale to calculate the zoom level.
+ * @return zoom level that fits the MapView.
+ */
+ public double calculateZoom(float minScale) {
+ return Math.log(mMapView.getScale() * minScale) / Math.log(2);
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java
index 147cd31b5a..8783712e10 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java
@@ -1,6 +1,7 @@
package com.mapbox.mapboxsdk.maps;
import android.os.Bundle;
+import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
@@ -29,18 +30,10 @@ public class SupportMapFragment extends Fragment {
private MapView mMap;
- public static SupportMapFragment newInstance(){
+ public static SupportMapFragment newInstance() {
return new SupportMapFragment();
}
- public static SupportMapFragment newInstance(MapboxMapOptions mapboxMapOptions) {
- final SupportMapFragment mapFragment = new SupportMapFragment();
- Bundle bundle = new Bundle();
- bundle.putParcelable(MapboxConstants.FRAG_ARG_MAPBOXMAPOPTIONS, mapboxMapOptions);
- mapFragment.setArguments(bundle);
- return mapFragment;
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
@@ -94,7 +87,12 @@ public class SupportMapFragment extends Fragment {
}
@NonNull
- public void getMapAsync(@NonNull OnMapReadyCallback onMapReadyCallback){
- mMap.getMapAsync(onMapReadyCallback);
+ public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ mMap.getMapAsync(onMapReadyCallback);
+ }
+ });
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
new file mode 100644
index 0000000000..543ff19e56
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
@@ -0,0 +1,121 @@
+package com.mapbox.mapboxsdk.maps;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+
+import com.mapbox.mapboxsdk.constants.MyBearingTracking;
+import com.mapbox.mapboxsdk.constants.MyLocationTracking;
+
+public class TrackingSettings {
+
+ private MapView mapView;
+ private UiSettings uiSettings;
+ private boolean dismissTrackingOnGesture = true;
+
+ @MyLocationTracking.Mode
+ private int mMyLocationTrackingMode;
+
+ @MyBearingTracking.Mode
+ private int mMyBearingTrackingMode;
+
+ TrackingSettings(@NonNull MapView mapView, UiSettings uiSettings) {
+ this.mapView = mapView;
+ this.uiSettings = uiSettings;
+ }
+
+ /**
+ * <p>
+ * Set the current my location tracking mode.
+ * </p>
+ * <p>
+ * Will enable my location if not active.
+ * </p>
+ * See {@link MyLocationTracking} for different values.
+ *
+ * @param myLocationTrackingMode The location tracking mode to be used.
+ * @throws SecurityException if no suitable permission is present
+ * @see MyLocationTracking
+ */
+ @UiThread
+ public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
+ mMyLocationTrackingMode = myLocationTrackingMode;
+ mapView.setMyLocationTrackingMode(myLocationTrackingMode);
+ validateGesturesForTrackingModes();
+ }
+
+ /**
+ * Returns the current user location tracking mode.
+ *
+ * @return The current user location tracking mode.
+ * One of the values from {@link MyLocationTracking.Mode}.
+ * @see MyLocationTracking.Mode
+ */
+ @UiThread
+ @MyLocationTracking.Mode
+ public int getMyLocationTrackingMode() {
+ return mMyLocationTrackingMode;
+ }
+
+ /**
+ * <p>
+ * Set the current my bearing tracking mode.
+ * </p>
+ * Shows the direction the user is heading.
+ * <p>
+ * When location tracking is disabled the direction of {@link UserLocationView} is rotated
+ * When location tracking is enabled the {@link MapView} is rotated based on bearing value.
+ * </p>
+ * See {@link MyBearingTracking} for different values.
+ *
+ * @param myBearingTrackingMode The bearing tracking mode to be used.
+ * @throws SecurityException if no suitable permission is present
+ * @see MyBearingTracking
+ */
+ @UiThread
+ public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
+ mMyBearingTrackingMode = myBearingTrackingMode;
+ mapView.setMyBearingTrackingMode(myBearingTrackingMode);
+ }
+
+ /**
+ * Returns the current user bearing tracking mode.
+ * See {@link MyBearingTracking} for possible return values.
+ *
+ * @return the current user bearing tracking mode.
+ * @see MyBearingTracking
+ */
+ @UiThread
+ @MyLocationTracking.Mode
+ public int getMyBearingTrackingMode() {
+ return mMyBearingTrackingMode;
+ }
+
+ public boolean isDismissTrackingOnGesture() {
+ return dismissTrackingOnGesture;
+ }
+
+ public void setDismissTrackingOnGesture(boolean dismissTrackingOnGesture) {
+ this.dismissTrackingOnGesture = dismissTrackingOnGesture;
+ validateGesturesForTrackingModes();
+ }
+
+ private void validateGesturesForTrackingModes() {
+ if (!dismissTrackingOnGesture) {
+ int myLocationTrackingMode = getMyLocationTrackingMode();
+ int myBearingTrackingMode = getMyBearingTrackingMode();
+
+ // Enable/disable gestures based on tracking mode
+ if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
+ uiSettings.setScrollGesturesEnabled(true);
+ uiSettings.setRotateGesturesEnabled(true);
+ } else {
+ uiSettings.setScrollGesturesEnabled(false);
+ uiSettings.setRotateGesturesEnabled((myBearingTrackingMode == MyBearingTracking.NONE));
+ }
+ }
+ }
+
+ public boolean isLocationTrackingDisabled(){
+ return mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
index d6cb106054..1538f49d60 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java
@@ -1,9 +1,14 @@
package com.mapbox.mapboxsdk.maps;
+import android.support.annotation.FloatRange;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
+import android.util.Log;
import android.view.Gravity;
import android.view.View;
+import android.widget.VideoView;
+
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
/**
* Settings for the user interface of a MapboxMap. To obtain this interface, call getUiSettings().
@@ -12,17 +17,9 @@ public class UiSettings {
private MapView mapView;
- private boolean compassEnabled;
- private int compassGravity;
- private int[] compassMargins;
-
- private boolean logoEnabled;
- private int logoGravity;
- private int[] logoMargins;
-
- private boolean attributionEnabled;
- private int attributionGravity;
- private int[] attributionMargins;
+ private ViewSettings compassSettings;
+ private ViewSettings logoSettings;
+ private ViewSettings attributionSettings;
private boolean rotateGesturesEnabled;
private boolean tiltGesturesEnabled;
@@ -30,11 +27,78 @@ public class UiSettings {
private boolean zoomControlsEnabled;
private boolean scrollGesturesEnabled;
+ private double maxZoomLevel = -1;
+ private double minZoomLevel = -1;
+
UiSettings(@NonNull MapView mapView) {
this.mapView = mapView;
- this.compassMargins = new int[4];
- this.attributionMargins = new int[4];
- this.logoMargins = new int[4];
+ this.compassSettings = new ViewSettings();
+ this.logoSettings = new ViewSettings();
+ this.attributionSettings = new ViewSettings();
+ }
+
+ /**
+ * <p>
+ * Sets the minimum zoom level the map can be displayed at.
+ * </p>
+ *
+ * @param minZoom The new minimum zoom level.
+ */
+ @UiThread
+ public void setMinZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) {
+ if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) {
+ Log.e(MapboxConstants.TAG, "Not setting minZoom, value is in unsupported range: " + minZoom);
+ return;
+ }
+ minZoomLevel = minZoom;
+ mapView.setMinZoom(minZoom);
+ }
+
+ /**
+ * <p>
+ * Gets the maximum zoom level the map can be displayed at.
+ * </p>
+ *
+ * @return The minimum zoom level.
+ */
+ @UiThread
+ public double getMinZoom() {
+ if (minZoomLevel == -1) {
+ return minZoomLevel = mapView.getMinZoom();
+ }
+ return minZoomLevel;
+ }
+
+ /**
+ * <p>
+ * Sets the maximum zoom level the map can be displayed at.
+ * </p>
+ *
+ * @param maxZoom The new maximum zoom level.
+ */
+ @UiThread
+ public void setMaxZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) {
+ if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) {
+ Log.e(MapboxConstants.TAG, "Not setting maxZoom, value is in unsupported range: " + maxZoom);
+ return;
+ }
+ maxZoomLevel = maxZoom;
+ mapView.setMaxZoom(maxZoom);
+ }
+
+ /**
+ * <p>
+ * Gets the maximum zoom level the map can be displayed at.
+ * </p>
+ *
+ * @return The maximum zoom level.
+ */
+ @UiThread
+ public double getMaxZoom() {
+ if (maxZoomLevel == -1) {
+ return maxZoomLevel = mapView.getMaxZoom();
+ }
+ return maxZoomLevel;
}
/**
@@ -49,8 +113,8 @@ public class UiSettings {
* @param compassEnabled True to enable the compass; false to disable the compass.
*/
public void setCompassEnabled(boolean compassEnabled) {
- this.compassEnabled = compassEnabled;
- this.mapView.setCompassEnabled(compassEnabled);
+ compassSettings.setEnabled(compassEnabled);
+ mapView.setCompassEnabled(compassEnabled);
}
/**
@@ -59,7 +123,7 @@ public class UiSettings {
* @return True if the compass is enabled; false if the compass is disabled.
*/
public boolean isCompassEnabled() {
- return compassEnabled;
+ return compassSettings.isEnabled();
}
/**
@@ -74,8 +138,8 @@ public class UiSettings {
*/
@UiThread
public void setCompassGravity(int gravity) {
- this.compassGravity = gravity;
- this.mapView.setCompassGravity(gravity);
+ compassSettings.setGravity(gravity);
+ mapView.setCompassGravity(gravity);
}
/**
@@ -84,7 +148,7 @@ public class UiSettings {
* @return The gravity
*/
public int getCompassGravity() {
- return compassGravity;
+ return compassSettings.getGravity();
}
/**
@@ -98,8 +162,8 @@ public class UiSettings {
*/
@UiThread
public void setCompassMargins(int left, int top, int right, int bottom) {
- this.compassMargins = new int[]{left, top, right, bottom};
- this.mapView.setCompassMargins(left, top, right, bottom);
+ compassSettings.setMargins(new int[]{left, top, right, bottom});
+ mapView.setCompassMargins(left, top, right, bottom);
}
/**
@@ -108,7 +172,7 @@ public class UiSettings {
* @return The left margin in pixels
*/
public int getCompassMarginLeft() {
- return compassMargins[0];
+ return compassSettings.getMargins()[0];
}
/**
@@ -117,7 +181,7 @@ public class UiSettings {
* @return The top margin in pixels
*/
public int getCompassMarginTop() {
- return compassMargins[1];
+ return compassSettings.getMargins()[1];
}
/**
@@ -126,7 +190,7 @@ public class UiSettings {
* @return The right margin in pixels
*/
public int getCompassMarginRight() {
- return compassMargins[2];
+ return compassSettings.getMargins()[2];
}
/**
@@ -135,7 +199,7 @@ public class UiSettings {
* @return The bottom margin in pixels
*/
public int getCompassMarginBottom() {
- return compassMargins[3];
+ return compassSettings.getMargins()[3];
}
/**
@@ -147,8 +211,8 @@ public class UiSettings {
* @param enabled True to enable the logo; false to disable the logo.
*/
public void setLogoEnabled(boolean enabled) {
- this.logoEnabled = enabled;
- this.mapView.setLogoVisibility(enabled );
+ logoSettings.setEnabled(enabled);
+ mapView.setLogoVisibility(enabled);
}
/**
@@ -157,7 +221,7 @@ public class UiSettings {
* @return True if the logo is enabled; false if the logo is disabled.
*/
public boolean isLogoEnabled() {
- return logoEnabled;
+ return logoSettings.isEnabled();
}
/**
@@ -171,8 +235,8 @@ public class UiSettings {
* @see Gravity
*/
public void setLogoGravity(int gravity) {
- this.logoGravity = gravity;
- this.mapView.setLogoGravity(gravity);
+ logoSettings.setGravity(gravity);
+ mapView.setLogoGravity(gravity);
}
/**
@@ -181,7 +245,7 @@ public class UiSettings {
* @return The gravity
*/
public int getLogoGravity() {
- return logoGravity;
+ return logoSettings.getGravity();
}
/**
@@ -194,8 +258,8 @@ public class UiSettings {
* @param bottom The bottom margin in pixels.
*/
public void setLogoMargins(int left, int top, int right, int bottom) {
- this.logoMargins = new int[]{left, top, right, bottom};
- this.mapView.setLogoMargins(left, top, right, bottom);
+ logoSettings.setMargins(new int[]{left, top, right, bottom});
+ mapView.setLogoMargins(left, top, right, bottom);
}
/**
@@ -203,8 +267,8 @@ public class UiSettings {
*
* @return The left margin in pixels
*/
- public int getLogoMarginLeft(){
- return logoMargins[0];
+ public int getLogoMarginLeft() {
+ return logoSettings.getMargins()[0];
}
/**
@@ -212,8 +276,8 @@ public class UiSettings {
*
* @return The top margin in pixels
*/
- public int getLogoMarginTop(){
- return logoMargins[1];
+ public int getLogoMarginTop() {
+ return logoSettings.getMargins()[1];
}
/**
@@ -221,8 +285,8 @@ public class UiSettings {
*
* @return The right margin in pixels
*/
- public int getLogoMarginRight(){
- return logoMargins[2];
+ public int getLogoMarginRight() {
+ return logoSettings.getMargins()[2];
}
/**
@@ -230,8 +294,8 @@ public class UiSettings {
*
* @return The bottom margin in pixels
*/
- public int getLogoMarginBottom(){
- return logoMargins[3];
+ public int getLogoMarginBottom() {
+ return logoSettings.getMargins()[3];
}
/**
@@ -243,8 +307,8 @@ public class UiSettings {
* @param enabled True to enable the logo; false to disable the logo.
*/
public void setAttributionEnabled(boolean enabled) {
- this.attributionEnabled = enabled;
- this.mapView.setAttributionVisibility(enabled ? View.VISIBLE : View.GONE);
+ attributionSettings.setEnabled(enabled);
+ mapView.setAttributionVisibility(enabled ? View.VISIBLE : View.GONE);
}
/**
@@ -253,7 +317,7 @@ public class UiSettings {
* @return True if the logo is enabled; false if the logo is disabled.
*/
public boolean isAttributionEnabled() {
- return attributionEnabled;
+ return attributionSettings.isEnabled();
}
/**
@@ -267,8 +331,8 @@ public class UiSettings {
* @see Gravity
*/
public void setAttributionGravity(int gravity) {
- this.attributionGravity = gravity;
- this.mapView.setAttributionGravity(gravity);
+ attributionSettings.setGravity(gravity);
+ mapView.setAttributionGravity(gravity);
}
/**
@@ -277,7 +341,7 @@ public class UiSettings {
* @return The gravity
*/
public int getAttributionGravity() {
- return attributionGravity;
+ return attributionSettings.getGravity();
}
/**
@@ -290,8 +354,8 @@ public class UiSettings {
* @param bottom The bottom margin in pixels.
*/
public void setAttributionMargins(int left, int top, int right, int bottom) {
- this.attributionMargins = new int[]{left, top, right, bottom};
- this.mapView.setAttributionMargins(left, top, right, bottom);
+ attributionSettings.setMargins(new int[]{left, top, right, bottom});
+ mapView.setAttributionMargins(left, top, right, bottom);
}
/**
@@ -299,8 +363,8 @@ public class UiSettings {
*
* @return The left margin in pixels
*/
- public int getAttributionMarginLeft(){
- return attributionMargins[0];
+ public int getAttributionMarginLeft() {
+ return attributionSettings.getMargins()[0];
}
/**
@@ -308,8 +372,8 @@ public class UiSettings {
*
* @return The top margin in pixels
*/
- public int getAttributionMarginTop(){
- return attributionMargins[1];
+ public int getAttributionMarginTop() {
+ return attributionSettings.getMargins()[1];
}
/**
@@ -317,8 +381,8 @@ public class UiSettings {
*
* @return The right margin in pixels
*/
- public int getAttributionMarginRight(){
- return attributionMargins[2];
+ public int getAttributionMarginRight() {
+ return attributionSettings.getMargins()[2];
}
/**
@@ -326,8 +390,8 @@ public class UiSettings {
*
* @return The bottom margin in pixels
*/
- public int getAttributionMarginBottom(){
- return attributionMargins[3];
+ public int getAttributionMarginBottom() {
+ return attributionSettings.getMargins()[3];
}
/**
@@ -477,4 +541,31 @@ public class UiSettings {
setTiltGesturesEnabled(enabled);
setZoomGesturesEnabled(enabled);
}
+
+ /**
+ * Returns the measured height of the MapView
+ *
+ * @return height in pixels
+ */
+ public float getHeight() {
+ return mapView.getMeasuredHeight();
+ }
+
+ /**
+ * Returns the measured width of the MapView
+ *
+ * @return widht in pixels
+ */
+ public float getWidth() {
+ return mapView.getMeasuredWidth();
+ }
+
+ /**
+ * Invalidates the ViewSettings instances shown on top of the MapView
+ */
+ public void invalidate() {
+ mapView.setLogoMargins(getLogoMarginLeft(), getLogoMarginTop(), getLogoMarginRight(), getLogoMarginBottom());
+ mapView.setCompassMargins(getCompassMarginLeft(), getCompassMarginTop(), getCompassMarginRight(), getCompassMarginBottom());
+ mapView.setAttributionMargins(getAttributionMarginLeft(), getAttributionMarginTop(), getAttributionMarginRight(), getAttributionMarginBottom());
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java
new file mode 100644
index 0000000000..a192a1b576
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ViewSettings.java
@@ -0,0 +1,36 @@
+package com.mapbox.mapboxsdk.maps;
+
+public class ViewSettings {
+
+ private boolean enabled;
+ private int gravity;
+ private int[]margins;
+
+ public ViewSettings() {
+ margins = new int[4];
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public int getGravity() {
+ return gravity;
+ }
+
+ public void setGravity(int gravity) {
+ this.gravity = gravity;
+ }
+
+ public int[] getMargins() {
+ return margins;
+ }
+
+ public void setMargins(int[] margins) {
+ this.margins = margins;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
index 0d84289332..28afb70de3 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CompassView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.maps;
+package com.mapbox.mapboxsdk.maps.widgets;
import android.content.Context;
import android.support.v4.content.ContextCompat;
@@ -11,6 +11,8 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import com.mapbox.mapboxsdk.R;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
import java.lang.ref.WeakReference;
import java.util.Timer;
@@ -21,7 +23,7 @@ import java.util.TimerTask;
* when it isn't true north (0.0). Tapping the compass resets the bearing to true
* north and hides the compass.
*/
-public class CompassView extends ImageView {
+public final class CompassView extends ImageView {
private Timer mNorthTimer;
private double mDirection = 0.0f;
@@ -46,7 +48,7 @@ public class CompassView extends ImageView {
// View configuration
setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.compass));
- setContentDescription(getResources().getString(R.string.compassContentDescription));
+ setContentDescription(getResources().getString(R.string.mapbox_compassContentDescription));
setEnabled(false);
// Layout params
@@ -139,17 +141,17 @@ public class CompassView extends ImageView {
public static class CompassClickListener implements View.OnClickListener {
- private WeakReference<MapView> mMapView;
+ private WeakReference<MapboxMap> mMapboxMap;
- public CompassClickListener(final MapView mapView) {
- mMapView = new WeakReference<>(mapView);
+ public CompassClickListener(final MapboxMap mapboxMap) {
+ mMapboxMap = new WeakReference<>(mapboxMap);
}
@Override
public void onClick(View v) {
- final MapView mapView = mMapView.get();
- if (mapView != null) {
- mapView.resetNorth();
+ final MapboxMap mapboxMap = mMapboxMap.get();
+ if (mapboxMap != null) {
+ mapboxMap.resetNorth();
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UserLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java
index 9f8261a0a7..98d66b9307 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UserLocationView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/UserLocationView.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.maps;
+package com.mapbox.mapboxsdk.maps.widgets;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
@@ -27,11 +27,14 @@ import android.view.ViewGroup;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
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;
+import com.mapbox.mapboxsdk.location.LocationService;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.Projection;
import java.lang.ref.WeakReference;
@@ -42,9 +45,8 @@ import java.lang.ref.WeakReference;
public final class UserLocationView extends View {
- private MapView mMapView;
-
- private float mDensity;
+ private MapboxMap mMapboxMap;
+ private Projection mProjection;
private boolean mShowMarker;
private boolean mShowDirection;
@@ -84,7 +86,6 @@ public final class UserLocationView extends View {
private LatLng mCurrentMapViewCoordinate;
private double mCurrentBearing;
-
private boolean mPaused = false;
private Location mUserLocation;
private UserLocationListener mUserLocationListener;
@@ -99,7 +100,6 @@ public final class UserLocationView extends View {
// Compass data
private MyBearingListener mBearingChangeListener;
- private static final long BEARING_DURATION = 100;
public UserLocationView(Context context) {
super(context);
@@ -132,9 +132,9 @@ public final class UserLocationView extends View {
// Setup the custom paint
Resources resources = context.getResources();
- int accuracyColor = resources.getColor(R.color.my_location_ring);
+ int accuracyColor = ContextCompat.getColor(context,R.color.my_location_ring);
- mDensity = resources.getDisplayMetrics().density;
+ float density = resources.getDisplayMetrics().density;
mMarkerCoordinate = new LatLng(0.0, 0.0);
mMarkerScreenPoint = new PointF();
mMarkerScreenMatrix = new Matrix();
@@ -148,7 +148,7 @@ public final class UserLocationView extends View {
mAccuracyPaintStroke = new Paint();
mAccuracyPaintStroke.setAntiAlias(true);
mAccuracyPaintStroke.setStyle(Paint.Style.STROKE);
- mAccuracyPaintStroke.setStrokeWidth(0.5f * mDensity);
+ mAccuracyPaintStroke.setStrokeWidth(0.5f * density);
mAccuracyPaintStroke.setColor(accuracyColor);
mAccuracyPaintStroke.setAlpha((int) (255 * 0.5f));
@@ -195,8 +195,9 @@ public final class UserLocationView extends View {
mUserLocationStaleDrawable.setBounds(mUserLocationStaleDrawableBounds);
}
- public void setMapView(MapView mapView) {
- mMapView = mapView;
+ public void setMapboxMap(MapboxMap mapboxMap) {
+ mMapboxMap = mapboxMap;
+ mProjection = mapboxMap.getProjection();
}
public void onStart() {
@@ -247,21 +248,15 @@ public final class UserLocationView extends View {
if (myLocationTrackingMode != MyLocationTracking.TRACKING_NONE && mUserLocation != null) {
// center map directly if we have a location fix
mMarkerCoordinate = new LatLng(mUserLocation.getLatitude(), mUserLocation.getLongitude());
- mMapView.getMapboxMap().moveCamera(CameraUpdateFactory.newLatLng(new LatLng(mUserLocation)));
+ mMapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(mUserLocation)));
// center view directly
mMarkerScreenMatrix.reset();
- mMarkerScreenMatrix.setTranslate(
- getMeasuredWidth() / 2,
- getMeasuredHeight() / 2);
+ mMarkerScreenPoint = getMarkerScreenPoint();
+ mMarkerScreenMatrix.setTranslate(mMarkerScreenPoint.x, mMarkerScreenPoint.y);
}
}
- @MyLocationTracking.Mode
- public int getMyLocationTrackingMode() {
- return mMyLocationTrackingMode;
- }
-
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -278,7 +273,7 @@ public final class UserLocationView extends View {
// compute new marker position
// TODO add JNI method that takes existing pointf
if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- mMarkerScreenPoint = mMapView.toScreenLocation(mMarkerCoordinate);
+ mMarkerScreenPoint = getMarkerScreenPoint();
mMarkerScreenMatrix.reset();
mMarkerScreenMatrix.setTranslate(
mMarkerScreenPoint.x,
@@ -289,11 +284,11 @@ public final class UserLocationView extends View {
if (mShowDirection) {
bearing = mMyBearingTrackingMode == MyBearingTracking.COMPASS ? mBearingChangeListener.getCompassBearing() : mUserLocation.getBearing();
} else {
- bearing = (float) mMapView.getBearing();
+ bearing = mMapboxMap.getCameraPosition().bearing;
}
if (mCurrentMapViewCoordinate == null) {
- mCurrentMapViewCoordinate = mMapView.getMapboxMap().getCameraPosition().target;
+ mCurrentMapViewCoordinate = mMapboxMap.getCameraPosition().target;
}
// only update if there is an actual change
@@ -302,11 +297,10 @@ public final class UserLocationView extends View {
.target(mMarkerCoordinate)
.bearing(bearing)
.build();
- mMapView.getMapboxMap().animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 300, null);
+ mMapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 300, null);
mMarkerScreenMatrix.reset();
- mMarkerScreenMatrix.setTranslate(
- getMeasuredWidth() / 2,
- getMeasuredHeight() / 2);
+ mMarkerScreenPoint = getMarkerScreenPoint();
+ mMarkerScreenMatrix.setTranslate(mMarkerScreenPoint.x, mMarkerScreenPoint.y);
// set values for next check for actual change
mCurrentMapViewCoordinate = mMarkerCoordinate;
@@ -317,10 +311,10 @@ public final class UserLocationView extends View {
// rotate so arrow in points to bearing
if (mShowDirection) {
if (mMyBearingTrackingMode == MyBearingTracking.COMPASS && mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- mMarkerScreenMatrix.preRotate(mCompassMarkerDirection + (float) mMapView.getDirection());
+ mMarkerScreenMatrix.preRotate(mCompassMarkerDirection + mMapboxMap.getCameraPosition().bearing);
} else if (mMyBearingTrackingMode == MyBearingTracking.GPS) {
if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- mMarkerScreenMatrix.preRotate(mGpsMarkerDirection + (float) mMapView.getDirection());
+ mMarkerScreenMatrix.preRotate(mGpsMarkerDirection + mMapboxMap.getCameraPosition().bearing);
} else {
mMarkerScreenMatrix.preRotate(mGpsMarkerDirection);
}
@@ -331,7 +325,7 @@ public final class UserLocationView extends View {
if (mShowAccuracy && !mStaleMarker) {
mAccuracyPath.reset();
mAccuracyPath.addCircle(0.0f, 0.0f,
- (float) (mMarkerAccuracy / mMapView.getMetersPerPixelAtLatitude(
+ (float) (mMarkerAccuracy / mMapboxMap.getProjection().getMetersPerPixelAtLatitude(
mMarkerCoordinate.getLatitude())),
Path.Direction.CW);
@@ -371,11 +365,11 @@ public final class UserLocationView extends View {
*/
private void toggleGps(boolean enableGps) {
- LocationServices locationServices = LocationServices.getLocationServices(getContext());
+ LocationService locationService = LocationService.getInstance(getContext());
if (enableGps) {
// Set an initial location if one available
- Location lastLocation = locationServices.getLastLocation();
+ Location lastLocation = locationService.getLastLocation();
if (lastLocation != null) {
setLocation(lastLocation);
}
@@ -385,16 +379,16 @@ public final class UserLocationView extends View {
}
// Register for Location Updates
- locationServices.addLocationListener(mUserLocationListener);
+ locationService.addLocationListener(mUserLocationListener);
} else {
// Disable location and user dot
setLocation(null);
// Deregister for Location Updates
- locationServices.removeLocationListener(mUserLocationListener);
+ locationService.removeLocationListener(mUserLocationListener);
}
- locationServices.toggleGPS(enableGps);
+ locationService.toggleGPS(enableGps);
}
public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
@@ -415,11 +409,6 @@ public final class UserLocationView extends View {
update();
}
- @MyBearingTracking.Mode
- public int getMyBearingTrackingMode() {
- return mMyBearingTrackingMode;
- }
-
private class MyBearingListener implements SensorEventListener {
private SensorManager mSensorManager;
@@ -483,9 +472,8 @@ public final class UserLocationView extends View {
SensorManager.getRotationMatrix(mR, null, mLastAccelerometer, mLastMagnetometer);
SensorManager.getOrientation(mR, mOrientation);
float azimuthInRadians = mOrientation[0];
- float azimuthInDegress = (float) (Math.toDegrees(azimuthInRadians) + 360) % 360;
- mCompassBearing = azimuthInDegress;
+ mCompassBearing = (float) (Math.toDegrees(azimuthInRadians) + 360) % 360;
if (mCompassBearing < 0) {
// only allow positive degrees
mCompassBearing += 360;
@@ -515,7 +503,7 @@ public final class UserLocationView extends View {
/**
- * Callback method for receiving location updates from LocationServices.
+ * Callback method for receiving location updates from LocationService.
*
* @param location The new Location data
*/
@@ -670,7 +658,7 @@ public final class UserLocationView extends View {
}
void updateOnNextFrame() {
- mMapView.update();
+ mMapboxMap.invalidate();
}
/**
@@ -770,4 +758,14 @@ public final class UserLocationView extends View {
return mPaused;
}
+ public PointF getMarkerScreenPoint() {
+ if (mMyLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
+ mMarkerScreenPoint = mProjection.toScreenLocation(mMarkerCoordinate);
+ } else {
+ int[] contentPadding = mMapboxMap.getPadding();
+ mMarkerScreenPoint = new PointF(((getMeasuredWidth() + contentPadding[0] - contentPadding[2]) / 2)
+ , ((getMeasuredHeight() - contentPadding[3] + contentPadding[1]) / 2));
+ }
+ return mMarkerScreenPoint;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEvent.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEvent.java
new file mode 100644
index 0000000000..87dfb7ec3c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEvent.java
@@ -0,0 +1,59 @@
+package com.mapbox.mapboxsdk.telemetry;
+
+import java.io.Serializable;
+
+public class MapboxEvent implements Serializable {
+ public static final int VERSION_NUMBER = 1;
+ public static final String MGLMapboxEventsUserAgent = "MapboxEventsAndroid/1.1";
+ public static final String MAPBOX_EVENTS_BASE_URL = "https://api.mapbox.com";
+
+ // Event Types
+ public static final String TYPE_TURNSTILE = "appUserTurnstile";
+ public static final String TYPE_MAP_LOAD = "map.load";
+ public static final String TYPE_MAP_CLICK = "map.click";
+ public static final String TYPE_MAP_DRAGEND = "map.dragend";
+ public static final String TYPE_LOCATION = "location";
+ public static final String TYPE_VISIT = "visit";
+
+ // Event Keys
+ public static final String KEY_LATITUDE = "lat";
+ public static final String KEY_LONGITUDE = "lng";
+ public static final String KEY_SPEED = "speed";
+ public static final String KEY_COURSE = "course";
+ public static final String KEY_ALTITUDE = "altitude";
+ public static final String KEY_HORIZONTAL_ACCURACY = "horizontalAccuracy";
+ public static final String KEY_ZOOM = "zoom";
+
+ public static final String KEY_PUSH_ENABLED = "enabled.push";
+ public static final String KEY_EMAIL_ENABLED = "enabled.email";
+ public static final String KEY_GESTURE_ID = "gesture";
+ public static final String KEY_ARRIVAL_DATE = "arrivalDate";
+ public static final String KEY_DEPARTURE_DATE = "departureDate";
+
+ public static final String GESTURE_SINGLETAP = "SingleTap";
+ public static final String GESTURE_DOUBLETAP = "DoubleTap";
+ public static final String GESTURE_TWO_FINGER_SINGLETAP = "TwoFingerTap";
+ public static final String GESTURE_QUICK_ZOOM = "QuickZoom";
+ public static final String GESTURE_PAN_START = "Pan";
+ public static final String GESTURE_PINCH_START = "Pinch";
+ public static final String GESTURE_ROTATION_START = "Rotation";
+ public static final String GESTURE_PITCH_START = "Pitch";
+
+ // Event Attributes
+ public static final String ATTRIBUTE_EVENT = "event";
+ public static final String ATTRIBUTE_SESSION_ID = "sessionId";
+ public static final String ATTRIBUTE_VERSION = "version";
+ public static final String ATTRIBUTE_CREATED = "created";
+ public static final String ATTRIBUTE_VENDOR_ID = "vendorId";
+ public static final String ATTRIBUTE_APP_BUNDLE_ID = "appBundleId";
+ public static final String ATTRIBUTE_MODEL = "model";
+ public static final String ATTRIBUTE_OPERATING_SYSTEM= "operatingSystem";
+ public static final String ATTRIBUTE_ORIENTATION = "orientation";
+ public static final String ATTRIBUTE_BATTERY_LEVEL = "batteryLevel";
+ public static final String ATTRIBUTE_APPLICATION_STATE = "applicationState";
+ public static final String ATTRIBUTE_RESOLUTION = "resolution";
+ public static final String ATTRIBUTE_ACCESSIBILITY_FONT_SCALE = "accessibilityFontScale";
+ public static final String ATTRIBUTE_CARRIER = "carrier";
+ public static final String ATTRIBUTE_CELLULAR_NETWORK_TYPE = "cellularNetworkType";
+ public static final String ATTRIBUTE_WIFI = "wifi";
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java
new file mode 100644
index 0000000000..a62fdc98c5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/MapboxEventManager.java
@@ -0,0 +1,532 @@
+package com.mapbox.mapboxsdk.telemetry;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.location.Location;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
+import android.os.BatteryManager;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.WindowManager;
+import com.mapbox.mapboxsdk.constants.MapboxConstants;
+import com.mapbox.mapboxsdk.location.LocationService;
+import com.mapbox.mapboxsdk.utils.ApiAccess;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Locale;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.UUID;
+import java.util.Vector;
+import okhttp3.CertificatePinner;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+public class MapboxEventManager {
+
+ private static final String TAG = "MapboxEventManager";
+
+ private static MapboxEventManager mapboxEventManager = null;
+
+ private boolean telemetryEnabled;
+
+ private final Vector<Hashtable<String, Object>> events = new Vector<>();
+ private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+ private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
+
+ private Context context = null;
+ private String accessToken = null;
+ private String eventsURL = MapboxEvent.MAPBOX_EVENTS_BASE_URL;
+
+ private String userAgent = MapboxEvent.MGLMapboxEventsUserAgent;
+
+ private Intent batteryStatus = null;
+
+ private DisplayMetrics displayMetrics = null;
+
+ private String mapboxVendorId = null;
+
+ private String mapboxSessionId = null;
+ private long mapboxSessionIdLastSet = 0;
+ private static long hourInMillis = 1000 * 60 * 60;
+ private static long flushDelayInitialInMillis = 1000 * 10; // 10 Seconds
+ private static long flushDelayInMillis = 1000 * 60 * 2; // 2 Minutes
+ private static final int SESSION_ID_ROTATION_HOURS = 24;
+
+ private static MessageDigest messageDigest = null;
+
+ private Timer timer = null;
+
+ private MapboxEventManager(@NonNull Context context) {
+ super();
+ this.accessToken = ApiAccess.getToken(context);
+ this.context = context;
+
+ // Setup Message Digest
+ try {
+ messageDigest = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ Log.w(TAG, "Error getting Encryption Algorithm: " + e);
+ }
+
+ SharedPreferences prefs = context.getSharedPreferences(MapboxConstants.MAPBOX_SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE);
+
+ // Determine if Telemetry Should Be Enabled
+ setTelemetryEnabled(prefs.getBoolean(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_ENABLED, true));
+
+ // Load / Create Vendor Id
+ if (prefs.contains(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_VENDORID)) {
+ mapboxVendorId = prefs.getString(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_VENDORID, "Default Value");
+ Log.d(TAG, "Found Vendor Id = " + mapboxVendorId);
+ } else {
+ String vendorId = UUID.randomUUID().toString();
+ vendorId = encodeString(vendorId);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_VENDORID, vendorId);
+ editor.apply();
+ editor.commit();
+ Log.d(TAG, "Set New Vendor Id = " + vendorId);
+ }
+
+ // Create Initial Session Id
+ rotateSessionId();
+
+ // Get DisplayMetrics Setup
+ displayMetrics = new DisplayMetrics();
+ ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(displayMetrics);
+
+ // Check for Staging Server Information
+ try {
+ ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
+ String stagingURL = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_STAGING_SERVER);
+ String stagingAccessToken = appInfo.metaData.getString(MapboxConstants.KEY_META_DATA_STAGING_ACCESS_TOKEN);
+ String appName = context.getPackageManager().getApplicationLabel(appInfo).toString();
+ PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
+ String versionName = packageInfo.versionName;
+ int versionCode = packageInfo.versionCode;
+
+ if (!TextUtils.isEmpty(stagingURL)) {
+ eventsURL = stagingURL;
+ }
+
+ if (!TextUtils.isEmpty(stagingAccessToken)) {
+ this.accessToken = stagingAccessToken;
+ }
+
+ // Build User Agent
+ if (!TextUtils.isEmpty(appName) && !TextUtils.isEmpty(versionName)) {
+ userAgent = appName + "/" + versionName + "/" + versionCode + " " + userAgent;
+ }
+
+ } catch (Exception e) {
+ Log.e(TAG, "Error Trying to load Staging Credentials: " + e.toString());
+ }
+
+ // Register for battery updates
+ IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ batteryStatus = context.registerReceiver(null, iFilter);
+ }
+
+ /**
+ * Primary Access method using Singleton pattern
+ * @param context Application Context
+ * @return MapboxEventManager
+ */
+ public static MapboxEventManager getMapboxEventManager(@NonNull Context context) {
+ if (mapboxEventManager == null) {
+ mapboxEventManager = new MapboxEventManager(context.getApplicationContext());
+ }
+ return mapboxEventManager;
+ }
+
+ public boolean isTelemetryEnabled() {
+ return telemetryEnabled;
+ }
+
+ /**
+ * Enables / Disables Telemetry
+ * @param telemetryEnabled True to start telemetry, false to stop it
+ */
+ public void setTelemetryEnabled(boolean telemetryEnabled) {
+ if (this.telemetryEnabled == telemetryEnabled) {
+ Log.i(TAG, "no need to start / stop telemetry as it's already in that state.");
+ return;
+ }
+
+ if (telemetryEnabled) {
+ Log.i(TAG, "Starting Telemetry Up!");
+ // Start It Up
+ context.startService(new Intent(context, TelemetryService.class));
+
+ // Make sure Ambient Mode is started at a minimum
+ if (LocationService.getInstance(context).isGPSEnabled()) {
+ LocationService.getInstance(context).toggleGPS(false);
+ }
+
+ // Manage Timer Flush
+ timer = new Timer();
+ timer.schedule(new FlushEventsTimerTask(), flushDelayInitialInMillis, flushDelayInMillis);
+ } else {
+ Log.i(TAG, "Shutting Telemetry Down");
+ // Shut It Down
+ events.removeAllElements();
+ context.stopService(new Intent(context, TelemetryService.class));
+
+ if (timer != null) {
+ timer.cancel();
+ timer = null;
+ }
+ }
+
+ // Persist
+ this.telemetryEnabled = telemetryEnabled;
+ SharedPreferences prefs = context.getSharedPreferences(MapboxConstants.MAPBOX_SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(MapboxConstants.MAPBOX_SHARED_PREFERENCE_KEY_TELEMETRY_ENABLED, telemetryEnabled);
+ editor.apply();
+ editor.commit();
+ }
+
+ /**
+ * Adds a Location Event to the system for processing
+ * @param location Location event
+ */
+ public void addLocationEvent(Location location) {
+ // Add Location even to queue
+ Hashtable<String, Object> event = new Hashtable<>();
+ event.put(MapboxEvent.KEY_LATITUDE, location.getLatitude());
+ event.put(MapboxEvent.KEY_LONGITUDE, location.getLongitude());
+ event.put(MapboxEvent.KEY_SPEED, location.getSpeed());
+ event.put(MapboxEvent.KEY_COURSE, location.getBearing());
+ event.put(MapboxEvent.KEY_ALTITUDE, location.getAltitude());
+ event.put(MapboxEvent.KEY_HORIZONTAL_ACCURACY, location.getAccuracy());
+ event.put(MapboxEvent.ATTRIBUTE_CREATED, dateFormat.format(new Date()));
+ event.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_LOCATION);
+
+ events.add(event);
+
+ rotateSessionId();
+ }
+
+ /**
+ * Push Interactive Events to the system for processing
+ * @param eventWithAttributes Event with attributes
+ */
+ public void pushEvent(Hashtable<String, Object> eventWithAttributes) {
+
+ if (eventWithAttributes == null) {
+ return;
+ }
+
+ String eventType = (String)eventWithAttributes.get(MapboxEvent.ATTRIBUTE_EVENT);
+ if (!TextUtils.isEmpty(eventType) && eventType.equalsIgnoreCase(MapboxEvent.TYPE_MAP_LOAD)) {
+ pushTurnstileEvent();
+ }
+
+ events.add(eventWithAttributes);
+ }
+
+ /**
+ * Pushes turnstile event for internal billing purposes
+ */
+ private void pushTurnstileEvent() {
+
+ Hashtable<String, Object> event = new Hashtable<>();
+ event.put(MapboxEvent.ATTRIBUTE_EVENT, MapboxEvent.TYPE_TURNSTILE);
+ event.put(MapboxEvent.ATTRIBUTE_CREATED, dateFormat.format(new Date()));
+/*
+ // Already set by processing
+ event.put(MapboxEvent.ATTRIBUTE_APP_BUNDLE_ID, context.getPackageName());
+ event.put(MapboxEvent.ATTRIBUTE_VERSION, MapboxEvent.VERSION_NUMBER);
+ event.put(MapboxEvent.ATTRIBUTE_VENDOR_ID, mapboxVendorId);
+*/
+
+ events.add(event);
+
+ // Send to Server Immediately
+ new FlushTheEventsTask().execute();
+ Log.d(TAG, "turnstile event pushed.");
+ }
+
+ /**
+ * SHA-1 Encoding for strings
+ * @param string String to encode
+ * @return String encoded if no error, original string if error
+ */
+ private String encodeString(String string) {
+ try {
+ if (messageDigest != null) {
+ messageDigest.reset();
+ messageDigest.update(string.getBytes("UTF-8"));
+ byte[] bytes = messageDigest.digest();
+
+ // Get the Hex version of the digest
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append( String.format("%02X", b) );
+ }
+ String hex = sb.toString();
+ Log.d(TAG, "original = " + string + "; hex = " + hex);
+
+ return hex;
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Error encoding string, will return in original form." + e);
+ }
+ return string;
+ }
+
+ /**
+ * Changes Session Id based on time boundary
+ */
+ private void rotateSessionId() {
+ long now = System.currentTimeMillis();
+ if (now - mapboxSessionIdLastSet > (SESSION_ID_ROTATION_HOURS * hourInMillis)) {
+ mapboxSessionId = UUID.randomUUID().toString();
+ mapboxSessionIdLastSet = System.currentTimeMillis();
+ }
+ }
+
+ private String getOrientation() {
+ switch (context.getResources().getConfiguration().orientation) {
+ case Configuration.ORIENTATION_LANDSCAPE:
+ return "Landscape";
+ case Configuration.ORIENTATION_PORTRAIT:
+ return "Portrait";
+ default:
+ return "Undefined";
+ }
+ }
+
+ private int getBatteryLevel() {
+ int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+
+ return Math.round((level / (float)scale) * 100);
+ }
+
+ private String getApplicationState() {
+
+ ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
+ if (appProcesses == null) {
+ return "Unknown";
+ }
+ final String packageName = context.getPackageName();
+ for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
+ if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
+ return "Foreground";
+ }
+ }
+ return "Background";
+ }
+
+ private float getAccesibilityFontScaleSize() {
+ // Values
+ // Small = 0.85
+ // Normal = 1.0
+ // Large = 1.15
+ // Huge = 1.3
+
+ return context.getResources().getConfiguration().fontScale;
+ }
+
+ private String getCellularCarrier() {
+ TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+ String carrierName = manager.getNetworkOperatorName();
+ if (TextUtils.isEmpty(carrierName)) {
+ carrierName = "None";
+ }
+ return carrierName;
+ }
+
+ private String getCellularNetworkType () {
+ TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+ switch (manager.getNetworkType()) {
+ case TelephonyManager.NETWORK_TYPE_1xRTT:
+ return "1xRTT";
+ case TelephonyManager.NETWORK_TYPE_CDMA:
+ return "CDMA";
+ case TelephonyManager.NETWORK_TYPE_EDGE:
+ return "EDGE";
+ case TelephonyManager.NETWORK_TYPE_EHRPD:
+ return "EHRPD";
+ case TelephonyManager.NETWORK_TYPE_EVDO_0:
+ return "EVDO_0";
+ case TelephonyManager.NETWORK_TYPE_EVDO_A:
+ return "EVDO_A";
+ case TelephonyManager.NETWORK_TYPE_EVDO_B:
+ return "EVDO_B";
+ case TelephonyManager.NETWORK_TYPE_GPRS:
+ return "GPRS";
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ return "HSDPA";
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ return "HSPA";
+ case TelephonyManager.NETWORK_TYPE_HSPAP:
+ return "HSPAP";
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ return "HSUPA";
+ case TelephonyManager.NETWORK_TYPE_IDEN:
+ return "IDEN";
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ return "LTE";
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ return "UMTS";
+ case TelephonyManager.NETWORK_TYPE_UNKNOWN:
+ return "Unknown";
+ default:
+ return "Default Unknown";
+ }
+ }
+
+
+ public String getConnectedToWifi() {
+
+ String status = "No";
+ WifiManager wifiMgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ if (wifiMgr.isWifiEnabled()) {
+ try {
+ WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
+ if( wifiInfo.getNetworkId() != -1 ){
+ status = "Yes";
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Error getting Wifi Connection Status: " + e);
+ status = "Unknown";
+ }
+ }
+
+ return status;
+ }
+
+ /**
+ * Task responsible for converting stored events and sending them to the server
+ */
+ private class FlushTheEventsTask extends AsyncTask<Void, Void, Void> {
+
+ @Override
+ protected Void doInBackground(Void... voids) {
+
+ if (events.size() < 1) {
+ Log.i(TAG, "No events in the queue to send so returning.");
+ return null;
+ }
+
+ // Check for NetworkConnectivity
+ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+ if (networkInfo == null || !networkInfo.isConnected()) {
+ Log.w(TAG, "Not connected to network, so returning without attempting to send events");
+ return null;
+ }
+
+ try {
+ // Send data
+ // =========
+ JSONArray jsonArray = new JSONArray();
+
+ for (Hashtable<String, Object> evt : events) {
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put(MapboxEvent.KEY_LATITUDE, evt.get(MapboxEvent.KEY_LATITUDE));
+ jsonObject.put(MapboxEvent.KEY_LONGITUDE, evt.get(MapboxEvent.KEY_LONGITUDE));
+ jsonObject.put(MapboxEvent.KEY_SPEED, evt.get(MapboxEvent.KEY_SPEED));
+ jsonObject.put(MapboxEvent.KEY_COURSE, evt.get(MapboxEvent.KEY_COURSE));
+ jsonObject.put(MapboxEvent.KEY_ALTITUDE, evt.get(MapboxEvent.KEY_ALTITUDE));
+ jsonObject.put(MapboxEvent.KEY_HORIZONTAL_ACCURACY, evt.get(MapboxEvent.KEY_HORIZONTAL_ACCURACY));
+ jsonObject.put(MapboxEvent.KEY_ZOOM, evt.get(MapboxEvent.KEY_ZOOM));
+
+ // Basic Event Meta Data
+ jsonObject.put(MapboxEvent.ATTRIBUTE_EVENT, evt.get(MapboxEvent.ATTRIBUTE_EVENT));
+ jsonObject.put(MapboxEvent.ATTRIBUTE_CREATED, evt.get(MapboxEvent.ATTRIBUTE_CREATED));
+ jsonObject.put(MapboxEvent.ATTRIBUTE_SESSION_ID, encodeString(mapboxSessionId));
+ jsonObject.put(MapboxEvent.ATTRIBUTE_VERSION, MapboxEvent.VERSION_NUMBER);
+ jsonObject.put(MapboxEvent.ATTRIBUTE_VENDOR_ID, mapboxVendorId);
+ jsonObject.put(MapboxEvent.ATTRIBUTE_APP_BUNDLE_ID, context.getPackageName());
+ jsonObject.put(MapboxEvent.ATTRIBUTE_MODEL, Build.MODEL);
+ jsonObject.put(MapboxEvent.ATTRIBUTE_OPERATING_SYSTEM, Build.VERSION.RELEASE);
+ jsonObject.put(MapboxEvent.ATTRIBUTE_ORIENTATION, getOrientation());
+ jsonObject.put(MapboxEvent.ATTRIBUTE_BATTERY_LEVEL, getBatteryLevel());
+ jsonObject.put(MapboxEvent.ATTRIBUTE_APPLICATION_STATE, getApplicationState());
+ jsonObject.put(MapboxEvent.ATTRIBUTE_RESOLUTION, displayMetrics.density);
+ jsonObject.put(MapboxEvent.ATTRIBUTE_ACCESSIBILITY_FONT_SCALE, getAccesibilityFontScaleSize());
+ jsonObject.put(MapboxEvent.ATTRIBUTE_CARRIER, getCellularCarrier());
+ jsonObject.put(MapboxEvent.ATTRIBUTE_CELLULAR_NETWORK_TYPE, getCellularNetworkType());
+ jsonObject.put(MapboxEvent.ATTRIBUTE_WIFI, getConnectedToWifi());
+
+ jsonArray.put(jsonObject);
+ }
+
+ // Based on http://square.github.io/okhttp/3.x/okhttp/okhttp3/CertificatePinner.html
+ CertificatePinner certificatePinner = new CertificatePinner.Builder()
+ .add("cloudfront-staging.tilestream.net", "sha1/KcdiTca54HxWTV8VuAd67x8I=")
+ .add("cloudfront-staging.tilestream.net", "sha1//KDE76PP0DQBDcTnMFBv+efp4eg=")
+ .add("api.mapbox.com", "sha1/Uv71ooi32pyba+oLD7egnXm7/GQ=")
+ .add("api.mapbox.com", "sha1/hOP0d37/ZTSGgCSseE3DIZ1uSg0=")
+ .build();
+
+ OkHttpClient client = new OkHttpClient.Builder().certificatePinner(certificatePinner).build();
+ RequestBody body = RequestBody.create(JSON, jsonArray.toString());
+
+ String url = eventsURL + "/events/v1?access_token=" + accessToken;
+ Log.d(TAG, "url = " + url);
+
+ Request request = new Request.Builder()
+ .url(url)
+ .header("User-Agent", userAgent)
+ .post(body)
+ .build();
+ Response response = client.newCall(request).execute();
+ Log.d(TAG, "Response Code from Mapbox Events Server: " + response.code() + " for " + events.size() + " events sent in.");
+
+ // Reset Events
+ // ============
+ events.removeAllElements();
+ } catch (Exception e) {
+ Log.e(TAG, "FlushTheEventsTask borked: " + e);
+ }
+
+ return null;
+ }
+
+ }
+
+
+ /**
+ * TimerTask responsible for sending event data to server
+ */
+ private class FlushEventsTimerTask extends TimerTask {
+ /**
+ * The task to run should be specified in the implementation of the {@code run()}
+ * method.
+ */
+ @Override
+ public void run() {
+ new FlushTheEventsTask().execute();
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java
new file mode 100644
index 0000000000..088d41be54
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryLocationReceiver.java
@@ -0,0 +1,71 @@
+package com.mapbox.mapboxsdk.telemetry;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Location;
+import android.location.LocationManager;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
+import android.os.Handler;
+import android.util.Log;
+
+public class TelemetryLocationReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "TelemLocationReceiver";
+
+ public static final String INTENT_STRING = "com.mapbox.mapboxsdk.telemetry.TelemetryLocationReceiver";
+
+ /**
+ * Default Constructor
+ */
+ public TelemetryLocationReceiver() {
+ super();
+ }
+
+ /**
+ * This method is called when the BroadcastReceiver is receiving an Intent
+ * broadcast. During this time you can use the other methods on
+ * BroadcastReceiver to view/modify the current result values. This method
+ * is always called within the main thread of its process, unless you
+ * explicitly asked for it to be scheduled on a different thread using
+ * {@link Context#registerReceiver(BroadcastReceiver,
+ * IntentFilter, String, Handler)}. When it runs on the main
+ * thread you should
+ * never perform long-running operations in it (there is a timeout of
+ * 10 seconds that the system allows before considering the receiver to
+ * be blocked and a candidate to be killed). You cannot launch a popup dialog
+ * in your implementation of onReceive().
+ * <p/>
+ * <p><b>If this BroadcastReceiver was launched through a &lt;receiver&gt; tag,
+ * then the object is no longer alive after returning from this
+ * function.</b> This means you should not perform any operations that
+ * return a result to you asynchronously -- in particular, for interacting
+ * with services, you should use
+ * {@link Context#startService(Intent)} instead of
+ * {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish
+ * to interact with a service that is already running, you can use
+ * {@link #peekService}.
+ * <p/>
+ * <p>The Intent filters used in {@link Context#registerReceiver}
+ * and in application manifests are <em>not</em> guaranteed to be exclusive. They
+ * are hints to the operating system about how to find suitable recipients. It is
+ * possible for senders to force delivery to specific recipients, bypassing filter
+ * resolution. For this reason, {@link #onReceive(Context, Intent) onReceive()}
+ * implementations should respond only to known actions, ignoring any unexpected
+ * Intents that they may receive.
+ *
+ * @param context The Context in which the receiver is running.
+ * @param intent The Intent being received.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Location location = (Location)intent.getExtras().get(LocationManager.KEY_LOCATION_CHANGED);
+ if (location != null) {
+ Log.d(TAG, "location received = " + location);
+ MapboxEventManager.getMapboxEventManager(context).addLocationEvent(location);
+ } else {
+ Log.d(TAG, "location NOT received");
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java
new file mode 100644
index 0000000000..56006dadf6
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/TelemetryService.java
@@ -0,0 +1,145 @@
+package com.mapbox.mapboxsdk.telemetry;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
+import android.os.AsyncTask;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+public class TelemetryService extends Service {
+
+ private static final String TAG = "TelemetryService";
+
+ private TelemetryLocationReceiver telemetryLocationReceiver = null;
+ private PowerManager.WakeLock telemetryWakeLock;
+
+ /**
+ * Return the communication channel to the service. May return null if
+ * clients can not bind to the service. The returned
+ * {@link IBinder} is usually for a complex interface
+ * that has been <a href="{@docRoot}guide/components/aidl.html">described using
+ * aidl</a>.
+ * <p/>
+ * <p><em>Note that unlike other application components, calls on to the
+ * IBinder interface returned here may not happen on the main thread
+ * of the process</em>. More information about the main thread can be found in
+ * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and
+ * Threads</a>.</p>
+ *
+ * @param intent The Intent that was used to bind to this service,
+ * as given to {@link Context#bindService
+ * Context.bindService}. Note that any extras that were included with
+ * the Intent at that point will <em>not</em> be seen here.
+ * @return Return an IBinder through which clients can call on to the
+ * service.
+ */
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+
+ /**
+ * Called by the system when the service is first created. Do not call this method directly.
+ */
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ Log.i(TAG, "onCreate() called");
+
+ // Enable Location Listening for lifecycle of app
+ IntentFilter filter = new IntentFilter(TelemetryLocationReceiver.INTENT_STRING);
+ telemetryLocationReceiver = new TelemetryLocationReceiver();
+ registerReceiver(telemetryLocationReceiver, filter);
+ }
+
+ /**
+ * Called by the system to notify a Service that it is no longer used and is being removed. The
+ * service should clean up any resources it holds (threads, registered
+ * receivers, etc) at this point. Upon return, there will be no more calls
+ * in to this Service object and it is effectively dead. Do not call this method directly.
+ */
+ @Override
+ public void onDestroy() {
+ shutdownTelemetry();
+ super.onDestroy();
+ }
+
+ /**
+ * This is called if the service is currently running and the user has
+ * removed a task that comes from the service's application. If you have
+ * set {@link ServiceInfo#FLAG_STOP_WITH_TASK ServiceInfo.FLAG_STOP_WITH_TASK}
+ * then you will not receive this callback; instead, the service will simply
+ * be stopped.
+ *
+ * @param rootIntent The original root Intent that was used to launch
+ * the task that is being removed.
+ */
+ @Override
+ public void onTaskRemoved(Intent rootIntent) {
+ shutdownTelemetry();
+ super.onTaskRemoved(rootIntent);
+ }
+
+ /**
+ * Called by the system every time a client explicitly starts the service by calling
+ * {@link Context#startService}, providing the arguments it supplied and a
+ * unique integer token representing the start request. Do not call this method directly.
+ * <p/>
+ * <p>For backwards compatibility, the default implementation calls
+ * {@link #onStart} and returns either {@link #START_STICKY}
+ * or {@link #START_STICKY_COMPATIBILITY}.
+ * <p/>
+ * <p>If you need your application to run on platform versions prior to API
+ * level 5, you can use the following model to handle the older {@link #onStart}
+ * callback in that case. The <code>handleCommand</code> method is implemented by
+ * you as appropriate:
+ * <p/>
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
+ * start_compatibility}
+ * <p/>
+ * <p class="caution">Note that the system calls this on your
+ * service's main thread. A service's main thread is the same
+ * thread where UI operations take place for Activities running in the
+ * same process. You should always avoid stalling the main
+ * thread's event loop. When doing long-running operations,
+ * network calls, or heavy disk I/O, you should kick off a new
+ * thread, or use {@link AsyncTask}.</p>
+ *
+ * @param intent The Intent supplied to {@link Context#startService},
+ * as given. This may be null if the service is being restarted after
+ * its process has gone away, and it had previously returned anything
+ * except {@link #START_STICKY_COMPATIBILITY}.
+ * @param flags Additional data about this start request. Currently either
+ * 0, {@link #START_FLAG_REDELIVERY}, or {@link #START_FLAG_RETRY}.
+ * @param startId A unique integer representing this specific request to
+ * start. Use with {@link #stopSelfResult(int)}.
+ * @return The return value indicates what semantics the system should
+ * use for the service's current started state. It may be one of the
+ * constants associated with the {@link #START_CONTINUATION_MASK} bits.
+ * @see #stopSelfResult(int)
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+
+ Log.i(TAG, "onStartCommand() called");
+
+ // Start WakeLock to keep Location Data working when device sleeps
+ PowerManager mgr = (PowerManager)getSystemService(Context.POWER_SERVICE);
+ telemetryWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TelemetryWakeLock");
+ telemetryWakeLock.acquire();
+
+ return START_NOT_STICKY;
+ }
+
+ private void shutdownTelemetry() {
+ unregisterReceiver(telemetryLocationReceiver);
+ telemetryWakeLock.release();
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/package-info.java
new file mode 100644
index 0000000000..d6cb1ca852
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/telemetry/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * This package contains the classes that manage the SDK's Telemetry services.
+ */
+package com.mapbox.mapboxsdk.telemetry;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ApiAccess.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ApiAccess.java
index 137d0730c3..4f968c3a13 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ApiAccess.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ApiAccess.java
@@ -37,7 +37,7 @@ public final class ApiAccess {
return token;
} catch (Exception e) {
// use fallback on string resource, used for development
- int tokenResId = context.getResources().getIdentifier("access_token", "string", context.getPackageName());
+ int tokenResId = context.getResources().getIdentifier("mapbox_access_token", "string", context.getPackageName());
return tokenResId != 0 ? context.getString(tokenResId) : null;
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
index 777d879d48..be0038e8b8 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
@@ -3,12 +3,12 @@
<!--Add references to exposed resources-->
<public name="AttributionAlertDialogStyle" type="style" />
- <public name="style_mapbox_streets" type="string" />
- <public name="style_emerald" type="string" />
- <public name="style_light" type="string" />
- <public name="style_dark" type="string" />
- <public name="style_satellite" type="string" />
- <public name="style_satellite_streets" type="string" />
+ <public name="mapbox_style_mapbox_streets" type="string" />
+ <public name="mapbox_style_emerald" type="string" />
+ <public name="mapbox_style_light" type="string" />
+ <public name="mapbox_style_dark" type="string" />
+ <public name="mapbox_style_satellite" type="string" />
+ <public name="mapbox_style_satellite_streets" type="string" />
<public name="center_longitude" type="attr" />
<public name="center_latitude" type="attr" />
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/attribution_telemetry_view.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/attribution_telemetry_view.xml
new file mode 100644
index 0000000000..04c47af90d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/attribution_telemetry_view.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ >
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/mapbox_attributionTelemetryMessage"
+ android:gravity="center_horizontal"/>
+
+ <ListView
+ android:id="@+id/telemetryOptionsList"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
+
+</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/infowindow_content.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/infowindow_content.xml
index 3e36cbf91a..a4fcc80681 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/infowindow_content.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/infowindow_content.xml
@@ -18,7 +18,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:maxEms="17"
- android:text="@string/infoWindowTitle"
+ android:text="@string/mapbox_infoWindowTitle"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
@@ -31,7 +31,7 @@
android:layout_marginTop="2dp"
android:lineSpacingExtra="1dp"
android:maxEms="17"
- android:text="@string/infoWindowDescription"
+ android:text="@string/mapbox_infoWindowDescription"
android:textColor="@color/gray"
android:textSize="14sp" />
@@ -40,7 +40,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxEms="17"
- android:text="@string/infoWindowAddress"
+ android:text="@string/mapbox_infoWindowAddress"
android:textColor="@color/black"
android:textSize="12sp"
android:visibility="gone" />
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml
index d62fd9cfba..288fb441ad 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapview_internal.xml
@@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <com.mapbox.mapboxsdk.maps.CompassView
+ <com.mapbox.mapboxsdk.maps.widgets.CompassView
android:id="@+id/compassView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
@@ -15,7 +15,7 @@
android:id="@+id/logoView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:contentDescription="@string/mapboxIconContentDescription"
+ android:contentDescription="@string/mapbox_mapboxIconContentDescription"
android:src="@drawable/attribution_logo" />
<ImageView
@@ -24,12 +24,12 @@
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:clickable="true"
- android:contentDescription="@string/attributionsIconContentDescription"
+ android:contentDescription="@string/mapbox_attributionsIconContentDescription"
android:padding="7dp"
android:src="@drawable/ic_info_outline_24dp_selector"
android:background="@drawable/bg_default_selector"/>
- <com.mapbox.mapboxsdk.maps.UserLocationView
+ <com.mapbox.mapboxsdk.maps.widgets.UserLocationView
android:id="@+id/userLocationView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/arrays.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/arrays.xml
index 2c1fdf8d13..d5d26d09d2 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/arrays.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/arrays.xml
@@ -4,11 +4,23 @@
<item>&#169; Mapbox</item>
<item>&#169; OpenStreetMap</item>
<item>Improve this map</item>
+ <item>Telemetry Settings</item>
+ </array>
+ <array name="attribution_telemetry_options">
+ <item>Tell Me More</item>
+ <item>Don\'t Participate</item>
+ <item>Participate</item>
+ </array>
+ <array name="attribution_telemetry_options_already_participating">
+ <item>Tell Me More</item>
+ <item>Stop Participating</item>
+ <item>Keep Participating</item>
</array>
<!-- If editing this array update MapView.ATTRIBUTION_INDEX_IMPROVE_THIS_MAP -->
<array name="attribution_links" formatted="false" translatable="false">
<item>https://www.mapbox.com/about/maps/</item>
<item>http://www.openstreetmap.org/about/</item>
<item>https://www.mapbox.com/map-feedback/#/%1$f/%2$f/%3$d</item>
+ <item>https://www.mapbox.com/telemetry/</item>
</array>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
index 63547247f3..f516e98dd8 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
@@ -4,6 +4,8 @@
<attr name="center_longitude" format="float" />
<attr name="center_latitude" format="float" />
<attr name="zoom" format="float" />
+ <attr name="zoom_max" format="float" />
+ <attr name="zoom_min" format="float" />
<attr name="direction" format="float" />
<attr name="tilt" format="float" />
<attr name="zoom_enabled" format="boolean" />
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
index fd612d511c..5167ecf936 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
@@ -5,7 +5,8 @@
<dimen name="infowindow_offset">-2dp</dimen>
<dimen name="infowindow_line_width">1.5dp</dimen>
<dimen name="seven_dp">7dp</dimen>
+ <dimen name="eight_dp">8dp</dimen>
<dimen name="ten_dp">10dp</dimen>
<dimen name="sixteen_dp">16dp</dimen>
<dimen name="seventy_six_dp">76dp</dimen>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
index eadcdcc043..f852c305b2 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml
@@ -1,18 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="compassContentDescription">Map compass. Click to reset the map rotation to North.</string>
- <string name="attributionsIconContentDescription">Attribution icon. Click to show attribution dialog.</string>
- <string name="attributionsDialogTitle">Mapbox Android SDK</string>
- <string name="mapboxIconContentDescription">The Mapbox logo.</string>
- <string name="infoWindowTitle">Title</string>
- <string name="infoWindowDescription">Description</string>
- <string name="infoWindowAddress">Address</string>
+ <string name="mapbox_compassContentDescription">Map compass. Click to reset the map rotation to North.</string>
+ <string name="mapbox_attributionsIconContentDescription">Attribution icon. Click to show attribution dialog.</string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Android SDK</string>
+ <string name="mapbox_attributionTelemetryTitle">Make Mapbox Maps Better</string>
+ <string name="mapbox_attributionTelemetryMessage">You are helping to make OpenStreetMap and Mapbox maps better by contributing anonymous usage data.</string>
+ <string name="mapbox_mapboxIconContentDescription">The Mapbox logo.</string>
+ <string name="mapbox_infoWindowTitle">Title</string>
+ <string name="mapbox_infoWindowDescription">Description</string>
+ <string name="mapbox_infoWindowAddress">Address</string>
<!-- these are public -->
- <string name="style_mapbox_streets">mapbox://styles/mapbox/streets-v8</string>
- <string name="style_emerald">mapbox://styles/mapbox/emerald-v8</string>
- <string name="style_light">mapbox://styles/mapbox/light-v8</string>
- <string name="style_dark">mapbox://styles/mapbox/dark-v8</string>
- <string name="style_satellite">mapbox://styles/mapbox/satellite-v8</string>
- <string name="style_satellite_streets">mapbox://styles/mapbox/satellite-hybrid-v8</string>
+ <string name="mapbox_style_mapbox_streets">mapbox://styles/mapbox/streets-v8</string>
+ <string name="mapbox_style_emerald">mapbox://styles/mapbox/emerald-v8</string>
+ <string name="mapbox_style_light">mapbox://styles/mapbox/light-v8</string>
+ <string name="mapbox_style_dark">mapbox://styles/mapbox/dark-v8</string>
+ <string name="mapbox_style_satellite">mapbox://styles/mapbox/satellite-v8</string>
+ <string name="mapbox_style_satellite_streets">mapbox://styles/mapbox/satellite-hybrid-v8</string>
</resources>