onCameraMoveCanceled = new CopyOnWriteArrayList<>();
@@ -24,6 +31,74 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
private OnCameraMoveListener onCameraMoveListener;
private OnCameraIdleListener onCameraIdleListener;
+ private final Runnable onCameraMoveStartedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // deprecated API
+ if (onCameraMoveStartedListener != null) {
+ onCameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
+ }
+
+ // new API
+ if (!onCameraMoveStarted.isEmpty()) {
+ for (OnCameraMoveStartedListener cameraMoveStartedListener : onCameraMoveStarted) {
+ cameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
+ }
+ }
+ }
+ };
+
+ private final Runnable onCameraMoveRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // deprecated API
+ if (onCameraMoveListener != null && !idle) {
+ onCameraMoveListener.onCameraMove();
+ }
+
+ // new API
+ if (!onCameraMove.isEmpty() && !idle) {
+ for (OnCameraMoveListener cameraMoveListener : onCameraMove) {
+ cameraMoveListener.onCameraMove();
+ }
+ }
+ }
+ };
+
+ private final Runnable onCameraMoveCancelRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // deprecated API
+ if (onCameraMoveCanceledListener != null && !idle) {
+ onCameraMoveCanceledListener.onCameraMoveCanceled();
+ }
+
+ // new API
+ if (!onCameraMoveCanceled.isEmpty() && !idle) {
+ for (OnCameraMoveCanceledListener cameraMoveCanceledListener : onCameraMoveCanceled) {
+ cameraMoveCanceledListener.onCameraMoveCanceled();
+ }
+ }
+ }
+ };
+
+ private final Runnable onCameraIdleRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // deprecated API
+ if (onCameraIdleListener != null) {
+ onCameraIdleListener.onCameraIdle();
+ }
+
+ // new API
+ if (!onCameraIdle.isEmpty()) {
+ for (OnCameraIdleListener cameraIdleListener : onCameraIdle) {
+ cameraIdleListener.onCameraIdle();
+ }
+ }
+ }
+ };
+
@Deprecated
void setOnCameraMoveStartedListener(OnCameraMoveStartedListener onCameraMoveStartedListener) {
this.onCameraMoveStartedListener = onCameraMoveStartedListener;
@@ -45,16 +120,13 @@ void setOnCameraIdleListener(OnCameraIdleListener onCameraIdleListener) {
}
@Override
- public void onCameraMoveStarted(int reason) {
+ public void onCameraMoveStarted(final int reason) {
if (!idle) {
return;
}
idle = false;
-
- // deprecated API
- if (onCameraMoveStartedListener != null) {
- onCameraMoveStartedListener.onCameraMoveStarted(reason);
- }
+ moveStartedReason = reason;
+ handler.post(onCameraMoveStartedRunnable);
// new API
if (!onCameraMoveStarted.isEmpty()) {
@@ -66,7 +138,7 @@ public void onCameraMoveStarted(int reason) {
@Override
public void onCameraMove() {
- // deprecated API
+ handler.post(onCameraMoveRunnable);
if (onCameraMoveListener != null && !idle) {
onCameraMoveListener.onCameraMove();
}
@@ -81,7 +153,7 @@ public void onCameraMove() {
@Override
public void onCameraMoveCanceled() {
- // deprecated API
+ handler.post(onCameraMoveCancelRunnable);
if (onCameraMoveCanceledListener != null && !idle) {
onCameraMoveCanceledListener.onCameraMoveCanceled();
}
@@ -98,7 +170,7 @@ public void onCameraMoveCanceled() {
public void onCameraIdle() {
if (!idle) {
idle = true;
- // deprecated API
+ handler.post(onCameraIdleRunnable);
if (onCameraIdleListener != null) {
onCameraIdleListener.onCameraIdle();
}
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 01c6da4971f..0c11a4220ea 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
@@ -11,6 +11,9 @@
import com.mapbox.mapboxsdk.utils.MapFragmentUtils;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Fragment wrapper around a map view.
*
@@ -25,10 +28,11 @@
*
* @see #getMapAsync(OnMapReadyCallback)
*/
-public final class MapFragment extends Fragment {
+public final class MapFragment extends Fragment implements OnMapReadyCallback {
+ private final List mapReadyCallbackList = new ArrayList<>();
+ private MapboxMap mapboxMap;
private MapView map;
- private OnMapReadyCallback onMapReadyCallback;
/**
* Creates a default MapFragment instance
@@ -63,7 +67,9 @@ public static MapFragment newInstance(@Nullable MapboxMapOptions mapboxMapOption
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Context context = inflater.getContext();
- return map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
+ map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
+ map.setVisibility(View.INVISIBLE);
+ return map;
}
/**
@@ -76,9 +82,16 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
map.onCreate(savedInstanceState);
- if (onMapReadyCallback != null) {
- map.getMapAsync(onMapReadyCallback);
+ map.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ this.mapboxMap = mapboxMap;
+ for (OnMapReadyCallback onMapReadyCallback : mapReadyCallbackList) {
+ onMapReadyCallback.onMapReady(mapboxMap);
}
+ map.setVisibility(View.VISIBLE);
}
/**
@@ -144,6 +157,7 @@ public void onLowMemory() {
public void onDestroyView() {
super.onDestroyView();
map.onDestroy();
+ mapReadyCallbackList.clear();
}
/**
@@ -152,10 +166,10 @@ public void onDestroyView() {
* @param onMapReadyCallback The callback to be invoked.
*/
public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
- if (map == null) {
- this.onMapReadyCallback = onMapReadyCallback;
+ if (mapboxMap == null) {
+ mapReadyCallbackList.add(onMapReadyCallback);
} else {
- map.getMapAsync(onMapReadyCallback);
+ onMapReadyCallback.onMapReady(mapboxMap);
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
index 0fea5ce0ff8..6424de342e3 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
@@ -550,7 +550,7 @@ public boolean onScale(ScaleGestureDetector detector) {
return super.onScale(detector);
}
- wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) >= 0;
+ wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) > 0;
if (tiltGestureOccurred) {
return false;
}
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 bf98e6bbc16..9525d488204 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
@@ -712,19 +712,21 @@ public final void moveCamera(CameraUpdate update) {
* @param callback the callback to be invoked when an animation finishes or is canceled
*/
public final void moveCamera(final CameraUpdate update, final MapboxMap.CancelableCallback callback) {
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- transform.moveCamera(MapboxMap.this, update, callback);
- // MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo`
- // invalidate camera position to provide OnCameraChange event.
- invalidateCameraPosition();
-
- if (callback != null) {
- callback.onFinish();
+ transform.moveCamera(MapboxMap.this, update, callback);
+ // MapChange.REGION_DID_CHANGE_ANIMATED is not called for `jumpTo`
+ // invalidate camera position to provide OnCameraChange event.
+ invalidateCameraPosition();
+
+ if (callback != null) {
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (callback != null) {
+ callback.onFinish();
+ }
}
- }
- });
+ });
+ }
}
/**
@@ -848,12 +850,7 @@ public final void easeCamera(final CameraUpdate update, final int durationMs, fi
if (durationMs <= 0) {
throw new IllegalArgumentException("Null duration passed into easeCamera");
}
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback, isDismissable);
- }
- });
+ transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback, isDismissable);
}
/**
@@ -923,12 +920,8 @@ public final void animateCamera(final CameraUpdate update, final int durationMs,
if (durationMs <= 0) {
throw new IllegalArgumentException("Null duration passed into animageCamera");
}
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- transform.animateCamera(MapboxMap.this, update, durationMs, callback);
- }
- });
+
+ transform.animateCamera(MapboxMap.this, update, durationMs, callback);
}
/**
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
index 2719d7f016b..34be9583290 100644
--- 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
@@ -725,9 +725,12 @@ public MapboxMapOptions setPrefetchesTiles(boolean enable) {
}
/**
- * Set the font-family for generating glyphs locally for ideographs in the ‘CJK Unified Ideographs’
+ * Set the font family for generating glyphs locally for ideographs in the ‘CJK Unified Ideographs’
* and ‘Hangul Syllables’ ranges.
*
+ * The font family argument is passed to {@link android.graphics.Typeface#create(String, int)}.
+ * Default system fonts are defined in '/system/etc/fonts.xml'
+ *
* @param fontFamily font family for local ideograph generation.
* @return This
*/
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 6c90cd95ecb..c072ec82379 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
@@ -11,6 +11,9 @@
import com.mapbox.mapboxsdk.utils.MapFragmentUtils;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Support Fragment wrapper around a map view.
*
@@ -25,10 +28,11 @@
*
* @see #getMapAsync(OnMapReadyCallback)
*/
-public class SupportMapFragment extends Fragment {
+public class SupportMapFragment extends Fragment implements OnMapReadyCallback {
+ private final List mapReadyCallbackList = new ArrayList<>();
+ private MapboxMap mapboxMap;
private MapView map;
- private OnMapReadyCallback onMapReadyCallback;
/**
* Creates a default MapFragment instance
@@ -63,7 +67,9 @@ public static SupportMapFragment newInstance(@Nullable MapboxMapOptions mapboxMa
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Context context = inflater.getContext();
- return map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
+ map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
+ map.setVisibility(View.INVISIBLE);
+ return map;
}
/**
@@ -76,9 +82,16 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
map.onCreate(savedInstanceState);
- if (onMapReadyCallback != null) {
- map.getMapAsync(onMapReadyCallback);
+ map.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ this.mapboxMap = mapboxMap;
+ for (OnMapReadyCallback onMapReadyCallback : mapReadyCallbackList) {
+ onMapReadyCallback.onMapReady(mapboxMap);
}
+ map.setVisibility(View.VISIBLE);
}
/**
@@ -144,6 +157,7 @@ public void onLowMemory() {
public void onDestroyView() {
super.onDestroyView();
map.onDestroy();
+ mapReadyCallbackList.clear();
}
/**
@@ -152,10 +166,10 @@ public void onDestroyView() {
* @param onMapReadyCallback The callback to be invoked.
*/
public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
- if (map == null) {
- this.onMapReadyCallback = onMapReadyCallback;
+ if (mapboxMap == null) {
+ mapReadyCallbackList.add(onMapReadyCallback);
} else {
- map.getMapAsync(onMapReadyCallback);
+ onMapReadyCallback.onMapReady(mapboxMap);
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
index 16c45ebea23..0d3f0d5e5b8 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
@@ -1,6 +1,7 @@
package com.mapbox.mapboxsdk.maps;
import android.graphics.PointF;
+import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
@@ -82,8 +83,15 @@ public void onMapChanged(@MapView.MapChange int change) {
if (change == REGION_DID_CHANGE_ANIMATED) {
updateCameraPosition(invalidateCameraPosition());
if (cameraCancelableCallback != null) {
- cameraCancelableCallback.onFinish();
- cameraCancelableCallback = null;
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (cameraCancelableCallback != null) {
+ cameraCancelableCallback.onFinish();
+ cameraCancelableCallback = null;
+ }
+ }
+ });
}
cameraChangeDispatcher.onCameraIdle();
mapView.removeOnMapChangedListener(this);
@@ -175,8 +183,15 @@ void cancelTransitions() {
// notify animateCamera and easeCamera about cancelling
if (cameraCancelableCallback != null) {
cameraChangeDispatcher.onCameraIdle();
- cameraCancelableCallback.onCancel();
- cameraCancelableCallback = null;
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (cameraCancelableCallback != null) {
+ cameraCancelableCallback.onCancel();
+ cameraCancelableCallback = null;
+ }
+ }
+ });
}
// cancel ongoing transitions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
index aa7934ec1e3..3fe3c7b40df 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
@@ -33,7 +33,6 @@
import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.location.LocationSource;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.Projection;
import com.mapbox.services.android.telemetry.location.LocationEngine;
@@ -162,7 +161,7 @@ private void init(Context context) {
}
@Deprecated
- public void init(LocationSource locationSource) {
+ public void init(LocationEngine locationSource) {
this.locationEngine = locationSource;
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
index 6a2bf6b07b0..f2faabd63b1 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
@@ -204,6 +204,7 @@ public void createOfflineRegion(@NonNull OfflineRegionDefinition definition, @No
}
ConnectivityReceiver.instance(context).activate();
+ FileSource.getInstance(context).activate();
createOfflineRegion(fileSource, definition, metadata, new CreateOfflineRegionCallback() {
@Override
@@ -212,6 +213,7 @@ public void onCreate(final OfflineRegion offlineRegion) {
@Override
public void run() {
ConnectivityReceiver.instance(context).deactivate();
+ FileSource.getInstance(context).deactivate();
callback.onCreate(offlineRegion);
}
});
@@ -223,6 +225,7 @@ public void onError(final String error) {
@Override
public void run() {
ConnectivityReceiver.instance(context).deactivate();
+ FileSource.getInstance(context).deactivate();
callback.onError(error);
}
});
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
index 1b9a1563529..bc9438ee93d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
@@ -400,9 +400,6 @@ public void run() {
* When the operation is complete or encounters an error, the given callback will be
* executed on the main thread.
*
- *
- * After you call this method, you may not call any additional methods on this object.
- *
*
* @param bytes the metadata in bytes
* @param callback the callback to be invoked
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshot.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshot.java
index eb4f94c428b..38c1491461a 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshot.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshot.java
@@ -28,7 +28,7 @@ private MapSnapshot(long nativePtr, Bitmap bitmap, String[] attributions, boolea
}
/**
- * @return the bitmap
+ * @return the large
*/
public Bitmap getBitmap() {
return bitmap;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
index 5deedc3e639..1c59bb468e7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java
@@ -5,19 +5,31 @@
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
+import android.support.v4.content.res.ResourcesCompat;
+import android.text.Html;
import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
import com.mapbox.mapboxsdk.R;
+import com.mapbox.mapboxsdk.attribution.AttributionLayout;
+import com.mapbox.mapboxsdk.attribution.AttributionMeasure;
+import com.mapbox.mapboxsdk.attribution.AttributionParser;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.storage.FileSource;
+import timber.log.Timber;
+
/**
- * The map snapshotter creates a bitmap of the map, rendered
+ * The map snapshotter creates a large of the map, rendered
* off the UI thread. The snapshotter itself must be used on
* the UI thread (for access to the main looper)
*/
@@ -269,43 +281,126 @@ public void cancel() {
* @param mapSnapshot the map snapshot to draw the overlay on
*/
protected void addOverlay(MapSnapshot mapSnapshot) {
- Bitmap original = mapSnapshot.getBitmap();
- Canvas canvas = new Canvas(original);
- addLogo(canvas, original);
+ Bitmap snapshot = mapSnapshot.getBitmap();
+ Canvas canvas = new Canvas(snapshot);
+ int margin = (int) context.getResources().getDisplayMetrics().density * LOGO_MARGIN_DP;
+ drawOverlay(mapSnapshot, snapshot, canvas, margin);
+ }
+
+ private void drawOverlay(MapSnapshot mapSnapshot, Bitmap snapshot, Canvas canvas, int margin) {
+ AttributionMeasure measure = getAttributionMeasure(mapSnapshot, snapshot, margin);
+ AttributionLayout layout = measure.measure();
+ drawLogo(mapSnapshot, canvas, margin, layout);
+ drawAttribution(mapSnapshot, canvas, measure, layout);
+ }
+
+ private AttributionMeasure getAttributionMeasure(MapSnapshot mapSnapshot, Bitmap snapshot, int margin) {
+ Logo logo = createScaledLogo(snapshot);
+ TextView longText = createTextView(mapSnapshot, false, logo.getScale());
+ TextView shortText = createTextView(mapSnapshot, true, logo.getScale());
+
+ return new AttributionMeasure.Builder()
+ .setSnapshot(snapshot)
+ .setLogo(logo.getLarge())
+ .setLogoSmall(logo.getSmall())
+ .setTextView(longText)
+ .setTextViewShort(shortText)
+ .setMarginPadding(margin)
+ .build();
+ }
+
+ private void drawLogo(MapSnapshot mapSnapshot, Canvas canvas, int margin, AttributionLayout layout) {
+ if (mapSnapshot.isShowLogo()) {
+ drawLogo(mapSnapshot.getBitmap(), canvas, margin, layout);
+ }
+ }
+
+ private void drawLogo(Bitmap snapshot, Canvas canvas, int margin, AttributionLayout placement) {
+ Bitmap selectedLogo = placement.getLogo();
+ if (selectedLogo != null) {
+ canvas.drawBitmap(selectedLogo, margin, snapshot.getHeight() - selectedLogo.getHeight() - margin, null);
+ }
+ }
+
+ private void drawAttribution(MapSnapshot mapSnapshot, Canvas canvas,
+ AttributionMeasure measure, AttributionLayout layout) {
+ // draw attribution
+ PointF anchorPoint = layout.getAnchorPoint();
+ if (anchorPoint != null) {
+ drawAttribution(canvas, measure, anchorPoint);
+ } else {
+ Bitmap snapshot = mapSnapshot.getBitmap();
+ Timber.e("Could not generate attribution for snapshot size: %s x %s."
+ + " You are required to provide your own attribution for the used sources: %s",
+ snapshot.getWidth(), snapshot.getHeight(), mapSnapshot.getAttributions());
+ }
+ }
+
+ private void drawAttribution(Canvas canvas, AttributionMeasure measure, PointF anchorPoint) {
+ canvas.save();
+ canvas.translate(anchorPoint.x, anchorPoint.y);
+ measure.getTextView().draw(canvas);
+ canvas.restore();
+ }
+
+ private TextView createTextView(MapSnapshot mapSnapshot, boolean shortText, float scale) {
+ int textColor = ResourcesCompat.getColor(context.getResources(), R.color.mapbox_gray_dark, context.getTheme());
+ int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ TextView textView = new TextView(context);
+ textView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT)
+ );
+ textView.setSingleLine(true);
+ textView.setTextSize(10 * scale);
+ textView.setTextColor(textColor);
+ textView.setBackgroundResource(R.drawable.mapbox_rounded_corner);
+ textView.setText(Html.fromHtml(createAttributionString(mapSnapshot, shortText)));
+ textView.measure(widthMeasureSpec, heightMeasureSpec);
+ textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
+ return textView;
}
/**
- * Draw a logo on the canvas created from the map snapshot.
+ * Create the attribution string.
*
- * @param canvas the canvas to draw the bitmap on
- * @param original the map snapshot image
+ * @param mapSnapshot the map snapshot to create the attribution for
+ * @param shortText indicates if the short variant of the string should be parsed
+ * @return the parsed attribution string
*/
- private void addLogo(Canvas canvas, Bitmap original) {
- DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
- float margin = displayMetrics.density * LOGO_MARGIN_DP;
- Bitmap logo = createScaledLogo(original);
- canvas.drawBitmap(logo, margin, original.getHeight() - logo.getHeight() - margin, null);
+ private String createAttributionString(MapSnapshot mapSnapshot, boolean shortText) {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(mapSnapshot.getAttributions())
+ .withCopyrightSign(false)
+ .withImproveMap(false)
+ .build();
+
+ return attributionParser.createAttributionString(shortText);
}
/**
* Create a scaled logo for a map snapshot.
*
* @param snapshot the map snapshot where the logo should be placed on
- * @return the scaled bitmap logo
+ * @return the scaled large logo
*/
- private Bitmap createScaledLogo(Bitmap snapshot) {
+ private Logo createScaledLogo(@NonNull Bitmap snapshot) {
Bitmap logo = BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_icon, null);
float scale = calculateLogoScale(snapshot, logo);
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
- return Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true);
+ Bitmap helmet = BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_helmet, null);
+ Bitmap large = Bitmap.createBitmap(logo, 0, 0, logo.getWidth(), logo.getHeight(), matrix, true);
+ Bitmap small = Bitmap.createBitmap(helmet, 0, 0, helmet.getWidth(), helmet.getHeight(), matrix, true);
+ return new Logo(large, small, scale);
}
/**
* Calculates the scale of the logo, only allow downscaling.
*
- * @param snapshot the bitmap of the map snapshot
- * @param logo the bitmap of the mapbox logo
+ * @param snapshot the large of the map snapshot
+ * @param logo the large of the mapbox logo
* @return the scale value
*/
private float calculateLogoScale(Bitmap snapshot, Bitmap logo) {
@@ -315,7 +410,14 @@ private float calculateLogoScale(Bitmap snapshot, Bitmap logo) {
float prefWidth = logo.getWidth() / widthRatio;
float prefHeight = logo.getHeight() / heightRatio;
float calculatedScale = Math.min(prefWidth / logo.getWidth(), prefHeight / logo.getHeight()) * 2;
- return calculatedScale < 1 ? calculatedScale : 1.0f;
+ if (calculatedScale > 1) {
+ // don't allow over-scaling
+ calculatedScale = 1.0f;
+ } else if (calculatedScale < 0.60f) {
+ // don't scale to low either
+ calculatedScale = 0.60f;
+ }
+ return calculatedScale;
}
/**
@@ -324,14 +426,17 @@ private float calculateLogoScale(Bitmap snapshot, Bitmap logo) {
*
* @param snapshot the generated snapshot
*/
- protected void onSnapshotReady(MapSnapshot snapshot) {
- if (callback != null) {
- if (snapshot.isShowLogo()) {
- addOverlay(snapshot);
+ protected void onSnapshotReady(final MapSnapshot snapshot) {
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (callback != null) {
+ addOverlay(snapshot);
+ callback.onSnapshotReady(snapshot);
+ reset();
+ }
}
- callback.onSnapshotReady(snapshot);
- reset();
- }
+ });
}
/**
@@ -364,4 +469,28 @@ protected native void nativeInitialize(MapSnapshotter mapSnapshotter,
@Override
protected native void finalize() throws Throwable;
+
+ private class Logo {
+ private Bitmap large;
+ private Bitmap small;
+ private float scale;
+
+ public Logo(Bitmap large, Bitmap small, float scale) {
+ this.large = large;
+ this.small = small;
+ this.scale = scale;
+ }
+
+ public Bitmap getLarge() {
+ return large;
+ }
+
+ public Bitmap getSmall() {
+ return small;
+ }
+
+ public float getScale() {
+ return scale;
+ }
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java
index 920a1270ac3..181d28191ad 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java
@@ -14,14 +14,14 @@ public class LocalGlyphRasterizer {
/***
* Uses Android-native drawing code to rasterize a single glyph
- * to a square @{link Bitmap} which can be returned to portable
+ * to a square {@link Bitmap} which can be returned to portable
* code for transformation into a Signed Distance Field glyph.
*
* @param fontFamily Font family string to pass to Typeface.create
* @param bold If true, use Typeface.BOLD option
* @param glyphID 16-bit Unicode BMP codepoint to draw
*
- * @return Return a @{link Bitmap} to be displayed in the requested tile.
+ * @return Return a {@link Bitmap} to be displayed in the requested tile.
*/
@WorkerThread
protected static Bitmap drawGlyphBitmap(String fontFamily, boolean bold, char glyphID) {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java
index c7d7a13a3de..f17d4574d54 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java
@@ -1,7 +1,7 @@
package com.mapbox.mapboxsdk.utils;
/**
- * Comparisons from std sdk, which aren't available in API level <= 15
+ * Comparisons from std sdk, which aren't available in API level 15 and below
*/
public class Compare {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_logo_helmet.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_logo_helmet.png
new file mode 100644
index 00000000000..2629afe6a38
Binary files /dev/null and b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_logo_helmet.png differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_logo_helmet.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_logo_helmet.png
new file mode 100644
index 00000000000..34bab3bf6d1
Binary files /dev/null and b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_logo_helmet.png differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_logo_helmet.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_logo_helmet.png
new file mode 100644
index 00000000000..942d78ec585
Binary files /dev/null and b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_logo_helmet.png differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_logo_helmet.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_logo_helmet.png
new file mode 100644
index 00000000000..947d5a2a307
Binary files /dev/null and b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_logo_helmet.png differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_logo_helmet.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_logo_helmet.png
new file mode 100644
index 00000000000..bec38cedc7c
Binary files /dev/null and b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_logo_helmet.png differ
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_rounded_corner.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_rounded_corner.xml
new file mode 100644
index 00000000000..c4dbfb3d80f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_rounded_corner.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
index b51c890e5c1..19007f503f1 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml
@@ -1,5 +1,6 @@
+ #5F5F5F
#7D7F80
#1E8CAB
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java
new file mode 100644
index 00000000000..f25cf1b7d8b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java
@@ -0,0 +1,309 @@
+package com.mapbox.mapboxsdk.attribution;
+
+import com.mapbox.mapboxsdk.BuildConfig;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.Set;
+
+import static junit.framework.Assert.assertEquals;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(constants = BuildConfig.class)
+public class AttributionParseTest {
+
+ private static final String STREETS_ATTRIBUTION = "© Mapbox © OpenStreetMap Improve this map\n";
+ private static final String SATELLITE_ATTRIBUTION = "© Mapbox © OpenStreetMap Improve this map © DigitalGlobe\n";
+
+ @Test
+ public void testParseAttributionStringSatellite() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(SATELLITE_ATTRIBUTION)
+ .build();
+
+ Set attributionList = attributionParser.getAttributions();
+ assertEquals("Size of list should match", 4, attributionList.size());
+
+ int counter = 0;
+ for (Attribution attribution : attributionList) {
+ switch (counter) {
+ case 0:
+ assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl());
+ assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle());
+ break;
+ case 1:
+ assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
+ assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle());
+ break;
+ case 2:
+ assertEquals("URL improve map should match", "https://www.mapbox.com/map-feedback/", attribution.getUrl());
+ assertEquals("Title improve map should match", "Improve this map", attribution.getTitle());
+ break;
+ case 3:
+ assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl());
+ assertEquals("Title digital globe should match", "© DigitalGlobe", attribution.getTitle());
+ break;
+ }
+ counter++;
+ }
+ }
+
+ @Test
+ public void testParseAttributionStringStreets() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(STREETS_ATTRIBUTION)
+ .build();
+
+ Set attributionList = attributionParser.getAttributions();
+ assertEquals("Size of list should match", 3, attributionList.size());
+
+ int counter = 0;
+ for (Attribution attribution : attributionList) {
+ switch (counter) {
+ case 0:
+ assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl());
+ assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle());
+ break;
+ case 1:
+ assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
+ assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle());
+ break;
+ case 2:
+ assertEquals("URL improve map should match", "https://www.mapbox.com/map-feedback/", attribution.getUrl());
+ assertEquals("Title improve map should match", "Improve this map", attribution.getTitle());
+ break;
+ }
+ counter++;
+ }
+ }
+
+ @Test
+ public void testParseAttributionWithoutMapbox() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(STREETS_ATTRIBUTION)
+ .withMapboxAttribution(false)
+ .build();
+
+ Set attributionList = attributionParser.getAttributions();
+ assertEquals("Size of list should match", 2, attributionList.size());
+
+ int counter = 0;
+ for (Attribution attribution : attributionList) {
+ switch (counter) {
+ case 0:
+ assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
+ assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle());
+ break;
+ case 1:
+ assertEquals("URL improve map should match", "https://www.mapbox.com/map-feedback/", attribution.getUrl());
+ assertEquals("Title improve map should match", "Improve this map", attribution.getTitle());
+ break;
+ }
+ counter++;
+ }
+ }
+
+ @Test
+ public void testParseAttributionArrayString() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(new String[] {STREETS_ATTRIBUTION, "", SATELLITE_ATTRIBUTION})
+ .build();
+ Set attributionList = attributionParser.getAttributions();
+ assertEquals("Size of list should match", 4, attributionList.size());
+
+ int counter = 0;
+ for (Attribution attribution : attributionList) {
+ switch (counter) {
+ case 0:
+ assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl());
+ assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle());
+ break;
+ case 1:
+ assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
+ assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle());
+ break;
+ case 2:
+ assertEquals("URL improve map should match", "https://www.mapbox.com/map-feedback/", attribution.getUrl());
+ assertEquals("Title improve map should match", "Improve this map", attribution.getTitle());
+ break;
+ case 3:
+ assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl());
+ assertEquals("Title digital globe should match", "© DigitalGlobe", attribution.getTitle());
+ break;
+ }
+ counter++;
+ }
+ }
+
+ @Test
+ public void testHideImproveThisMapAttributionArrayString() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(SATELLITE_ATTRIBUTION)
+ .withImproveMap(false)
+ .build();
+ Set attributionList = attributionParser.getAttributions();
+ assertEquals("Size of list should match", 3, attributionList.size());
+
+ int counter = 0;
+ for (Attribution attribution : attributionList) {
+ switch (counter) {
+ case 0:
+ assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl());
+ assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle());
+ break;
+ case 1:
+ assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
+ assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle());
+ break;
+ case 2:
+ assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl());
+ assertEquals("Title digital globe should match", "© DigitalGlobe", attribution.getTitle());
+ break;
+ }
+ counter++;
+ }
+ }
+
+ @Test
+ public void testParseHideCopyrightAttributionArrayString() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(STREETS_ATTRIBUTION, "", SATELLITE_ATTRIBUTION)
+ .withCopyrightSign(false)
+ .build();
+ Set attributionList = attributionParser.getAttributions();
+ assertEquals("Size of list should match", 4, attributionList.size());
+
+ int counter = 0;
+ for (Attribution attribution : attributionList) {
+ switch (counter) {
+ case 0:
+ assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl());
+ assertEquals("Title mapbox should match", "Mapbox", attribution.getTitle());
+ break;
+ case 1:
+ assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl());
+ assertEquals("Title openstreetmap should match", "OpenStreetMap", attribution.getTitle());
+ break;
+ case 2:
+ assertEquals("URL improve map should match", "https://www.mapbox.com/map-feedback/", attribution.getUrl());
+ assertEquals("Title improve map should match", "Improve this map", attribution.getTitle());
+ break;
+ case 3:
+ assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl());
+ assertEquals("Title digital globe should match", "DigitalGlobe", attribution.getTitle());
+ break;
+ }
+ counter++;
+ }
+ }
+
+ @Test
+ public void testOutputWithoutCopyRightString() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(STREETS_ATTRIBUTION)
+ .withCopyrightSign(false)
+ .withImproveMap(false)
+ .build();
+
+ assertEquals(
+ "Attribution string should match",
+ "© Mapbox / OpenStreetMap",
+ attributionParser.createAttributionString()
+ );
+ }
+
+
+ @Test
+ public void testOutputWithCopyRightString() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(STREETS_ATTRIBUTION)
+ .withImproveMap(false)
+ .build();
+
+ assertEquals(
+ "Attribution string should match",
+ "© Mapbox / © OpenStreetMap",
+ attributionParser.createAttributionString()
+ );
+ }
+
+ @Test
+ public void testOutputWithoutCopyRightWithoutMapboxString() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(STREETS_ATTRIBUTION)
+ .withCopyrightSign(false)
+ .withImproveMap(false)
+ .withMapboxAttribution(false)
+ .build();
+
+ assertEquals(
+ "Attribution string should match",
+ "© OpenStreetMap",
+ attributionParser.createAttributionString()
+ );
+ }
+
+ @Test
+ public void testOutputWithCopyRightWithoutMapboxString() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(STREETS_ATTRIBUTION)
+ .withImproveMap(false)
+ .withMapboxAttribution(false)
+ .build();
+
+ assertEquals(
+ "Attribution string should match",
+ "© OpenStreetMap",
+ attributionParser.createAttributionString()
+ );
+ }
+
+ @Test
+ public void testOutputSatelliteString() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(STREETS_ATTRIBUTION, SATELLITE_ATTRIBUTION, "blabla", "")
+ .withImproveMap(false)
+ .withCopyrightSign(false)
+ .withMapboxAttribution(false)
+ .build();
+
+ assertEquals(
+ "Attribution string should match",
+ "© OpenStreetMap / DigitalGlobe",
+ attributionParser.createAttributionString()
+ );
+ }
+
+ @Test
+ public void testShortOpenStreetMapString() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(STREETS_ATTRIBUTION, SATELLITE_ATTRIBUTION, "blabla", "")
+ .withImproveMap(false)
+ .withCopyrightSign(false)
+ .withMapboxAttribution(false)
+ .build();
+
+ assertEquals(
+ "Attribution string should match",
+ "© OSM / DigitalGlobe",
+ attributionParser.createAttributionString(true)
+ );
+ }
+
+ @Test
+ public void testShortOpenStreetMapWithoutCopyrightString() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(STREETS_ATTRIBUTION, SATELLITE_ATTRIBUTION, "blabla", "")
+ .withImproveMap(false)
+ .withCopyrightSign(false)
+ .build();
+
+ assertEquals(
+ "Attribution string should match",
+ "© Mapbox / OSM / DigitalGlobe",
+ attributionParser.createAttributionString(true)
+ );
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java
deleted file mode 100644
index 090d274fe79..00000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.mapbox.mapboxsdk.maps;
-
-import org.junit.Test;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class CameraChangeDispatcherTest {
-
- @Test
- public void testSetCameraIdleListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraIdleListener listener = mock(MapboxMap.OnCameraIdleListener.class);
- dispatcher.setOnCameraIdleListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraIdle();
- verify(listener).onCameraIdle();
- }
-
- @Test
- public void testSetCameraMoveStartedListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveStartedListener listener = mock(MapboxMap.OnCameraMoveStartedListener.class);
- dispatcher.setOnCameraMoveStartedListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- verify(listener).onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- }
-
- @Test
- public void testSetCameraMoveCancelListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveCanceledListener listener = mock(MapboxMap.OnCameraMoveCanceledListener.class);
- dispatcher.setOnCameraMoveCanceledListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraMoveCanceled();
- verify(listener).onCameraMoveCanceled();
- }
-
- @Test
- public void testSetCameraMoveListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveListener listener = mock(MapboxMap.OnCameraMoveListener.class);
- dispatcher.setOnCameraMoveListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraMove();
- verify(listener).onCameraMove();
- }
-
- @Test
- public void testAddCameraIdleListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraIdleListener listener = mock(MapboxMap.OnCameraIdleListener.class);
- dispatcher.addOnCameraIdleListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraIdle();
- verify(listener).onCameraIdle();
- }
-
- @Test
- public void testAddCameraMoveStartedListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveStartedListener listener = mock(MapboxMap.OnCameraMoveStartedListener.class);
- dispatcher.addOnCameraMoveStartedListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- verify(listener).onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- }
-
- @Test
- public void testAddCameraMoveCancelListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveCanceledListener listener = mock(MapboxMap.OnCameraMoveCanceledListener.class);
- dispatcher.addOnCameraMoveCancelListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraMoveCanceled();
- verify(listener).onCameraMoveCanceled();
- }
-
- @Test
- public void testAddCameraMoveListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveListener listener = mock(MapboxMap.OnCameraMoveListener.class);
- dispatcher.addOnCameraMoveListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraMove();
- verify(listener).onCameraMove();
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 2533c5de35d..003fc1df6c6 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -806,4 +806,4 @@