summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobrun <tobrun@mapbox.com>2017-11-14 11:13:34 +0100
committerPablo Guardiola <guardiola31337@gmail.com>2017-11-14 11:13:34 +0100
commitf0f113bafc49a735a596357a0982e298648f4d48 (patch)
treefcbbfba79f0c44c8938f885c999bfd0df95d5a03
parenta9bd09c015f36174abdc5aee0de775500597617d (diff)
downloadqtlocation-mapboxgl-f0f113bafc49a735a596357a0982e298648f4d48.tar.gz
MapSnapshot attribution (#10362)
* [android] - add attribution * [android] - optimise attribution sources * [android] - rework datamodel to attribution class * [android] - refactor Attribution, add tests * [android] - add getter for attribution string * [android] - rework attribution to include small logo, add layout placement * [android] - finalise integration and layout logic
-rw-r--r--platform/android/MapboxGLAndroidSDK/build.gradle5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/Attribution.java59
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionLayout.java62
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionMeasure.java230
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java257
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java77
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshot.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java179
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_logo_helmet.pngbin0 -> 1650 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_logo_helmet.pngbin0 -> 950 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_logo_helmet.pngbin0 -> 2184 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_logo_helmet.pngbin0 -> 3389 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_logo_helmet.pngbin0 -> 4468 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_rounded_corner.xml10
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java309
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java10
-rw-r--r--platform/android/dependencies.gradle1
20 files changed, 1128 insertions, 77 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle
index bf0af3052b..ce886b1001 100644
--- a/platform/android/MapboxGLAndroidSDK/build.gradle
+++ b/platform/android/MapboxGLAndroidSDK/build.gradle
@@ -10,6 +10,7 @@ dependencies {
}
testCompile rootProject.ext.dep.junit
testCompile rootProject.ext.dep.mockito
+ testCompile rootProject.ext.dep.robolectric
// Mapbox Android Services (GeoJSON support)
compile(rootProject.ext.dep.mapboxJavaGeoJSON) {
@@ -126,7 +127,9 @@ android {
}
testOptions {
- unitTests.returnDefaultValues = true
+ unitTests{
+ returnDefaultValues = true
+ }
}
buildTypes {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/Attribution.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/Attribution.java
new file mode 100644
index 0000000000..0877b3ab97
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/Attribution.java
@@ -0,0 +1,59 @@
+package com.mapbox.mapboxsdk.attribution;
+
+public class Attribution {
+
+ private static final String OPENSTREETMAP = "OpenStreetMap";
+ private static final String OPENSTREETMAP_ABBR = "OSM";
+ static final String TELEMETRY = "Telemetry Settings";
+
+ static final String IMPROVE_MAP_URL = "https://www.mapbox.com/map-feedback/";
+ static final String MAPBOX_URL = "https://www.mapbox.com/about/maps/";
+ static final String TELEMETRY_URL = "https://www.mapbox.com/telemetry/";
+
+ private String title;
+ private String url;
+
+ Attribution(String title, String url) {
+ this.title = title;
+ this.url = url;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getTitleAbbreviated() {
+ if (title.equals(OPENSTREETMAP)) {
+ return OPENSTREETMAP_ABBR;
+ }
+ return title;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Attribution that = (Attribution) o;
+
+ if (title != null ? !title.equals(that.title) : that.title != null) {
+ return false;
+ }
+ return url != null ? url.equals(that.url) : that.url == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = title != null ? title.hashCode() : 0;
+ result = 31 * result + (url != null ? url.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionLayout.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionLayout.java
new file mode 100644
index 0000000000..b08a8353be
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionLayout.java
@@ -0,0 +1,62 @@
+package com.mapbox.mapboxsdk.attribution;
+
+import android.graphics.Bitmap;
+import android.graphics.PointF;
+import android.support.annotation.Nullable;
+
+public class AttributionLayout {
+
+ private Bitmap logo;
+ private PointF anchorPoint;
+ private boolean shortText;
+
+ public AttributionLayout(@Nullable Bitmap logo, @Nullable PointF anchorPoint, boolean shortText) {
+ this.logo = logo;
+ this.anchorPoint = anchorPoint;
+ this.shortText = shortText;
+ }
+
+ public Bitmap getLogo() {
+ return logo;
+ }
+
+ public PointF getAnchorPoint() {
+ return anchorPoint;
+ }
+
+ public boolean isShortText() {
+ return shortText;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ AttributionLayout that = (AttributionLayout) o;
+
+ if (logo != null ? !logo.equals(that.logo) : that.logo != null) {
+ return false;
+ }
+ return anchorPoint != null ? anchorPoint.equals(that.anchorPoint) : that.anchorPoint == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = logo != null ? logo.hashCode() : 0;
+ result = 31 * result + (anchorPoint != null ? anchorPoint.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "AttributionLayout{"
+ + "logo=" + logo
+ + ", anchorPoint=" + anchorPoint
+ + '}';
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionMeasure.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionMeasure.java
new file mode 100644
index 0000000000..667060168b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionMeasure.java
@@ -0,0 +1,230 @@
+package com.mapbox.mapboxsdk.attribution;
+
+import android.graphics.Bitmap;
+import android.graphics.PointF;
+import android.widget.TextView;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class AttributionMeasure {
+
+ private Bitmap logo;
+ private Bitmap logoSmall;
+ private Bitmap snapshot;
+ private TextView textView;
+ private TextView textViewShort;
+ private float margin;
+
+ private boolean shorterText;
+
+ AttributionMeasure(Bitmap snapshot, Bitmap logo, Bitmap logoSmall, TextView tv, TextView tvShort, float margin) {
+ this.snapshot = snapshot;
+ this.logo = logo;
+ this.logoSmall = logoSmall;
+ this.textView = tv;
+ this.textViewShort = tvShort;
+ this.margin = margin;
+ }
+
+ public AttributionLayout measure() {
+ Chain chain = new Chain(
+ new FullLogoLongTextCommand(),
+ new FullLogoShortTextCommand(),
+ new SmallLogoLongTextCommand(),
+ new SmallLogoShortTextCommand(),
+ new LongTextCommand(),
+ new ShortTextCommand(),
+ new NoTextCommand()
+ );
+
+ AttributionLayout attributionLayout = chain.start(this);
+ shorterText = attributionLayout.isShortText();
+ return attributionLayout;
+ }
+
+
+ private static class FullLogoLongTextCommand implements Command {
+ public AttributionLayout execute(AttributionMeasure measure) {
+ float width = measure.getLogoContainerWidth() + measure.getTextViewContainerWidth();
+ boolean fitBounds = width <= measure.getMaxSize();
+ if (fitBounds) {
+ PointF anchor = calculateAnchor(measure.snapshot, measure.textView, measure.margin);
+ return new AttributionLayout(measure.logo, anchor, false);
+ }
+ return null;
+ }
+ }
+
+ private static class FullLogoShortTextCommand implements Command {
+ @Override
+ public AttributionLayout execute(AttributionMeasure measure) {
+ float width = measure.getLogoContainerWidth() + measure.getTextViewShortContainerWidth();
+ boolean fitBounds = width <= measure.getMaxSizeShort();
+ if (fitBounds) {
+ PointF anchor = calculateAnchor(measure.snapshot, measure.textView, measure.margin);
+ return new AttributionLayout(measure.logo, anchor, true);
+ }
+ return null;
+ }
+ }
+
+ private static class SmallLogoLongTextCommand implements Command {
+ @Override
+ public AttributionLayout execute(AttributionMeasure measure) {
+ float width = measure.getLogoSmallContainerWidth() + measure.getTextViewContainerWidth();
+ boolean fitBounds = width <= measure.getMaxSize();
+ if (fitBounds) {
+ PointF anchor = calculateAnchor(measure.snapshot, measure.textView, measure.margin);
+ return new AttributionLayout(measure.logoSmall, anchor, false);
+ }
+ return null;
+ }
+ }
+
+ private static class SmallLogoShortTextCommand implements Command {
+ @Override
+ public AttributionLayout execute(AttributionMeasure measure) {
+ float width = measure.getLogoContainerWidth() + measure.getTextViewShortContainerWidth();
+ boolean fitBounds = width <= measure.getMaxSizeShort();
+ if (fitBounds) {
+ PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin);
+ return new AttributionLayout(measure.logoSmall, anchor, true);
+ }
+ return null;
+ }
+ }
+
+ private static class LongTextCommand implements Command {
+ @Override
+ public AttributionLayout execute(AttributionMeasure measure) {
+ float width = measure.getTextViewContainerWidth() + measure.margin;
+ boolean fitBounds = width <= measure.getMaxSize();
+ if (fitBounds) {
+ return new AttributionLayout(null, calculateAnchor(measure.snapshot, measure.textView, measure.margin), false);
+ }
+ return null;
+ }
+ }
+
+ private static class ShortTextCommand implements Command {
+ @Override
+ public AttributionLayout execute(AttributionMeasure measure) {
+ float width = measure.getTextViewShortContainerWidth() + measure.margin;
+ boolean fitBounds = width <= measure.getMaxSizeShort();
+ if (fitBounds) {
+ PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin);
+ return new AttributionLayout(null, anchor, true);
+ }
+ return null;
+ }
+ }
+
+ private static class NoTextCommand implements Command {
+ @Override
+ public AttributionLayout execute(AttributionMeasure measure) {
+ return new AttributionLayout(null, null, false);
+ }
+ }
+
+ private static PointF calculateAnchor(Bitmap snapshot, TextView textView, float margin) {
+ return new PointF(
+ snapshot.getWidth() - textView.getMeasuredWidth() - margin,
+ snapshot.getHeight() - margin - textView.getMeasuredHeight()
+ );
+ }
+
+ public TextView getTextView() {
+ return shorterText ? textViewShort : textView;
+ }
+
+ private class Chain {
+ public List<Command> commands;
+
+ Chain(Command... commands) {
+ this.commands = Arrays.asList(commands);
+ }
+
+ public AttributionLayout start(AttributionMeasure measure) {
+ AttributionLayout attributionLayout = null;
+ for (Command command : commands) {
+ attributionLayout = command.execute(measure);
+ if (attributionLayout != null) {
+ break;
+ }
+ }
+ return attributionLayout;
+ }
+ }
+
+ public interface Command {
+ AttributionLayout execute(AttributionMeasure measure);
+ }
+
+ private float getTextViewContainerWidth() {
+ return textView.getMeasuredWidth() + margin;
+ }
+
+ private float getLogoContainerWidth() {
+ return logo.getWidth() + (2 * margin);
+ }
+
+ private float getTextViewShortContainerWidth() {
+ return textViewShort.getMeasuredWidth() + margin;
+ }
+
+ private float getLogoSmallContainerWidth() {
+ return logoSmall.getWidth() + (2 * margin);
+ }
+
+ private float getMaxSize() {
+ return snapshot.getWidth() * 8 / 10;
+ }
+
+ private float getMaxSizeShort() {
+ return snapshot.getWidth();
+ }
+
+ public static class Builder {
+ private Bitmap snapshot;
+ private Bitmap logo;
+ private Bitmap logoSmall;
+ private TextView textView;
+ private TextView textViewShort;
+ private float marginPadding;
+
+ public Builder setSnapshot(Bitmap snapshot) {
+ this.snapshot = snapshot;
+ return this;
+ }
+
+ public Builder setLogo(Bitmap logo) {
+ this.logo = logo;
+ return this;
+ }
+
+ public Builder setLogoSmall(Bitmap logoSmall) {
+ this.logoSmall = logoSmall;
+ return this;
+ }
+
+ public Builder setTextView(TextView textView) {
+ this.textView = textView;
+ return this;
+ }
+
+ public Builder setTextViewShort(TextView textViewShort) {
+ this.textViewShort = textViewShort;
+ return this;
+ }
+
+ public Builder setMarginPadding(float marginPadding) {
+ this.marginPadding = marginPadding;
+ return this;
+ }
+
+ public AttributionMeasure build() {
+ return new AttributionMeasure(snapshot, logo, logoSmall, textView, textViewShort, marginPadding);
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java
new file mode 100644
index 0000000000..90bb23429f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java
@@ -0,0 +1,257 @@
+package com.mapbox.mapboxsdk.attribution;
+
+import android.text.Html;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.URLSpan;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Responsible for parsing attribution data coming from Sources and MapSnapshot.
+ * <p>
+ * Exposes multiple configuration options to manipulate data being parsed.
+ * Use the Options object to build these configurations.
+ * </p>
+ */
+public class AttributionParser {
+
+ private final Set<Attribution> attributions = new LinkedHashSet<>();
+ private final String attributionData;
+ private final boolean withImproveMap;
+ private final boolean withCopyrightSign;
+ private final boolean withTelemetryAttribution;
+ private final boolean withMapboxAttribution;
+
+ AttributionParser(String attributionData, boolean withImproveMap, boolean withCopyrightSign,
+ boolean withTelemetryAttribution, boolean withMapboxAttribution) {
+ this.attributionData = attributionData;
+ this.withImproveMap = withImproveMap;
+ this.withCopyrightSign = withCopyrightSign;
+ this.withTelemetryAttribution = withTelemetryAttribution;
+ this.withMapboxAttribution = withMapboxAttribution;
+ }
+
+ /**
+ * Get parsed attributions.
+ *
+ * @return the attributions
+ */
+ public Set<Attribution> getAttributions() {
+ return attributions;
+ }
+
+ /**
+ * Get parsed attribution string.
+ *
+ * @return the parsed attribution string
+ */
+ public String createAttributionString() {
+ return createAttributionString(false);
+ }
+
+ /**
+ * Get parsed attribution string.
+ *
+ * @param shortenedOutput if attribution string should contain shortened output
+ * @return the parsed attribution string
+ */
+ public String createAttributionString(boolean shortenedOutput) {
+ StringBuilder stringBuilder = new StringBuilder(withCopyrightSign ? "" : "© ");
+ int counter = 0;
+ for (Attribution attribution : attributions) {
+ counter++;
+ stringBuilder.append(!shortenedOutput ? attribution.getTitle() : attribution.getTitleAbbreviated());
+ if (counter != attributions.size()) {
+ stringBuilder.append(" / ");
+ }
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Main attribution for configuration
+ */
+ protected void parse() {
+ parseAttributions();
+ addAdditionalAttributions();
+ }
+
+ /**
+ * Parse attributions
+ */
+ private void parseAttributions() {
+ SpannableStringBuilder htmlBuilder = (SpannableStringBuilder) fromHtml(attributionData);
+ URLSpan[] urlSpans = htmlBuilder.getSpans(0, htmlBuilder.length(), URLSpan.class);
+ for (URLSpan urlSpan : urlSpans) {
+ parseUrlSpan(htmlBuilder, urlSpan);
+ }
+ }
+
+ /**
+ * Parse an URLSpan containing an attribution.
+ *
+ * @param htmlBuilder the html builder
+ * @param urlSpan the url span to be parsed
+ */
+ private void parseUrlSpan(SpannableStringBuilder htmlBuilder, URLSpan urlSpan) {
+ String url = urlSpan.getURL();
+ if (isUrlValid(url)) {
+ String anchor = parseAnchorValue(htmlBuilder, urlSpan);
+ attributions.add(new Attribution(anchor, url));
+ }
+ }
+
+ /**
+ * Invoked to validate if an url is valid to be included in the final attribution.
+ *
+ * @param url the url to be validated
+ * @return if the url is valid
+ */
+ private boolean isUrlValid(String url) {
+ return isValidForImproveThisMap(url) && isValidForMapbox(url);
+ }
+
+ /**
+ * Invoked to validate if an url is valid for the improve map configuration.
+ *
+ * @param url the url to be validated
+ * @return if the url is valid for improve this map
+ */
+ private boolean isValidForImproveThisMap(String url) {
+ return withImproveMap || !url.equals(Attribution.IMPROVE_MAP_URL);
+ }
+
+ /**
+ * Invoked to validate if an url is valid for the Mapbox configuration.
+ *
+ * @param url the url to be validated
+ * @return if the url is valid for Mapbox
+ */
+ private boolean isValidForMapbox(String url) {
+ return withMapboxAttribution || !url.equals(Attribution.MAPBOX_URL);
+ }
+
+ /**
+ * Parse the attribution by parsing the anchor value of html href tag.
+ *
+ * @param htmlBuilder the html builder
+ * @param urlSpan the current urlSpan
+ * @return the parsed anchor value
+ */
+ private String parseAnchorValue(SpannableStringBuilder htmlBuilder, URLSpan urlSpan) {
+ int start = htmlBuilder.getSpanStart(urlSpan);
+ int end = htmlBuilder.getSpanEnd(urlSpan);
+ int length = end - start;
+ char[] charKey = new char[length];
+ htmlBuilder.getChars(start, end, charKey, 0);
+ return stripCopyright(String.valueOf(charKey));
+ }
+
+ /**
+ * Utility to strip the copyright sign from an attribution
+ *
+ * @param anchor the attribution string to strip
+ * @return the stripped attribution string without the copyright sign
+ */
+ private String stripCopyright(String anchor) {
+ if (!withCopyrightSign && anchor.startsWith("© ")) {
+ anchor = anchor.substring(2, anchor.length());
+ }
+ return anchor;
+ }
+
+ /**
+ * Invoked to manually add attributions
+ */
+ private void addAdditionalAttributions() {
+ if (withTelemetryAttribution) {
+ attributions.add(new Attribution(Attribution.TELEMETRY, Attribution.TELEMETRY_URL));
+ }
+ }
+
+ /**
+ * Convert a string to a spanned html representation.
+ *
+ * @param html the string to convert
+ * @return the spanned html representation
+ */
+ private static Spanned fromHtml(String html) {
+ Spanned result;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
+ } else {
+ result = Html.fromHtml(html);
+ }
+ return result;
+ }
+
+ /**
+ * Builder to configure using an AttributionParser.
+ * <p>
+ * AttributionData, set with {@link #withAttributionData(String...)}, is the only required property to build
+ * the underlying AttributionParser. Other properties include trimming the copyright sign, adding telemetry
+ * attribution or hiding attribution as improve this map and Mapbox.
+ * </p>
+ */
+ public static class Options {
+ private boolean withImproveMap = true;
+ private boolean withCopyrightSign = true;
+ private boolean withTelemetryAttribution = false;
+ private boolean withMapboxAttribution = true;
+ private String[] attributionDataStringArray;
+
+ public Options withAttributionData(String... attributionData) {
+ this.attributionDataStringArray = attributionData;
+ return this;
+ }
+
+ public Options withImproveMap(boolean withImproveMap) {
+ this.withImproveMap = withImproveMap;
+ return this;
+ }
+
+ public Options withCopyrightSign(boolean withCopyrightSign) {
+ this.withCopyrightSign = withCopyrightSign;
+ return this;
+ }
+
+ public Options withTelemetryAttribution(boolean withTelemetryAttribution) {
+ this.withTelemetryAttribution = withTelemetryAttribution;
+ return this;
+ }
+
+ public Options withMapboxAttribution(boolean withMapboxAttribution) {
+ this.withMapboxAttribution = withMapboxAttribution;
+ return this;
+ }
+
+ public AttributionParser build() {
+ if (attributionDataStringArray == null) {
+ throw new IllegalStateException("Using builder without providing attribution data");
+ }
+
+ String fullAttributionString = parseAttribution(attributionDataStringArray);
+ AttributionParser attributionParser = new AttributionParser(
+ fullAttributionString,
+ withImproveMap,
+ withCopyrightSign,
+ withTelemetryAttribution,
+ withMapboxAttribution
+ );
+ attributionParser.parse();
+ return attributionParser;
+ }
+
+ private String parseAttribution(String[] attribution) {
+ StringBuilder builder = new StringBuilder();
+ for (String attr : attribution) {
+ if (!attr.isEmpty()) {
+ builder.append(attr);
+ }
+ }
+ return builder.toString();
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
index 32aa250997..9f887ab7cc 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
@@ -55,6 +55,7 @@ class HTTPRequest implements Callback {
private HTTPRequest(long nativePtr, String resourceUrl, String etag, String modified) {
mNativePtr = nativePtr;
+ Timber.e("requesting: %s",resourceUrl);
try {
HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
index 9ccff387f5..2956d864e6 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
@@ -7,22 +7,20 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
-import android.text.Html;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.text.style.URLSpan;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Toast;
-
import com.mapbox.mapboxsdk.R;
+import com.mapbox.mapboxsdk.attribution.Attribution;
+import com.mapbox.mapboxsdk.attribution.AttributionParser;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.style.sources.Source;
import com.mapbox.services.android.telemetry.MapboxTelemetry;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
+import java.util.Set;
/**
* Responsible for managing attribution interactions on the map.
@@ -39,8 +37,8 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
private final Context context;
private final MapboxMap mapboxMap;
- private String[] attributionKeys;
- private HashMap<String, String> attributionMap;
+ private String[] attributionTitles;
+ private Set<Attribution> attributionSet;
AttributionDialogManager(@NonNull Context context, @NonNull MapboxMap mapboxMap) {
this.context = context;
@@ -50,18 +48,26 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
// Called when someone presses the attribution icon on the map
@Override
public void onClick(View view) {
- attributionMap = new AttributionBuilder(context, mapboxMap).build();
+ attributionSet = new AttributionBuilder(mapboxMap).build();
showAttributionDialog();
}
private void showAttributionDialog() {
- attributionKeys = attributionMap.keySet().toArray(new String[attributionMap.size()]);
+ attributionTitles = getAttributionTitles();
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.mapbox_attributionsDialogTitle);
- builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionKeys), this);
+ builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionTitles), this);
builder.show();
}
+ private String[] getAttributionTitles() {
+ List<String> titles = new ArrayList<>();
+ for (Attribution attribution : attributionSet) {
+ titles.add(attribution.getTitle());
+ }
+ return titles.toArray(new String[titles.size()]);
+ }
+
// Called when someone selects an attribution or telemetry settings from the dialog
@Override
public void onClick(DialogInterface dialog, int which) {
@@ -73,7 +79,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
}
private boolean isLatestEntry(int attributionKeyIndex) {
- return attributionKeyIndex == attributionKeys.length - 1;
+ return attributionKeyIndex == attributionTitles.length - 1;
}
private void showTelemetryDialog() {
@@ -105,7 +111,8 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
}
private void showMapFeedbackWebPage(int which) {
- String url = attributionMap.get(attributionKeys[which]);
+ Attribution[] attributions = attributionSet.toArray(new Attribution[attributionSet.size()]);
+ String url = attributions[which].getUrl();
if (url.contains(MAP_FEEDBACK_URL)) {
url = buildMapFeedbackMapUrl(mapboxMap.getCameraPosition());
}
@@ -132,46 +139,24 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
private static class AttributionBuilder {
- private final HashMap<String, String> map = new LinkedHashMap<>();
- private final Context context;
private final MapboxMap mapboxMap;
- AttributionBuilder(Context context, MapboxMap mapboxMap) {
- this.context = context.getApplicationContext();
+ AttributionBuilder(MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
}
- private HashMap<String, String> build() {
+ private Set<Attribution> build() {
+ List<String> attributions = new ArrayList<>();
for (Source source : mapboxMap.getSources()) {
- parseAttribution(source.getAttribution());
- }
- addTelemetryEntryToAttributionMap();
- return map;
- }
-
- private void parseAttribution(String attributionSource) {
- if (!TextUtils.isEmpty(attributionSource)) {
- SpannableStringBuilder htmlBuilder = (SpannableStringBuilder) Html.fromHtml(attributionSource);
- URLSpan[] urlSpans = htmlBuilder.getSpans(0, htmlBuilder.length(), URLSpan.class);
- for (URLSpan urlSpan : urlSpans) {
- map.put(resolveAnchorValue(htmlBuilder, urlSpan), urlSpan.getURL());
- }
+ attributions.add(source.getAttribution());
}
- }
-
- private String resolveAnchorValue(SpannableStringBuilder htmlBuilder, URLSpan urlSpan) {
- int start = htmlBuilder.getSpanStart(urlSpan);
- int end = htmlBuilder.getSpanEnd(urlSpan);
- int length = end - start;
- char[] charKey = new char[length];
- htmlBuilder.getChars(start, end, charKey, 0);
- return String.valueOf(charKey);
- }
- private void addTelemetryEntryToAttributionMap() {
- String telemetryKey = context.getString(R.string.mapbox_telemetrySettings);
- String telemetryLink = context.getString(R.string.mapbox_telemetryLink);
- map.put(telemetryKey, telemetryLink);
+ return new AttributionParser.Options()
+ .withCopyrightSign(true)
+ .withImproveMap(true)
+ .withTelemetryAttribution(true)
+ .withAttributionData(attributions.toArray(new String[attributions.size()]))
+ .build().getAttributions();
}
}
}
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 eb4f94c428..38c1491461 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 @@ public class MapSnapshot {
}
/**
- * @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 5deedc3e63..1c59bb468e 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.Bitmap;
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 class MapSnapshotter {
* @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 @@ public class MapSnapshotter {
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 @@ public class MapSnapshotter {
*
* @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 @@ public class 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/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 0000000000..2629afe6a3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_logo_helmet.png
Binary files 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 0000000000..34bab3bf6d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_logo_helmet.png
Binary files 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 0000000000..942d78ec58
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_logo_helmet.png
Binary files 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 0000000000..947d5a2a30
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_logo_helmet.png
Binary files 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 0000000000..bec38cedc7
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_logo_helmet.png
Binary files 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 0000000000..c4dbfb3d80
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_rounded_corner.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#A6FFFFFF"/>
+ <padding
+ android:left="7.5dp"
+ android:right="7.5dp"
+ android:top="2dp"
+ android:bottom="3dp"/>
+ <corners android:radius="7.5dp"/>
+</shape> \ 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 b51c890e5c..19007f503f 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 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <color name="mapbox_gray_dark">#5F5F5F</color>
<color name="mapbox_gray">#7D7F80</color>
<color name="mapbox_blue">#1E8CAB</color>
</resources>
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 0000000000..f25cf1b7d8
--- /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 = "<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">&copy; Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">&copy; OpenStreetMap</a> <a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a>\n";
+ private static final String SATELLITE_ATTRIBUTION = "<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">&copy; Mapbox</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">&copy; OpenStreetMap</a> <a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a> <a href=\"https://www.digitalglobe.com/\" target=\"_blank\">&copy; DigitalGlobe</a>\n";
+
+ @Test
+ public void testParseAttributionStringSatellite() throws Exception {
+ AttributionParser attributionParser = new AttributionParser.Options()
+ .withAttributionData(SATELLITE_ATTRIBUTION)
+ .build();
+
+ Set<Attribution> 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<Attribution> 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<Attribution> 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<Attribution> 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<Attribution> 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<Attribution> 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/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
index 245786e1d0..c4fe93d200 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java
@@ -69,7 +69,7 @@ public class MapSnapshotterActivity extends AppCompatActivity {
.withPixelRatio(1)
// Optionally the style
- .withStyle((column + row) % 2 == 0 ? Style.TRAFFIC_DAY : Style.DARK);
+ .withStyle((column + row) % 2 == 0 ? Style.MAPBOX_STREETS : Style.DARK);
// Optionally the visible region
if (row % 2 == 0) {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java
index 781e7b6334..b690f18b6a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java
@@ -9,14 +9,12 @@ import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
-
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.snapshotter.MapSnapshot;
import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
import com.mapbox.mapboxsdk.testapp.R;
-
import timber.log.Timber;
/**
@@ -46,7 +44,7 @@ public class MapSnapshotterMarkerActivity extends AppCompatActivity implements M
getApplicationContext(),
new MapSnapshotter
.Options(Math.min(container.getMeasuredWidth(), 1024), Math.min(container.getMeasuredHeight(), 1024))
- .withStyle(Style.TRAFFIC_DAY)
+ .withStyle(Style.OUTDOORS)
.withCameraPosition(new CameraPosition.Builder().target(new LatLng(52.090737, 5.121420)).zoom(15).build())
);
mapSnapshotter.start(MapSnapshotterMarkerActivity.this);
@@ -55,6 +53,12 @@ public class MapSnapshotterMarkerActivity extends AppCompatActivity implements M
}
@Override
+ protected void onStop() {
+ super.onStop();
+ mapSnapshotter.cancel();
+ }
+
+ @Override
public void onSnapshotReady(MapSnapshot snapshot) {
Timber.i("Snapshot ready");
ImageView imageView = (ImageView) findViewById(R.id.snapshot_image);
diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle
index eadf7aea56..0094b79281 100644
--- a/platform/android/dependencies.gradle
+++ b/platform/android/dependencies.gradle
@@ -25,6 +25,7 @@ ext {
// unit test
junit : 'junit:junit:4.12',
mockito : 'org.mockito:mockito-core:2.10.0',
+ robolectric : 'org.robolectric:robolectric:3.5.1',
// instrumentation test
testRunner : "com.android.support.test:runner:${testRunnerVersion}",