summaryrefslogtreecommitdiff
path: root/platform/android
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android')
-rw-r--r--platform/android/CHANGELOG.md44
-rw-r--r--platform/android/MapboxGLAndroidSDK/build.gradle5
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle-publish.gradle74
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml9
-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.java56
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUtil.java19
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationSource.java49
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java12
-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/maps/CameraChangeDispatcher.java90
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java41
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java23
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java3
-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/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java2
-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/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java87
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java3
-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/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml2
-rw-r--r--platform/android/dependencies.gradle9
42 files changed, 1484 insertions, 321 deletions
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md
index 213676c8be..79223e21ea 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -2,7 +2,49 @@
Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started.
-## 5.2.0 - TBA
+## 5.3.0 - December 20, 2017
+ - Add support for TinySDF [#10706](https://github.com/mapbox/mapbox-gl-native/pull/10706)
+ - Save restore MyLocationViewSettings [#10746](https://github.com/mapbox/mapbox-gl-native/pull/10746)
+ - Post animation callback invocation [#10664](https://github.com/mapbox/mapbox-gl-native/pull/10664)
+ - Allow configuring Http logging [#10681](https://github.com/mapbox/mapbox-gl-native/pull/10681)
+ - Fix reverse scale gesture [#10688](https://github.com/mapbox/mapbox-gl-native/pull/10688)
+ - Update offline region metadata documentation [#10693](https://github.com/mapbox/mapbox-gl-native/pull/10693)
+ - Post camera listener invocation [#10690](https://github.com/mapbox/mapbox-gl-native/pull/10690)
+ - Activate filesource for offline region creation [#10718](https://github.com/mapbox/mapbox-gl-native/pull/10718)
+ - Update Spanish/Vietnamese translations [#10740](https://github.com/mapbox/mapbox-gl-native/pull/10740)
+ - Update instrumented make target [#10724](https://github.com/mapbox/mapbox-gl-native/pull/10724)
+ - Remove black flash on start for fragments [#10717](https://github.com/mapbox/mapbox-gl-native/pull/10717)
+ - CompassView decode crash [#10717](https://github.com/mapbox/mapbox-gl-native/pull/10717)
+ - Android SDK renaming [#10609](https://github.com/mapbox/mapbox-gl-native/pull/10609)
+ - Map touch listener based lists [#10749](https://github.com/mapbox/mapbox-gl-native/pull/10749)
+
+## 5.2.1 - December 6, 2017
+ - Close race condition in RunLoop [#10537](https://github.com/mapbox/mapbox-gl-native/pull/10537)
+ - OkHttp 3.9.1 [#10515](https://github.com/mapbox/mapbox-gl-native/pull/10515)
+ - Attribution anchor point fix [#10558](https://github.com/mapbox/mapbox-gl-native/pull/10558)
+ - Pre API 19 VerifyError [#10579](https://github.com/mapbox/mapbox-gl-native/pull/10579)
+ - Set larger Http request limit [#10567](https://github.com/mapbox/mapbox-gl-native/pull/10567)
+ - Remove jar generation from maven publish [#10625](https://github.com/mapbox/mapbox-gl-native/pull/10625)
+ - Enable Map Rendering when paused for multiple window support [#10509](https://github.com/mapbox/mapbox-gl-native/pull/10509)
+ - Activate FileSource when listing offline regions [#10531](https://github.com/mapbox/mapbox-gl-native/pull/10531)
+ - Harden MarkerView integration by checking for null bitmap [#10532](https://github.com/mapbox/mapbox-gl-native/pull/10532)
+ - Use concurrent lists for camera change listeners [#10542](https://github.com/mapbox/mapbox-gl-native/pull/10542)
+ - Handle destroy activity as part of theme switching [#10589](https://github.com/mapbox/mapbox-gl-native/pull/10589)
+ - add FileSource activation/deactivation to MapSnapshotter [#10556](https://github.com/mapbox/mapbox-gl-native/pull/10556)
+
+## 5.2.0 - November 17, 2017
+
+- Monkey crashes [#10472](https://github.com/mapbox/mapbox-gl-native/pull/10472)
+
+## 5.2.0-beta.5 - November 14, 2017
+
+- MapSnapshot attribution [#10362](https://github.com/mapbox/mapbox-gl-native/pull/10362)
+- Downgrade min sdk to 14 [#10355](https://github.com/mapbox/mapbox-gl-native/pull/10355)
+- Harden deselection mechanism for markers [#10403](https://github.com/mapbox/mapbox-gl-native/pull/10403)
+- Cherry picks to agua [#10442](https://github.com/mapbox/mapbox-gl-native/pull/10442)
+- Rework test activity gen script setup [#10365](https://github.com/mapbox/mapbox-gl-native/pull/10365)
+- Fix broken android unit tests, update test make target to SDK [#10387](https://github.com/mapbox/mapbox-gl-native/pull/10387)
+- Check for null value when calling mapboxMap.clear [#10388](https://github.com/mapbox/mapbox-gl-native/pull/10388)
## 5.2.0-beta.4 - November 3, 2017
diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle
index 32eeab138f..70c98f534a 100644
--- a/platform/android/MapboxGLAndroidSDK/build.gradle
+++ b/platform/android/MapboxGLAndroidSDK/build.gradle
@@ -14,6 +14,7 @@ dependencies {
testImplementation rootProject.ext.dep.lost
testImplementation rootProject.ext.dep.junit
testImplementation rootProject.ext.dep.mockito
+ testImplementation rootProject.ext.dep.robolectric
}
android {
@@ -119,7 +120,9 @@ android {
}
testOptions {
- unitTests.returnDefaultValues = true
+ unitTests{
+ returnDefaultValues = true
+ }
}
buildTypes {
diff --git a/platform/android/MapboxGLAndroidSDK/gradle-publish.gradle b/platform/android/MapboxGLAndroidSDK/gradle-publish.gradle
index c5037974c0..9805763a99 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle-publish.gradle
+++ b/platform/android/MapboxGLAndroidSDK/gradle-publish.gradle
@@ -14,22 +14,17 @@ repositories {
mavenCentral()
}
-// From https://raw.github.com/mcxiaoke/gradle-mvn-push/master/jar.gradle
-android.libraryVariants.all { variant ->
- def jarTask = project.tasks.create(name: "jar${variant.name.capitalize()}", type: Jar) {
- from variant.javaCompile.destinationDir
- exclude "**/R.class"
- exclude "**/BuildConfig.class"
- }
- jarTask.dependsOn variant.javaCompile
- artifacts.add('archives', jarTask);
-}
-
-// From https://raw.github.com/mcxiaoke/gradle-mvn-push/master/gradle-mvn-push.gradle
def isReleaseBuild() {
return VERSION_NAME.contains("SNAPSHOT") == false
}
+def isLocalBuild() {
+ if (System.getenv('IS_LOCAL_DEVELOPMENT') != null) {
+ return System.getenv('IS_LOCAL_DEVELOPMENT').toBoolean()
+ }
+ return true
+}
+
def getReleaseRepositoryUrl() {
return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL :
"https://oss.sonatype.org/service/local/staging/deploy/maven2/"
@@ -40,6 +35,10 @@ def getSnapshotRepositoryUrl() {
"https://oss.sonatype.org/content/repositories/snapshots/"
}
+def obtainMavenLocalUrl() {
+ return getRepositories().mavenLocal().getUrl()
+}
+
def getRepositoryUsername() {
return hasProperty('USERNAME') ? USERNAME :
(hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "")
@@ -50,22 +49,6 @@ def getRepositoryPassword() {
(hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "")
}
-task apklib(type: Zip) {
- appendix = extension = 'apklib'
-
- from 'AndroidManifest.xml'
- into('res') {
- from 'res'
- }
- into('src') {
- from 'src'
- }
-}
-
-artifacts {
- archives apklib
-}
-
afterEvaluate { project ->
uploadArchives {
repositories {
@@ -76,24 +59,16 @@ afterEvaluate { project ->
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
- repository(url: getReleaseRepositoryUrl()) {
- authentication(userName: getRepositoryUsername(),
- password: getRepositoryPassword())
- }
- snapshotRepository(url: getSnapshotRepositoryUrl()) {
- authentication(userName: getRepositoryUsername(),
- password: getRepositoryPassword())
- }
-
-/*
- // Leaving out as artifact was incorrectly named when found
- addFilter('aar') { artifact, file ->
- artifact.name == archivesBaseName
- }
- addFilter('apklib') { artifact, file ->
- artifact.name == archivesBaseName + '-apklib'
+ if (isLocalBuild()) {
+ repository(url: obtainMavenLocalUrl())
+ } else {
+ repository(url: getReleaseRepositoryUrl()) {
+ authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
+ }
+ snapshotRepository(url: getSnapshotRepositoryUrl()) {
+ authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
+ }
}
-*/
pom.project {
name POM_NAME
@@ -151,3 +126,12 @@ afterEvaluate { project ->
archives androidJavadocsJar
}
}
+
+// See: https://github.com/chrisbanes/gradle-mvn-push/issues/43#issuecomment-84140513
+afterEvaluate { project ->
+ android.libraryVariants.all { variant ->
+ tasks.androidJavadocs.doFirst {
+ classpath += files(variant.javaCompile.classpath.files)
+ }
+ }
+}
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
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/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java
deleted file mode 100644
index 090d274fe7..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcherTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.mapbox.mapboxsdk.maps;
-
-import org.junit.Test;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class CameraChangeDispatcherTest {
-
- @Test
- public void testSetCameraIdleListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraIdleListener listener = mock(MapboxMap.OnCameraIdleListener.class);
- dispatcher.setOnCameraIdleListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraIdle();
- verify(listener).onCameraIdle();
- }
-
- @Test
- public void testSetCameraMoveStartedListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveStartedListener listener = mock(MapboxMap.OnCameraMoveStartedListener.class);
- dispatcher.setOnCameraMoveStartedListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- verify(listener).onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- }
-
- @Test
- public void testSetCameraMoveCancelListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveCanceledListener listener = mock(MapboxMap.OnCameraMoveCanceledListener.class);
- dispatcher.setOnCameraMoveCanceledListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraMoveCanceled();
- verify(listener).onCameraMoveCanceled();
- }
-
- @Test
- public void testSetCameraMoveListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveListener listener = mock(MapboxMap.OnCameraMoveListener.class);
- dispatcher.setOnCameraMoveListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraMove();
- verify(listener).onCameraMove();
- }
-
- @Test
- public void testAddCameraIdleListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraIdleListener listener = mock(MapboxMap.OnCameraIdleListener.class);
- dispatcher.addOnCameraIdleListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraIdle();
- verify(listener).onCameraIdle();
- }
-
- @Test
- public void testAddCameraMoveStartedListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveStartedListener listener = mock(MapboxMap.OnCameraMoveStartedListener.class);
- dispatcher.addOnCameraMoveStartedListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- verify(listener).onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- }
-
- @Test
- public void testAddCameraMoveCancelListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveCanceledListener listener = mock(MapboxMap.OnCameraMoveCanceledListener.class);
- dispatcher.addOnCameraMoveCancelListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraMoveCanceled();
- verify(listener).onCameraMoveCanceled();
- }
-
- @Test
- public void testAddCameraMoveListener() {
- CameraChangeDispatcher dispatcher = new CameraChangeDispatcher();
- MapboxMap.OnCameraMoveListener listener = mock(MapboxMap.OnCameraMoveListener.class);
- dispatcher.addOnCameraMoveListener(listener);
- dispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE);
- dispatcher.onCameraMove();
- verify(listener).onCameraMove();
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 2533c5de35..003fc1df6c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -806,4 +806,4 @@
<!-- android:value="true" /> -->
</application>
-</manifest> \ No newline at end of file
+</manifest>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
index 22da952560..43e89728cb 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java
@@ -16,6 +16,7 @@ import android.widget.ListView;
import android.widget.TextView;
import com.mapbox.mapboxsdk.constants.Style;
+import com.mapbox.mapboxsdk.http.HttpRequestUtil;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
@@ -54,6 +55,7 @@ public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCa
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ HttpRequestUtil.setLogEnabled(false);
setContentView(R.layout.activity_debug_mode);
setupToolbar();
setupMapView(savedInstanceState);
@@ -204,6 +206,7 @@ public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCa
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
+ HttpRequestUtil.setLogEnabled(true);
}
@Override
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 1825a0f1ab..aadf021a89 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
@@ -68,7 +68,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/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
index 568c53201f..e70c3410a8 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
@@ -69,4 +69,4 @@
<string name="description_textureview_animate">Animate a map rendered on a TextureView</string>
<string name="description_grid_source">Example Custom Geometry Source</string>
<string name="description_local_glyph">Suzhou using Droid Sans for Chinese glyphs</string>
-</resources> \ No newline at end of file
+</resources>
diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle
index 9a8115bf0f..a32e18166d 100644
--- a/platform/android/dependencies.gradle
+++ b/platform/android/dependencies.gradle
@@ -1,5 +1,5 @@
ext {
- minSdkVersion = 15
+ minSdkVersion = 14
targetSdkVersion = 25
compileSdkVersion = 25
buildToolsVersion = "26.0.3"
@@ -7,7 +7,7 @@ ext {
versionCode = 12
versionName = "6.0.0"
- mapboxServicesVersion = "2.2.8"
+ mapboxServicesVersion = "2.2.9"
supportLibVersion = "25.4.0"
espressoVersion = '3.0.1'
testRunnerVersion = '1.0.1'
@@ -20,11 +20,12 @@ ext {
mapboxAndroidTelemetry : "com.mapbox.mapboxsdk:mapbox-android-telemetry:${mapboxServicesVersion}@aar",
// mapzen lost
- lost : 'com.mapzen.android:lost:1.1.1',
+ lost : 'com.mapzen.android:lost:3.0.4',
// 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}",
@@ -41,7 +42,7 @@ ext {
// square crew
timber : 'com.jakewharton.timber:timber:4.5.1',
- okhttp3 : 'com.squareup.okhttp3:okhttp:3.9.0',
+ okhttp3 : 'com.squareup.okhttp3:okhttp:3.9.1',
leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}",
leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}"
]