diff options
author | Fabian Guerra <fabian.guerra@mapbox.com> | 2018-01-04 12:55:19 -0600 |
---|---|---|
committer | Fabian Guerra <fabian.guerra@mapbox.com> | 2018-01-04 12:55:19 -0600 |
commit | 187b6dc31cf281b5157de1acd92f3e8ef9d0acf9 (patch) | |
tree | 1d9eaac5d1e8393d8fd665aa678581a6086a9eb6 /platform/android/MapboxGLAndroidSDK/src/main | |
parent | c62b0af24fc76b4bb2eb34100611dd3ee9ee5536 (diff) | |
parent | 760ef23ac3faf4437cadb52983383c9501336964 (diff) | |
download | qtlocation-mapboxgl-187b6dc31cf281b5157de1acd92f3e8ef9d0acf9.tar.gz |
Merge branch 'release-agua' into masterupstream/fabian-merge-release-agua
# Conflicts:
# circle.yml
# cmake/core-files.cmake
# platform/android/CHANGELOG.md
# platform/android/MapboxGLAndroidSDK/build.gradle
# platform/android/MapboxGLAndroidSDK/gradle.properties
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java
# platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java
# platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
# platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
# platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LocalGlyphActivity.java
# platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java
# platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_local_glyph.xml
# platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
# platform/darwin/src/MGLMapSnapshotter.mm
# platform/darwin/src/MGLRendererConfiguration.h
# platform/ios/CHANGELOG.md
# platform/ios/DEVELOPING.md
# platform/ios/INSTALL.md
# platform/ios/README.md
# platform/ios/resources/bg.lproj/Localizable.strings
# platform/ios/resources/es.lproj/Localizable.strings
# platform/ios/resources/vi.lproj/Localizable.strings
# platform/ios/src/MGLMapView.mm
# platform/macos/macos.xcodeproj/project.pbxproj
# src/mbgl/text/glyph_manager.cpp
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main')
31 files changed, 1081 insertions, 177 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml index b61035a008..f59585bfe5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml @@ -1,14 +1,17 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.mapbox.mapboxsdk"> + xmlns:tools="http://schemas.android.com/tools" + package="com.mapbox.mapboxsdk"> <uses-feature android:glEsVersion="0x00020000" android:required="true" /> <uses-feature android:name="android.hardware.wifi" android:required="false" /> <!-- Implied by ACCESS_WIFI_STATE. --> <uses-feature android:name="android.hardware.location.gps" android:required="false"/> - <uses-permission android:name="android.permission.INTERNET" /> - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> + <uses-sdk tools:overrideLibrary="com.mapzen.lost"/> + <application> <!-- Include the telemetry service to simplify set up (https://www.mapbox.com/telemetry) --> <service android:name="com.mapbox.services.android.telemetry.service.TelemetryService"/> 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..c2408ca718 --- /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.textViewShort, 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..8463814794 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 @@ -22,6 +22,7 @@ import javax.net.ssl.SSLException; import okhttp3.Call; import okhttp3.Callback; +import okhttp3.Dispatcher; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -31,7 +32,8 @@ import timber.log.Timber; class HTTPRequest implements Callback { - private static OkHttpClient mClient = new OkHttpClient(); + private static OkHttpClient mClient = new OkHttpClient.Builder().dispatcher(getDispatcher()).build(); + private static boolean logEnabled = true; private String USER_AGENT_STRING = null; private static final int CONNECTION_ERROR = 0; @@ -47,6 +49,14 @@ class HTTPRequest implements Callback { private Call mCall; private Request mRequest; + private static Dispatcher getDispatcher() { + Dispatcher dispatcher = new Dispatcher(); + // Matches core limit set on + // https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/src/http_file_source.cpp#L192 + dispatcher.setMaxRequestsPerHost(20); + return dispatcher; + } + private native void nativeOnFailure(int type, String message); private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires, @@ -85,14 +95,7 @@ class HTTPRequest implements Callback { } mRequest = builder.build(); mCall = mClient.newCall(mRequest); - - // TODO remove code block for workaround in #10303 - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) { - mCall.enqueue(this); - } else { - // Calling execute instead of enqueue is a workaround for #10303 - onResponse(mCall, mCall.execute()); - } + mCall.enqueue(this); } catch (Exception exception) { onFailure(exception); } @@ -116,12 +119,15 @@ class HTTPRequest implements Callback { @Override public void onResponse(Call call, Response response) throws IOException { - if (response.isSuccessful()) { - Timber.v("[HTTP] Request was successful (code = %s).", response.code()); - } else { - // We don't want to call this unsuccessful because a 304 isn't really an error - String message = !TextUtils.isEmpty(response.message()) ? response.message() : "No additional information"; - Timber.d("[HTTP] Request with response code = %s: %s", response.code(), message); + + if (logEnabled) { + if (response.isSuccessful()) { + Timber.v("[HTTP] Request was successful (code = %s).", response.code()); + } else { + // We don't want to call this unsuccessful because a 304 isn't really an error + String message = !TextUtils.isEmpty(response.message()) ? response.message() : "No additional information"; + Timber.d("[HTTP] Request with response code = %s: %s", response.code(), message); + } } byte[] body; @@ -165,13 +171,15 @@ class HTTPRequest implements Callback { String errorMessage = e.getMessage() != null ? e.getMessage() : "Error processing the request"; - if (type == TEMPORARY_ERROR) { - Timber.d("Request failed due to a temporary error: %s", errorMessage); - } else if (type == CONNECTION_ERROR) { - Timber.i("Request failed due to a connection error: %s", errorMessage); - } else { - // PERMANENT_ERROR - Timber.w("Request failed due to a permanent error: %s", errorMessage); + if (logEnabled) { + if (type == TEMPORARY_ERROR) { + Timber.d("Request failed due to a temporary error: %s", errorMessage); + } else if (type == CONNECTION_ERROR) { + Timber.i("Request failed due to a connection error: %s", errorMessage); + } else { + // PERMANENT_ERROR + Timber.w("Request failed due to a permanent error: %s", errorMessage); + } } mLock.lock(); @@ -205,4 +213,8 @@ class HTTPRequest implements Callback { return ""; } } + + static void enableLog(boolean enabled) { + logEnabled = enabled; + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java new file mode 100644 index 0000000000..af39faeded --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java @@ -0,0 +1,19 @@ +package com.mapbox.mapboxsdk.http; + +/** + * Utility class for setting HttpRequest configurations + */ +public class HttpRequestUtil { + + /** + * Set the log state of HttpRequest. + * <p> + * This configuration will outlast the lifecycle of the Map. + * </p> + * + * @param enabled True will enable logging, false will disable + */ + public static void setLogEnabled(boolean enabled) { + HTTPRequest.enableLog(enabled); + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java index c6bc13f538..1313587158 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java @@ -32,7 +32,7 @@ import com.mapzen.android.lost.api.LostApiClient; * @deprecated Use a {@link Mapbox#getLocationEngine()} instead. */ @Deprecated -public class LocationSource extends LocationEngine implements LocationListener { +public class LocationSource extends LocationEngine implements LostApiClient.ConnectionCallbacks, LocationListener { private Context context; private LostApiClient lostApiClient; @@ -45,7 +45,9 @@ public class LocationSource extends LocationEngine implements LocationListener { public LocationSource(Context context) { super(); this.context = context.getApplicationContext(); - lostApiClient = new LostApiClient.Builder(this.context).build(); + lostApiClient = new LostApiClient.Builder(this.context) + .addConnectionCallbacks(this) + .build(); } /** @@ -61,12 +63,7 @@ public class LocationSource extends LocationEngine implements LocationListener { */ @Override public void activate() { - if (!lostApiClient.isConnected()) { - lostApiClient.connect(); - } - for (LocationEngineListener listener : locationListeners) { - listener.onConnected(); - } + connect(); } /** @@ -76,7 +73,7 @@ public class LocationSource extends LocationEngine implements LocationListener { */ @Override public void deactivate() { - if (lostApiClient.isConnected()) { + if (lostApiClient != null && lostApiClient.isConnected()) { lostApiClient.disconnect(); } } @@ -93,6 +90,24 @@ public class LocationSource extends LocationEngine implements LocationListener { } /** + * Invoked when the location provider has connected. + */ + @Override + public void onConnected() { + for (LocationEngineListener listener : locationListeners) { + listener.onConnected(); + } + } + + /** + * Invoked when the location provider connection has been suspended. + */ + @Override + public void onConnectionSuspended() { + // Empty + } + + /** * Returns the Last known location is the location provider is connected and location permissions are granted. * * @return the last known location @@ -102,7 +117,7 @@ public class LocationSource extends LocationEngine implements LocationListener { public Location getLastLocation() { if (lostApiClient.isConnected()) { //noinspection MissingPermission - return LocationServices.FusedLocationApi.getLastLocation(); + return LocationServices.FusedLocationApi.getLastLocation(lostApiClient); } return null; } @@ -136,7 +151,7 @@ public class LocationSource extends LocationEngine implements LocationListener { if (lostApiClient.isConnected()) { //noinspection MissingPermission - LocationServices.FusedLocationApi.requestLocationUpdates(request, this); + LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, this); } } @@ -146,7 +161,7 @@ public class LocationSource extends LocationEngine implements LocationListener { @Override public void removeLocationUpdates() { if (lostApiClient.isConnected()) { - LocationServices.FusedLocationApi.removeLocationUpdates(this); + LocationServices.FusedLocationApi.removeLocationUpdates(lostApiClient, this); } } @@ -171,4 +186,14 @@ public class LocationSource extends LocationEngine implements LocationListener { listener.onLocationChanged(location); } } + + private void connect() { + if (lostApiClient != null) { + if (lostApiClient.isConnected()) { + onConnected(); + } else { + lostApiClient.connect(); + } + } + } }
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java index 9f256c341b..64b33ad598 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java @@ -302,12 +302,14 @@ class AnnotationManager { } for (Marker marker : selectedMarkers) { - if (marker.isInfoWindowShown()) { - marker.hideInfoWindow(); - } + if (marker != null) { + if (marker.isInfoWindowShown()) { + marker.hideInfoWindow(); + } - if (marker instanceof MarkerView) { - markerViewManager.deselect((MarkerView) marker, false); + if (marker instanceof MarkerView) { + markerViewManager.deselect((MarkerView) marker, false); + } } } 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/maps/CameraChangeDispatcher.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java index f046744c31..69a43d4d3e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk.maps; +import android.os.Handler; import android.support.annotation.NonNull; import java.util.concurrent.CopyOnWriteArrayList; @@ -9,10 +10,16 @@ import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveCanceledListener; import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener; import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener; +/** + * Class responsible for dispatching camera change events to registered listeners. + */ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraMoveListener, MapboxMap.OnCameraMoveCanceledListener, OnCameraIdleListener { + private final Handler handler = new Handler(); + private boolean idle = true; + private int moveStartedReason; private final CopyOnWriteArrayList<OnCameraMoveStartedListener> onCameraMoveStarted = new CopyOnWriteArrayList<>(); private final CopyOnWriteArrayList<OnCameraMoveCanceledListener> 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 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M } @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 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M @Override public void onCameraMove() { - // deprecated API + handler.post(onCameraMoveRunnable); if (onCameraMoveListener != null && !idle) { onCameraMoveListener.onCameraMove(); } @@ -81,7 +153,7 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M @Override public void onCameraMoveCanceled() { - // deprecated API + handler.post(onCameraMoveCancelRunnable); if (onCameraMoveCanceledListener != null && !idle) { onCameraMoveCanceledListener.onCameraMoveCanceled(); } @@ -98,7 +170,7 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M 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 01c6da4971..0c11a4220e 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 android.view.ViewGroup; import com.mapbox.mapboxsdk.utils.MapFragmentUtils; +import java.util.ArrayList; +import java.util.List; + /** * Fragment wrapper around a map view. * <p> @@ -25,10 +28,11 @@ import com.mapbox.mapboxsdk.utils.MapFragmentUtils; * * @see #getMapAsync(OnMapReadyCallback) */ -public final class MapFragment extends Fragment { +public final class MapFragment extends Fragment implements OnMapReadyCallback { + private final List<OnMapReadyCallback> mapReadyCallbackList = new ArrayList<>(); + private MapboxMap mapboxMap; private MapView map; - private OnMapReadyCallback onMapReadyCallback; /** * Creates a default MapFragment instance @@ -63,7 +67,9 @@ public final class MapFragment extends Fragment { 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 final class MapFragment extends Fragment { 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 final class MapFragment extends Fragment { public void onDestroyView() { super.onDestroyView(); map.onDestroy(); + mapReadyCallbackList.clear(); } /** @@ -152,10 +166,10 @@ public final class MapFragment extends Fragment { * @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 0fea5ce0ff..6424de342e 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 @@ final class MapGestureDetector { 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 bf98e6bbc1..9525d48820 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 class MapboxMap { * @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 class MapboxMap { 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 class MapboxMap { 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 2719d7f016..34be958329 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 class MapboxMapOptions implements Parcelable { } /** - * 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 6c90cd95ec..c072ec8237 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 android.view.ViewGroup; import com.mapbox.mapboxsdk.utils.MapFragmentUtils; +import java.util.ArrayList; +import java.util.List; + /** * Support Fragment wrapper around a map view. * <p> @@ -25,10 +28,11 @@ import com.mapbox.mapboxsdk.utils.MapFragmentUtils; * * @see #getMapAsync(OnMapReadyCallback) */ -public class SupportMapFragment extends Fragment { +public class SupportMapFragment extends Fragment implements OnMapReadyCallback { + private final List<OnMapReadyCallback> mapReadyCallbackList = new ArrayList<>(); + private MapboxMap mapboxMap; private MapView map; - private OnMapReadyCallback onMapReadyCallback; /** * Creates a default MapFragment instance @@ -63,7 +67,9 @@ public class SupportMapFragment extends Fragment { 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 class SupportMapFragment extends Fragment { 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 class SupportMapFragment extends Fragment { public void onDestroyView() { super.onDestroyView(); map.onDestroy(); + mapReadyCallbackList.clear(); } /** @@ -152,10 +166,10 @@ public class SupportMapFragment extends Fragment { * @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 16c45ebea2..0d3f0d5e5b 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 @@ final class Transform implements MapView.OnMapChangedListener { 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 @@ final class Transform implements MapView.OnMapChangedListener { // 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 aa7934ec1e..3fe3c7b40d 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.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.MyBearingTracking; import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.location.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 @@ public class MyLocationView extends View { } @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 6a2bf6b07b..f2faabd63b 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 class OfflineManager { } ConnectivityReceiver.instance(context).activate(); + FileSource.getInstance(context).activate(); createOfflineRegion(fileSource, definition, metadata, new CreateOfflineRegionCallback() { @Override @@ -212,6 +213,7 @@ public class OfflineManager { @Override public void run() { ConnectivityReceiver.instance(context).deactivate(); + FileSource.getInstance(context).deactivate(); callback.onCreate(offlineRegion); } }); @@ -223,6 +225,7 @@ public class OfflineManager { @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 1b9a156352..bc9438ee93 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 class OfflineRegion { * When the operation is complete or encounters an error, the given callback will be * executed on the main thread. * </p> - * <p> - * After you call this method, you may not call any additional methods on this object. - * </p> * * @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 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/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java index 920a1270ac..181d28191a 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 c7d7a13a3d..f17d4574d5 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 Binary files differnew file mode 100644 index 0000000000..2629afe6a3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_logo_helmet.png 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 Binary files differnew file mode 100644 index 0000000000..34bab3bf6d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_logo_helmet.png 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 Binary files differnew file mode 100644 index 0000000000..942d78ec58 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_logo_helmet.png 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 Binary files differnew file mode 100644 index 0000000000..947d5a2a30 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_logo_helmet.png 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 Binary files differnew file mode 100644 index 0000000000..bec38cedc7 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_logo_helmet.png 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> |