diff options
Diffstat (limited to 'platform/android')
312 files changed, 11666 insertions, 4048 deletions
diff --git a/platform/android/.gitignore b/platform/android/.gitignore index 7a3db0aafd..4abd458378 100644 --- a/platform/android/.gitignore +++ b/platform/android/.gitignore @@ -20,7 +20,6 @@ local.properties # Token file MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml -MapboxGLAndroidSDKWearTestApp/src/main/res/values/developer-config.xml # Twitter Fabric / Crashlytics fabric.properties diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 75a6c4d2b9..8ce8b3d3c1 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,9 +2,11 @@ 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.1.4 +## 5.2.0 - TBA -* Russian and Ukrainian localizations [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945) +* Add support for ImageSource [#9110](https://github.com/mapbox/mapbox-gl-native/pull/9110) +* Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835)) +* TBA ### 5.1.4 - September 25, 2017 @@ -21,6 +23,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to * Avoid adding duplicate points to bounds [#9955](https://github.com/mapbox/mapbox-gl-native/pull/9955) * Download is complete fix [#9913](https://github.com/mapbox/mapbox-gl-native/pull/9913) * MAS 2.2.3 [#9901](https://github.com/mapbox/mapbox-gl-native/pull/9901) +* Russian and Ukrainian localizations [#9945](https://github.com/mapbox/mapbox-gl-native/pull/9945) ## 5.1.3 - August 18, 2017 diff --git a/platform/android/MapboxGLAndroidSDK/.gitignore b/platform/android/MapboxGLAndroidSDK/.gitignore new file mode 100644 index 0000000000..cec211fe81 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/.gitignore @@ -0,0 +1,2 @@ +lint-baseline.xml +lint/lint-baseline-local.xml
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 018294d462..c96587fce6 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -9,6 +9,8 @@ dependencies { compile(rootProject.ext.dep.lost) { exclude group: 'com.google.guava' } + testCompile rootProject.ext.dep.junit + testCompile rootProject.ext.dep.mockito // Mapbox Android Services (GeoJSON support) compile(rootProject.ext.dep.mapboxJavaGeoJSON) { @@ -39,11 +41,14 @@ android { // to invoke the Java tests. When we explicitly specify an ABI of 'none', no native dependencies are // added. When another ABI is specified explicitly, we're just going to build that ABI. In all other // cases, all ABIs are built. - // When invoking from the command line, set `-Pmapbox.abis=...` to only build the desired architectures. + // + // When invoking from the command line or to override the device default, set `-Pmapbox.abis=...` to + // only build the desired architectures. + // // When building from Android Studio, gradle.properties sets `android.buildOnlyTargetAbi=true` so that // only the architecture for the device you're running on gets built. def abi = 'all' - if (!project.hasProperty('android.injected.invoked.from.ide')) { + if (!project.hasProperty('android.injected.invoked.from.ide') || project.hasProperty("mapbox.abis")) { // Errors when the user invokes Gradle from the command line and didn't set mapbox.abis abi = project.getProperty("mapbox.abis") } @@ -114,8 +119,14 @@ android { } lintOptions { + disable 'MissingTranslation', 'TypographyQuotes' + baseline file("lint-baseline-local.xml") checkAllWarnings true - warningsAsErrors true + warningsAsErrors false + } + + testOptions { + unitTests.returnDefaultValues = true } buildTypes { @@ -145,3 +156,5 @@ configurations { apply from: 'gradle-javadoc.gradle' apply from: 'gradle-publish.gradle' apply from: 'gradle-checkstyle.gradle' +apply from: 'gradle-tests-staticblockremover.gradle' +apply from: '../gradle-lint.gradle' diff --git a/platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle b/platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle new file mode 100644 index 0000000000..523dc99dd1 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle @@ -0,0 +1,59 @@ +buildscript { + repositories { + mavenCentral() + mavenLocal() + } + + dependencies { + classpath 'com.darylteo.gradle:javassist-plugin:0.4.1' + } +} + +import com.darylteo.gradle.javassist.tasks.TransformationTask +import com.darylteo.gradle.javassist.transformers.ClassTransformer +import javassist.CtClass +import javassist.CtConstructor + +class StaticBlockRemover extends ClassTransformer { + + private static final NATIVE_MAP_VIEW = "com.mapbox.mapboxsdk.maps.NativeMapView"; + private static + final NATIVE_CONNECTIVITY_LISTENER = "com.mapbox.mapboxsdk.net.NativeConnectivityListener"; + private static final OFFLINE_MANAGER = "com.mapbox.mapboxsdk.offline.OfflineManager"; + private static final OFFLINE_REGION = "com.mapbox.mapboxsdk.offline.OfflineRegion"; + + public void applyTransformations(CtClass clazz) throws Exception { + if (shouldFilter(clazz)) { + CtConstructor constructor = clazz.getClassInitializer() + if (constructor != null) { + clazz.removeConstructor(constructor) + } + } + } + + public boolean shouldFilter(CtClass clazz) { + return hasAStaticBlock(clazz); + } + + private boolean hasAStaticBlock(CtClass clazz) { + String name = clazz.getName(); + boolean isNativeMapView = name.equals(NATIVE_MAP_VIEW); + boolean isNativeConnectivityListener = name.equals(NATIVE_CONNECTIVITY_LISTENER); + boolean isOfflineManager = name.equals(OFFLINE_MANAGER); + boolean isOfflineRegion = name.equals(OFFLINE_REGION); + + return isNativeMapView || isNativeConnectivityListener || isOfflineManager || isOfflineRegion; + } +} + +task removeStatic(type: TransformationTask) { + // TODO Find a better way to get output classes path + String fromToDirPath = buildDir.getAbsolutePath() + "/intermediates/classes/debug" + from fromToDirPath + transformation = new StaticBlockRemover() + into fromToDirPath +} + +afterEvaluate { + compileDebugUnitTestSources.dependsOn(removeStatic) +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index 078fdbc801..a9ec9fae88 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=5.2.0-SNAPSHOT +VERSION_NAME=5.2.0-beta-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native @@ -17,4 +17,4 @@ POM_PACKAGING=aar # Only build native dependencies for the current ABI # See https://code.google.com/p/android/issues/detail?id=221098#c20 -android.buildOnlyTargetAbi=true +android.buildOnlyTargetAbi=true
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml b/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml new file mode 100644 index 0000000000..0a76f53505 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/lint-baseline-local.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="4" by="lint 2.3.1"> + + <issue + id="MissingTranslation" + message=""`mapbox_attributionErrorNoBrowser`" is not translated in "ca" (Catalan), "es" (Spanish), "lt" (Lithuanian), "nl" (Dutch), "sv" (Swedish), "vi" (Vietnamese)" + errorLine1=" <string name="mapbox_attributionErrorNoBrowser">No web browser installed on device, can\'t open web page.</string>" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="src/main/res/values/strings.xml" + line="13" + column="13"/> + </issue> + + <issue + id="MissingTranslation" + message=""`mapbox_telemetrySettings`" is not translated in "ca" (Catalan), "es" (Spanish), "lt" (Lithuanian), "nl" (Dutch), "sv" (Swedish), "vi" (Vietnamese)" + errorLine1=" <string name="mapbox_telemetrySettings">Telemetry Settings</string>" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="src/main/res/values/strings.xml" + line="15" + column="13"/> + </issue> + + <issue + id="TypographyQuotes" + message="Replace straight quotes ('') with directional quotes (‘’, &#8216; and &#8217;) ?" + errorLine1=" <string name="mapbox_attributionTelemetryMessage">Estàs ajudant a millorar els mapes d\'OpenStreetMap i de Mapbox aportant dades d\'ús anònimes.</string>" + errorLine2=" ^"> + <location + file="src/main/res/values-ca/strings.xml" + line="9" + column="55"/> + </issue> + +</issues> diff --git a/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml b/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml new file mode 100644 index 0000000000..fd65c9f627 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/lint/lint-baseline-ci.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- REMEMBER! First you run Lint locally you'll need to move lint-baseline-local.xml.xml file + generated into the lint folder and called it lint-baseline-local.xml + If you remove any error when running Lint locally, you'll get a warning from the command + line advising you to remove it from the baseline. If you remove it (remember to remove it + from lint-baseline-local.xml file) you should remove it too from + lint-baseline-ci.xml (THIS FILE) which is the only one included in the repo. + Eventually, it'll be removed (when we remove all current lint errors included). --> +<issues format="4" by="lint 2.3.1"> + + <issue + id="MissingTranslation" + message=""`mapbox_attributionErrorNoBrowser`" is not translated in "ca" (Catalan), "es" (Spanish), "lt" (Lithuanian), "nl" (Dutch), "sv" (Swedish), "vi" (Vietnamese)" + errorLine1=" <string name="mapbox_attributionErrorNoBrowser">No web browser installed on device, can\'t open web page.</string>" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="src/main/res/values/strings.xml" + line="13" + column="13"/> + </issue> + + <issue + id="MissingTranslation" + message=""`mapbox_telemetrySettings`" is not translated in "ca" (Catalan), "es" (Spanish), "lt" (Lithuanian), "nl" (Dutch), "sv" (Swedish), "vi" (Vietnamese)" + errorLine1=" <string name="mapbox_telemetrySettings">Telemetry Settings</string>" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="src/main/res/values/strings.xml" + line="15" + column="13"/> + </issue> + + <issue + id="TypographyQuotes" + message="Replace straight quotes ('') with directional quotes (‘’, &#8216; and &#8217;) ?" + errorLine1=" <string name="mapbox_attributionTelemetryMessage">Estàs ajudant a millorar els mapes d\'OpenStreetMap i de Mapbox aportant dades d\'ús anònimes.</string>" + errorLine2=" ^"> + <location + file="src/main/res/values-ca/strings.xml" + line="9" + column="55"/> + </issue> + +</issues> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java new file mode 100644 index 0000000000..8a75176ccd --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java @@ -0,0 +1,15 @@ +package com.mapbox.mapboxsdk; + +/** + * Centralises the knowledge about "mapbox-gl" library loading. + */ +public class LibraryLoader { + + /** + * Loads "libmapbox-gl.so" native shared library. + */ + public static void load() { + System.loadLibrary("mapbox-gl"); + } + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java index eadc3fdcf5..7fd9d6172d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk; +import android.annotation.SuppressLint; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -25,8 +26,10 @@ import timber.log.Timber; * connectivity state. * </p> */ +@UiThread public final class Mapbox { + @SuppressLint("StaticFieldLeak") private static Mapbox INSTANCE; private Context context; private String accessToken; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java index ddedf3debf..3fd2fa4ebf 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java @@ -14,7 +14,10 @@ import com.mapbox.mapboxsdk.geometry.LatLng; * * @param <U> Type of the marker view to be composed. * @param <T> Type of the builder to be used for composing. + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ +@Deprecated public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends BaseMarkerViewOptions<U, T>> implements Parcelable { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java index f9ca9bf4cc..3c9cb31211 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk.annotations; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -35,6 +36,7 @@ public final class IconFactory { public static final String ICON_MARKERVIEW_ID = ICON_ID_PREFIX + "marker_view"; private Context context; + @SuppressLint("StaticFieldLeak") private static IconFactory instance; private Icon defaultMarker; private Icon defaultMarkerView; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java index 56e8cc4ce2..eb82c7bf53 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java @@ -24,7 +24,10 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; * used with event listeners to bring up info windows. An {@link InfoWindow} is displayed by default * when either a title or snippet is provided. * </p> + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ +@Deprecated public class MarkerView extends Marker { private MarkerViewManager markerViewManager; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java index 8704e882ea..8304d0e6ed 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java @@ -30,7 +30,10 @@ import java.util.Map; * <p> * This class is responsible for managing a {@link MarkerView} item. * </p> + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ +@Deprecated public class MarkerViewManager implements MapView.OnMapChangedListener { private final ViewGroup markerViewContainer; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java index 2d829537fc..79c72e5f70 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java @@ -12,7 +12,10 @@ import com.mapbox.mapboxsdk.geometry.LatLng; * <p> * Do not extend this class directly but extend {@link BaseMarkerViewOptions} instead. * </p> + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ +@Deprecated public class MarkerViewOptions extends BaseMarkerViewOptions<MarkerView, MarkerViewOptions> { private MarkerView marker; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java index 1ee59057d2..97a9ea94ee 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java @@ -55,7 +55,7 @@ public class MapboxConstants { /** * The currently supported maximum zoom level. */ - public static final float MAXIMUM_ZOOM = 20.0f; + public static final float MAXIMUM_ZOOM = 25.5f; /** * The currently supported maximum tilt value. diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java new file mode 100644 index 0000000000..7fc70716da --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigChooser.java @@ -0,0 +1,293 @@ +package com.mapbox.mapboxsdk.egl; + +import android.opengl.GLSurfaceView; +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLDisplay; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.utils.Compare.compare; +import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_MASK_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_BUFFER_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_COLOR_BUFFER_TYPE; +import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT; +import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_NONE; +import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE; +import static javax.microedition.khronos.egl.EGL10.EGL_RGB_BUFFER; +import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES; +import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS; +import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE; +import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT; + +/** + * Selects the right EGLConfig needed for `mapbox-gl-native` + */ +public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser { + + /** + * Requires API level 17 + * + * @see android.opengl.EGL14.EGL_CONFORMANT; + */ + @SuppressWarnings("JavadocReference") + private static final int EGL_CONFORMANT = 0x3042; + + /** + * Requires API level 17 + * + * @see android.opengl.EGL14.EGL_OPENGL_ES2_BIT; + */ + @SuppressWarnings("JavadocReference") + private static final int EGL_OPENGL_ES2_BIT = 0x0004; + + @Override + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + int[] configAttribs = getConfigAttributes(); + + // Determine number of possible configurations + int[] numConfigs = getNumberOfConfigurations(egl, display, configAttribs); + if (numConfigs[0] < 1) { + Timber.e("eglChooseConfig() returned no configs."); + throw new EGLConfigException("eglChooseConfig() failed"); + } + + // Get all possible configurations + EGLConfig[] possibleConfigurations = getPossibleConfigurations(egl, display, configAttribs, numConfigs); + + // Choose best match + EGLConfig config = chooseBestMatchConfig(egl, display, possibleConfigurations); + if (config == null) { + Timber.e("No config chosen"); + throw new EGLConfigException("No config chosen"); + } + + return config; + } + + private int[] getNumberOfConfigurations(EGL10 egl, EGLDisplay display, int[] configAttributes) { + int[] numConfigs = new int[1]; + if (!egl.eglChooseConfig(display, configAttributes, null, 0, numConfigs)) { + Timber.e("eglChooseConfig(NULL) returned error %d", egl.eglGetError()); + throw new EGLConfigException("eglChooseConfig() failed"); + } + return numConfigs; + } + + private EGLConfig[] getPossibleConfigurations(EGL10 egl, EGLDisplay display, + int[] configAttributes, int[] numConfigs) { + EGLConfig[] configs = new EGLConfig[numConfigs[0]]; + if (!egl.eglChooseConfig(display, configAttributes, configs, numConfigs[0], numConfigs)) { + Timber.e("eglChooseConfig() returned error %d", egl.eglGetError()); + throw new EGLConfigException("eglChooseConfig() failed"); + } + return configs; + } + + // Quality + enum BufferFormat { + Format16Bit(3), + Format32BitNoAlpha(1), + Format32BitAlpha(2), + Format24Bit(0), + Unknown(4); + + int value; + + BufferFormat(int value) { + this.value = value; + } + } + + enum DepthStencilFormat { + Format16Depth8Stencil(1), + Format24Depth8Stencil(0); + + int value; + + DepthStencilFormat(int value) { + this.value = value; + } + } + + private EGLConfig chooseBestMatchConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { + class Config implements Comparable<Config> { + private final BufferFormat bufferFormat; + private final DepthStencilFormat depthStencilFormat; + private final boolean isNotConformant; + private final boolean isCaveat; + private final int index; + private final EGLConfig config; + + public Config(BufferFormat bufferFormat, DepthStencilFormat depthStencilFormat, + boolean isNotConformant, boolean isCaveat, int index, EGLConfig config) { + this.bufferFormat = bufferFormat; + this.depthStencilFormat = depthStencilFormat; + this.isNotConformant = isNotConformant; + this.isCaveat = isCaveat; + this.index = index; + this.config = config; + } + + + @Override + public int compareTo(@NonNull Config other) { + int i = compare(bufferFormat.value, other.bufferFormat.value); + if (i != 0) { + return i; + } + + i = compare(depthStencilFormat.value, other.depthStencilFormat.value); + if (i != 0) { + return i; + } + + i = compare(isNotConformant, other.isNotConformant); + if (i != 0) { + return i; + } + + i = compare(isCaveat, other.isCaveat); + if (i != 0) { + return i; + } + + i = compare(index, other.index); + if (i != 0) { + return i; + } + + return 0; + } + } + + List<Config> matches = new ArrayList<>(); + + int i = 0; + for (EGLConfig config : configs) { + i++; + + int caveat = getConfigAttr(egl, display, config, EGL_CONFIG_CAVEAT); + int conformant = getConfigAttr(egl, display, config, EGL_CONFORMANT); + int bits = getConfigAttr(egl, display, config, EGL_BUFFER_SIZE); + int red = getConfigAttr(egl, display, config, EGL_RED_SIZE); + int green = getConfigAttr(egl, display, config, EGL_GREEN_SIZE); + int blue = getConfigAttr(egl, display, config, EGL_BLUE_SIZE); + int alpha = getConfigAttr(egl, display, config, EGL_ALPHA_SIZE); + int alphaMask = getConfigAttr(egl, display, config, EGL_ALPHA_MASK_SIZE); + int depth = getConfigAttr(egl, display, config, EGL_DEPTH_SIZE); + int stencil = getConfigAttr(egl, display, config, EGL_STENCIL_SIZE); + int sampleBuffers = getConfigAttr(egl, display, config, EGL_SAMPLE_BUFFERS); + int samples = getConfigAttr(egl, display, config, EGL_SAMPLES); + + boolean configOk = (depth == 24) || (depth == 16); + configOk &= stencil == 8; + configOk &= sampleBuffers == 0; + configOk &= samples == 0; + + // Filter our configs first for depth, stencil and anti-aliasing + if (configOk) { + // Work out the config's buffer format + BufferFormat bufferFormat; + if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) { + bufferFormat = BufferFormat.Format16Bit; + } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) { + bufferFormat = BufferFormat.Format32BitNoAlpha; + } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) { + bufferFormat = BufferFormat.Format32BitAlpha; + } else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) { + bufferFormat = BufferFormat.Format24Bit; + } else { + bufferFormat = BufferFormat.Unknown; + } + + // Work out the config's depth stencil format + DepthStencilFormat depthStencilFormat; + if ((depth == 16) && (stencil == 8)) { + depthStencilFormat = DepthStencilFormat.Format16Depth8Stencil; + } else { + depthStencilFormat = DepthStencilFormat.Format24Depth8Stencil; + } + + boolean isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT; + boolean isCaveat = caveat != EGL_NONE; + + // Ignore formats we don't recognise + if (bufferFormat != BufferFormat.Unknown) { + matches.add(new Config(bufferFormat, depthStencilFormat, isNotConformant, isCaveat, i, config)); + } + } + + } + + // Sort + Collections.sort(matches); + + if (matches.size() == 0) { + throw new EGLConfigException("No matching configurations after filtering"); + } + + Config bestMatch = matches.get(0); + + if (bestMatch.isCaveat) { + Timber.w("Chosen config has a caveat."); + } + + if (bestMatch.isNotConformant) { + Timber.w("Chosen config is not conformant."); + } + + return bestMatch.config; + } + + private int getConfigAttr(EGL10 egl, EGLDisplay display, EGLConfig config, int attributeName) { + int[] attributevalue = new int[1]; + if (!egl.eglGetConfigAttrib(display, config, attributeName, attributevalue)) { + Timber.e("eglGetConfigAttrib(%d) returned error %d", attributeName, egl.eglGetError()); + throw new EGLConfigException("eglGetConfigAttrib() failed"); + } + return attributevalue[0]; + } + + + private int[] getConfigAttributes() { + boolean emulator = inEmulator(); + Timber.i("In emulator: %s", emulator); + + // Get all configs at least RGB 565 with 16 depth and 8 stencil + return new int[] { + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BUFFER_SIZE, 16, + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + (emulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT, + (emulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + } + + /** + * Detect if we are in emulator. + */ + private boolean inEmulator() { + return System.getProperty("ro.kernel.qemu") != null; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java new file mode 100644 index 0000000000..3f576d0eda --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/egl/EGLConfigException.java @@ -0,0 +1,21 @@ +package com.mapbox.mapboxsdk.egl; + +/** + * Used for EGL configuration exceptions + */ +public class EGLConfigException extends RuntimeException { + public EGLConfigException() { + } + + public EGLConfigException(String message) { + super(message); + } + + public EGLConfigException(String message, Throwable cause) { + super(message, cause); + } + + public EGLConfigException(Throwable cause) { + super(cause); + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngQuad.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngQuad.java new file mode 100644 index 0000000000..e374eee8f3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngQuad.java @@ -0,0 +1,87 @@ +package com.mapbox.mapboxsdk.geometry; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A geographical area representing a non-aligned quadrilateral + * <p> + * This class does not wrap values to the world bounds + * </p> + */ +public class LatLngQuad implements Parcelable { + + private final LatLng topLeft; + private final LatLng topRight; + private final LatLng bottomRight; + private final LatLng bottomLeft; + + /** + * Construct a new LatLngQuad based on its corners, + * in order top left, top right, bottom left, bottom right + */ + public LatLngQuad(final LatLng topLeft, final LatLng topRight, final LatLng bottomRight, final LatLng bottomLeft) { + this.topLeft = topLeft; + this.topRight = topRight; + this.bottomRight = bottomRight; + this.bottomLeft = bottomLeft; + } + + public LatLng getTopLeft() { + return this.topLeft; + } + + public LatLng getTopRight() { + return this.topRight; + } + + public LatLng getBottomRight() { + return this.bottomRight; + } + + public LatLng getBottomLeft() { + return this.bottomLeft; + } + + public static final Parcelable.Creator<LatLngQuad> CREATOR = new Parcelable.Creator<LatLngQuad>() { + @Override + public LatLngQuad createFromParcel(final Parcel in) { + return readFromParcel(in); + } + + @Override + public LatLngQuad[] newArray(final int size) { + return new LatLngQuad[size]; + } + }; + + @Override + public int hashCode() { + int code = topLeft.hashCode(); + code = (code ^ code >>> 31) + topRight.hashCode(); + code = (code ^ code >>> 31) + bottomRight.hashCode(); + code = (code ^ code >>> 31) + bottomLeft.hashCode(); + return code; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel out, final int arg1) { + topLeft.writeToParcel(out, arg1); + topRight.writeToParcel(out, arg1); + bottomRight.writeToParcel(out, arg1); + bottomLeft.writeToParcel(out, arg1); + } + + private static LatLngQuad readFromParcel(final Parcel in) { + final LatLng topLeft = new LatLng(in); + final LatLng topRight = new LatLng(in); + final LatLng bottomRight = new LatLng(in); + final LatLng bottomLeft = new LatLng(in); + return new LatLngQuad(topLeft, topRight, bottomRight, bottomLeft); + } +} 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 7f3a48c57a..e2626a026b 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 @@ -110,13 +110,11 @@ class HTTPRequest implements Callback { @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful()) { - Timber.v(String.format("[HTTP] Request was successful (code = %d).", response.code())); + 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(String.format( - "[HTTP] Request with response code = %d: %s", - response.code(), message)); + Timber.d("[HTTP] Request with response code = %s: %s", response.code(), message); } byte[] body; @@ -161,15 +159,12 @@ class HTTPRequest implements Callback { String errorMessage = e.getMessage() != null ? e.getMessage() : "Error processing the request"; if (type == TEMPORARY_ERROR) { - Timber.d(String.format(MapboxConstants.MAPBOX_LOCALE, - "Request failed due to a temporary error: %s", errorMessage)); + Timber.d("Request failed due to a temporary error: %s", errorMessage); } else if (type == CONNECTION_ERROR) { - Timber.i(String.format(MapboxConstants.MAPBOX_LOCALE, - "Request failed due to a connection error: %s", errorMessage)); + Timber.i("Request failed due to a connection error: %s", errorMessage); } else { // PERMANENT_ERROR - Timber.w(String.format(MapboxConstants.MAPBOX_LOCALE, - "Request failed due to a permanent error: %s", errorMessage)); + Timber.w("Request failed due to a permanent error: %s", errorMessage); } mLock.lock(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java new file mode 100644 index 0000000000..939fadc9c2 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java @@ -0,0 +1,86 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; +import android.support.v4.util.LongSparseArray; + +import com.mapbox.mapboxsdk.annotations.Annotation; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates {@link Annotation}'s functionality.. + */ +class AnnotationContainer implements Annotations { + + private final NativeMapView nativeMapView; + private final LongSparseArray<Annotation> annotations; + + AnnotationContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) { + this.nativeMapView = nativeMapView; + this.annotations = annotations; + } + + @Override + public Annotation obtainBy(long id) { + return annotations.get(id); + } + + @Override + public List<Annotation> obtainAll() { + List<Annotation> annotations = new ArrayList<>(); + for (int i = 0; i < this.annotations.size(); i++) { + annotations.add(this.annotations.get(this.annotations.keyAt(i))); + } + return annotations; + } + + @Override + public void removeBy(long id) { + if (nativeMapView != null) { + nativeMapView.removeAnnotation(id); + } + annotations.remove(id); + } + + @Override + public void removeBy(@NonNull Annotation annotation) { + long id = annotation.getId(); + removeBy(id); + } + + @Override + public void removeBy(@NonNull List<? extends Annotation> annotationList) { + int count = annotationList.size(); + long[] ids = new long[count]; + for (int i = 0; i < count; i++) { + ids[i] = annotationList.get(i).getId(); + } + + removeNativeAnnotations(ids); + + for (long id : ids) { + annotations.remove(id); + } + } + + @Override + public void removeAll() { + int count = annotations.size(); + long[] ids = new long[count]; + for (int i = 0; i < count; i++) { + ids[i] = annotations.keyAt(i); + } + + removeNativeAnnotations(ids); + + annotations.clear(); + } + + private void removeNativeAnnotations(long[] ids) { + if (nativeMapView != null) { + nativeMapView.removeAnnotations(ids); + } + } +}
\ 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 d15d5eddf8..c09c926eb5 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 @@ -14,7 +14,6 @@ import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; -import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerView; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; @@ -45,12 +44,11 @@ class AnnotationManager { private static final String LAYER_ID_SHAPE_ANNOTATIONS = "com.mapbox.annotations.shape."; private static final long NO_ANNOTATION_ID = -1; - private final NativeMapView nativeMapView; private final MapView mapView; private final IconManager iconManager; private final InfoWindowManager infoWindowManager = new InfoWindowManager(); private final MarkerViewManager markerViewManager; - private final LongSparseArray<Annotation> annotations = new LongSparseArray<>(); + private final LongSparseArray<Annotation> annotationsArray; private final List<Marker> selectedMarkers = new ArrayList<>(); private final List<String> shapeAnnotationIds = new ArrayList<>(); @@ -59,11 +57,22 @@ class AnnotationManager { private MapboxMap.OnPolygonClickListener onPolygonClickListener; private MapboxMap.OnPolylineClickListener onPolylineClickListener; - AnnotationManager(NativeMapView view, MapView mapView, MarkerViewManager markerViewManager) { - this.nativeMapView = view; + private Annotations annotations; + private Markers markers; + private Polygons polygons; + private Polylines polylines; + + AnnotationManager(NativeMapView view, MapView mapView, LongSparseArray<Annotation> annotationsArray, + MarkerViewManager markerViewManager, IconManager iconManager, Annotations annotations, + Markers markers, Polygons polygons, Polylines polylines) { this.mapView = mapView; - this.iconManager = new IconManager(nativeMapView); + this.annotationsArray = annotationsArray; this.markerViewManager = markerViewManager; + this.iconManager = iconManager; + this.annotations = annotations; + this.markers = markers; + this.polygons = polygons; + this.polylines = polylines; if (view != null) { // null checking needed for unit tests view.addOnMapChangedListener(markerViewManager); @@ -88,15 +97,15 @@ class AnnotationManager { // Annotation getAnnotation(long id) { - return annotations.get(id); + return annotations.obtainBy(id); } List<Annotation> getAnnotations() { - List<Annotation> annotations = new ArrayList<>(); - for (int i = 0; i < this.annotations.size(); i++) { - annotations.add(this.annotations.get(this.annotations.keyAt(i))); - } - return annotations; + return annotations.obtainAll(); + } + + void removeAnnotation(long id) { + annotations.removeBy(id); } void removeAnnotation(@NonNull Annotation annotation) { @@ -109,30 +118,19 @@ class AnnotationManager { if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); + } else { + // do icon cleanup + iconManager.iconCleanup(marker.getIcon()); } } else { // instanceOf Polygon/Polyline shapeAnnotationIds.remove(annotation.getId()); } - long id = annotation.getId(); - if (nativeMapView != null) { - nativeMapView.removeAnnotation(id); - } - annotations.remove(id); - } - - void removeAnnotation(long id) { - if (nativeMapView != null) { - nativeMapView.removeAnnotation(id); - } - annotations.remove(id); + annotations.removeBy(annotation); } void removeAnnotations(@NonNull List<? extends Annotation> annotationList) { - int count = annotationList.size(); - long[] ids = new long[count]; - for (int i = 0; i < count; i++) { - Annotation annotation = annotationList.get(i); + for (Annotation annotation : annotationList) { if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.hideInfoWindow(); @@ -142,48 +140,39 @@ class AnnotationManager { if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); + } else { + iconManager.iconCleanup(marker.getIcon()); } } else { // instanceOf Polygon/Polyline shapeAnnotationIds.remove(annotation.getId()); } - ids[i] = annotationList.get(i).getId(); - } - - if (nativeMapView != null) { - nativeMapView.removeAnnotations(ids); - } - - for (long id : ids) { - annotations.remove(id); } + annotations.removeBy(annotationList); } void removeAnnotations() { Annotation annotation; - int count = annotations.size(); + int count = annotationsArray.size(); long[] ids = new long[count]; selectedMarkers.clear(); for (int i = 0; i < count; i++) { - ids[i] = annotations.keyAt(i); - annotation = annotations.get(ids[i]); + ids[i] = annotationsArray.keyAt(i); + annotation = annotationsArray.get(ids[i]); if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.hideInfoWindow(); if (marker instanceof MarkerView) { markerViewManager.removeMarkerView((MarkerView) marker); + } else { + iconManager.iconCleanup(marker.getIcon()); } } else { // instanceOf Polygon/Polyline shapeAnnotationIds.remove(annotation.getId()); } } - - if (nativeMapView != null) { - nativeMapView.removeAnnotations(ids); - } - - annotations.clear(); + annotations.removeAll(); } // @@ -191,134 +180,109 @@ class AnnotationManager { // Marker addMarker(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap) { - Marker marker = prepareMarker(markerOptions); - long id = nativeMapView != null ? nativeMapView.addMarker(marker) : 0; - marker.setMapboxMap(mapboxMap); - marker.setId(id); - annotations.put(id, marker); - return marker; + return markers.addBy(markerOptions, mapboxMap); } List<Marker> addMarkers(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap mapboxMap) { - int count = markerOptionsList.size(); - List<Marker> markers = new ArrayList<>(count); - if (count > 0) { - BaseMarkerOptions markerOptions; - Marker marker; - for (int i = 0; i < count; i++) { - markerOptions = markerOptionsList.get(i); - marker = prepareMarker(markerOptions); - markers.add(marker); - } - - if (markers.size() > 0) { - long[] ids; - if (nativeMapView != null) { - ids = nativeMapView.addMarkers(markers); - } else { - ids = new long[markers.size()]; - } - - long id; - Marker m; - for (int i = 0; i < ids.length; i++) { - m = markers.get(i); - m.setMapboxMap(mapboxMap); - id = ids[i]; - m.setId(id); - annotations.put(id, m); - } + return markers.addBy(markerOptionsList, mapboxMap); + } - } + void updateMarker(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) { + if (!isAddedToMap(updatedMarker)) { + logNonAdded(updatedMarker); + return; } - return markers; + markers.update(updatedMarker, mapboxMap); + } + + List<Marker> getMarkers() { + return markers.obtainAll(); } - private Marker prepareMarker(BaseMarkerOptions markerOptions) { - Marker marker = markerOptions.getMarker(); - Icon icon = iconManager.loadIconForMarker(marker); - marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon)); - return marker; + @NonNull + List<Marker> getMarkersInRect(@NonNull RectF rectangle) { + return markers.obtainAllIn(rectangle); } MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap, @Nullable MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) { - final MarkerView marker = prepareViewMarker(markerOptions); - - // add marker to map - marker.setMapboxMap(mapboxMap); - long id = nativeMapView.addMarker(marker); - marker.setId(id); - annotations.put(id, marker); - - if (onMarkerViewAddedListener != null) { - markerViewManager.addOnMarkerViewAddedListener(marker, onMarkerViewAddedListener); - } - markerViewManager.setEnabled(true); - markerViewManager.setWaitingForRenderInvoke(true); - return marker; + return markers.addViewBy(markerOptions, mapboxMap, onMarkerViewAddedListener); } List<MarkerView> addMarkerViews(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions, @NonNull MapboxMap mapboxMap) { - List<MarkerView> markers = new ArrayList<>(); - for (BaseMarkerViewOptions markerViewOption : markerViewOptions) { - // if last marker - if (markerViewOptions.indexOf(markerViewOption) == markerViewOptions.size() - 1) { - // get notified when render occurs to invalidate and draw MarkerViews - markerViewManager.setWaitingForRenderInvoke(true); - } - // add marker to map - MarkerView marker = prepareViewMarker(markerViewOption); - marker.setMapboxMap(mapboxMap); - long id = nativeMapView.addMarker(marker); - marker.setId(id); - annotations.put(id, marker); - markers.add(marker); - } - markerViewManager.setEnabled(true); - markerViewManager.update(); - return markers; + return markers.addViewsBy(markerViewOptions, mapboxMap); } - private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) { - MarkerView marker = markerViewOptions.getMarker(); - iconManager.loadIconForMarkerView(marker); - return marker; + List<MarkerView> getMarkerViewsInRect(@NonNull RectF rectangle) { + return markers.obtainViewsIn(rectangle); } - void updateMarker(@NonNull Marker updatedMarker) { - if (!isAddedToMap(updatedMarker)) { - Timber.w("Attempting to update non-added Marker with value %s", updatedMarker); + void reloadMarkers() { + markers.reload(); + } + + // + // Polygons + // + + Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) { + Polygon polygon = polygons.addBy(polygonOptions, mapboxMap); + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId()); + return polygon; + } + + List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) { + List<Polygon> polygonList = polygons.addBy(polygonOptionsList, mapboxMap); + for (Polygon polygon : polygonList) { + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polygon.getId()); + } + return polygonList; + } + + void updatePolygon(Polygon polygon) { + if (!isAddedToMap(polygon)) { + logNonAdded(polygon); return; } - ensureIconLoaded(updatedMarker); - nativeMapView.updateMarker(updatedMarker); - annotations.setValueAt(annotations.indexOfKey(updatedMarker.getId()), updatedMarker); + polygons.update(polygon); } - private boolean isAddedToMap(Annotation annotation) { - return annotation != null && annotation.getId() != -1 && annotations.indexOfKey(annotation.getId()) > -1; + List<Polygon> getPolygons() { + return polygons.obtainAll(); + } + + // + // Polylines + // + + Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) { + Polyline polyline = polylines.addBy(polylineOptions, mapboxMap); + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId()); + return polyline; } - private void ensureIconLoaded(Marker marker) { - if (!(marker instanceof MarkerView)) { - iconManager.ensureIconLoaded(marker, mapboxMap); + List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) { + List<Polyline> polylineList = polylines.addBy(polylineOptionsList, mapboxMap); + for (Polyline polyline : polylineList) { + shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + polyline.getId()); } + return polylineList; } - List<Marker> getMarkers() { - List<Marker> markers = new ArrayList<>(); - Annotation annotation; - for (int i = 0; i < annotations.size(); i++) { - annotation = annotations.get(annotations.keyAt(i)); - if (annotation instanceof Marker) { - markers.add((Marker) annotation); - } + void updatePolyline(Polyline polyline) { + if (!isAddedToMap(polyline)) { + logNonAdded(polyline); + return; } - return markers; + polylines.update(polyline); } + List<Polyline> getPolylines() { + return polylines.obtainAll(); + } + + // TODO Refactor from here still in progress void setOnMarkerClickListener(@Nullable MapboxMap.OnMarkerClickListener listener) { onMarkerClickListener = listener; } @@ -393,205 +357,6 @@ class AnnotationManager { return selectedMarkers; } - @NonNull - List<Marker> getMarkersInRect(@NonNull RectF rectangle) { - // convert Rectangle to be density dependent - float pixelRatio = nativeMapView.getPixelRatio(); - RectF rect = new RectF(rectangle.left / pixelRatio, - rectangle.top / pixelRatio, - rectangle.right / pixelRatio, - rectangle.bottom / pixelRatio); - - long[] ids = nativeMapView.queryPointAnnotations(rect); - - List<Long> idsList = new ArrayList<>(ids.length); - for (long id : ids) { - idsList.add(id); - } - - List<Marker> annotations = new ArrayList<>(ids.length); - List<Annotation> annotationList = getAnnotations(); - int count = annotationList.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotationList.get(i); - if (annotation instanceof com.mapbox.mapboxsdk.annotations.Marker && idsList.contains(annotation.getId())) { - annotations.add((com.mapbox.mapboxsdk.annotations.Marker) annotation); - } - } - - return new ArrayList<>(annotations); - } - - List<MarkerView> getMarkerViewsInRect(@NonNull RectF rectangle) { - float pixelRatio = nativeMapView.getPixelRatio(); - RectF rect = new RectF(rectangle.left / pixelRatio, - rectangle.top / pixelRatio, - rectangle.right / pixelRatio, - rectangle.bottom / pixelRatio); - - long[] ids = nativeMapView.queryPointAnnotations(rect); - - List<Long> idsList = new ArrayList<>(ids.length); - for (long id : ids) { - idsList.add(id); - } - - List<MarkerView> annotations = new ArrayList<>(ids.length); - List<Annotation> annotationList = getAnnotations(); - int count = annotationList.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotationList.get(i); - if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) { - annotations.add((MarkerView) annotation); - } - } - - return new ArrayList<>(annotations); - } - - // - // Polygons - // - - Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) { - Polygon polygon = polygonOptions.getPolygon(); - if (!polygon.getPoints().isEmpty()) { - long id = nativeMapView != null ? nativeMapView.addPolygon(polygon) : 0; - polygon.setId(id); - polygon.setMapboxMap(mapboxMap); - shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id); - annotations.put(id, polygon); - } - return polygon; - } - - List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) { - int count = polygonOptionsList.size(); - - Polygon polygon; - List<Polygon> polygons = new ArrayList<>(count); - if (count > 0) { - for (PolygonOptions polygonOptions : polygonOptionsList) { - polygon = polygonOptions.getPolygon(); - if (!polygon.getPoints().isEmpty()) { - polygons.add(polygon); - } - } - - long[] ids; - if (nativeMapView != null) { - ids = nativeMapView.addPolygons(polygons); - } else { - ids = new long[polygons.size()]; - } - - long id; - for (int i = 0; i < ids.length; i++) { - polygon = polygons.get(i); - polygon.setMapboxMap(mapboxMap); - id = ids[i]; - polygon.setId(id); - shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id); - annotations.put(id, polygon); - } - } - return polygons; - } - - void updatePolygon(@NonNull Polygon polygon) { - if (!isAddedToMap(polygon)) { - Timber.w("Attempting to update non-added Polygon with value %s", polygon); - return; - } - - nativeMapView.updatePolygon(polygon); - annotations.setValueAt(annotations.indexOfKey(polygon.getId()), polygon); - } - - List<Polygon> getPolygons() { - List<Polygon> polygons = new ArrayList<>(); - Annotation annotation; - for (int i = 0; i < annotations.size(); i++) { - annotation = annotations.get(annotations.keyAt(i)); - if (annotation instanceof Polygon) { - polygons.add((Polygon) annotation); - } - } - return polygons; - } - - // - // Polylines - // - - Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) { - Polyline polyline = polylineOptions.getPolyline(); - if (!polyline.getPoints().isEmpty()) { - long id = nativeMapView != null ? nativeMapView.addPolyline(polyline) : 0; - polyline.setMapboxMap(mapboxMap); - polyline.setId(id); - shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id); - annotations.put(id, polyline); - } - return polyline; - } - - List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) { - int count = polylineOptionsList.size(); - Polyline polyline; - List<Polyline> polylines = new ArrayList<>(count); - - if (count > 0) { - for (PolylineOptions options : polylineOptionsList) { - polyline = options.getPolyline(); - if (!polyline.getPoints().isEmpty()) { - polylines.add(polyline); - } - } - - long[] ids; - if (nativeMapView != null) { - ids = nativeMapView.addPolylines(polylines); - } else { - ids = new long[polylines.size()]; - } - - long id; - Polyline p; - for (int i = 0; i < ids.length; i++) { - p = polylines.get(i); - p.setMapboxMap(mapboxMap); - id = ids[i]; - p.setId(id); - shapeAnnotationIds.add(LAYER_ID_SHAPE_ANNOTATIONS + id); - annotations.put(id, p); - } - } - return polylines; - } - - void updatePolyline(@NonNull Polyline polyline) { - if (!isAddedToMap(polyline)) { - Timber.w("Attempting to update non-added Polyline with value %s", polyline); - return; - } - - nativeMapView.updatePolyline(polyline); - annotations.setValueAt(annotations.indexOfKey(polyline.getId()), polyline); - } - - List<Polyline> getPolylines() { - List<Polyline> polylines = new ArrayList<>(); - Annotation annotation; - for (int i = 0; i < annotations.size(); i++) { - annotation = annotations.get(annotations.keyAt(i)); - if (annotation instanceof Polyline) { - polylines.add((Polyline) annotation); - } - } - return polylines; - } - InfoWindowManager getInfoWindowManager() { return infoWindowManager; } @@ -601,9 +366,9 @@ class AnnotationManager { } void adjustTopOffsetPixels(MapboxMap mapboxMap) { - int count = annotations.size(); + int count = annotationsArray.size(); for (int i = 0; i < count; i++) { - Annotation annotation = annotations.get(i); + Annotation annotation = annotationsArray.get(i); if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.setTopOffsetPixels( @@ -619,18 +384,12 @@ class AnnotationManager { } } - void reloadMarkers() { - iconManager.reloadIcons(); - int count = annotations.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotations.get(i); - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - nativeMapView.removeAnnotation(annotation.getId()); - long newId = nativeMapView.addMarker(marker); - marker.setId(newId); - } - } + private boolean isAddedToMap(Annotation annotation) { + return annotation != null && annotation.getId() != -1 && annotationsArray.indexOfKey(annotation.getId()) > -1; + } + + private void logNonAdded(Annotation annotation) { + Timber.w("Attempting to update non-added %s with value %s", annotation.getClass().getCanonicalName(), annotation); } // diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java new file mode 100644 index 0000000000..ae41cbb0cb --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java @@ -0,0 +1,25 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; + +import com.mapbox.mapboxsdk.annotations.Annotation; + +import java.util.List; + +/** + * Interface that defines convenient methods for working with a {@link Annotation}'s collection. + */ +interface Annotations { + Annotation obtainBy(long id); + + List<Annotation> obtainAll(); + + void removeBy(long id); + + void removeBy(@NonNull Annotation annotation); + + void removeBy(@NonNull List<? extends Annotation> annotationList); + + void removeAll(); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java index 18eecfd9c3..b1d6df2103 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java @@ -7,10 +7,10 @@ import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerView; -import com.mapbox.mapboxsdk.exceptions.IconBitmapChangedException; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Responsible for managing icons added to the Map. @@ -25,15 +25,14 @@ import java.util.List; */ class IconManager { - private NativeMapView nativeMapView; - private List<Icon> icons; + private final Map<Icon, Integer> iconMap = new HashMap<>(); + private NativeMapView nativeMapView; private int highestIconWidth; private int highestIconHeight; IconManager(NativeMapView nativeMapView) { this.nativeMapView = nativeMapView; - this.icons = new ArrayList<>(); // load transparent icon for MarkerView to trace actual markers, see #6352 loadIcon(IconFactory.recreate(IconFactory.ICON_MARKERVIEW_ID, IconFactory.ICON_MARKERVIEW_BITMAP)); } @@ -83,13 +82,13 @@ class IconManager { } private void addIcon(Icon icon, boolean addIconToMap) { - if (!icons.contains(icon)) { - icons.add(icon); + if (!iconMap.keySet().contains(icon)) { + iconMap.put(icon, 1); if (addIconToMap) { loadIcon(icon); } } else { - validateIconChanged(icon); + iconMap.put(icon, iconMap.get(icon) + 1); } } @@ -121,18 +120,11 @@ class IconManager { } void reloadIcons() { - for (Icon icon : icons) { + for (Icon icon : iconMap.keySet()) { loadIcon(icon); } } - private void validateIconChanged(Icon icon) { - Icon oldIcon = icons.get(icons.indexOf(icon)); - if (!oldIcon.getBitmap().sameAs(icon.getBitmap())) { - throw new IconBitmapChangedException(); - } - } - void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) { Icon icon = marker.getIcon(); if (icon == null) { @@ -149,4 +141,23 @@ class IconManager { marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon)); } } + + void iconCleanup(Icon icon) { + int refCounter = iconMap.get(icon) - 1; + if (refCounter == 0) { + remove(icon); + } else { + updateIconRefCounter(icon, refCounter); + } + } + + private void remove(Icon icon) { + nativeMapView.removeAnnotationIcon(icon.getId()); + iconMap.remove(icon); + } + + private void updateIconRefCounter(Icon icon, int refCounter) { + iconMap.put(icon, refCounter); + } + } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java index 11100d6f17..af207204d9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java @@ -21,7 +21,8 @@ import java.util.List; */ class InfoWindowManager { - private List<InfoWindow> infoWindows; + private final List<InfoWindow> infoWindows = new ArrayList<>(); + private MapboxMap.InfoWindowAdapter infoWindowAdapter; private boolean allowConcurrentMultipleInfoWindows; @@ -29,13 +30,11 @@ class InfoWindowManager { private MapboxMap.OnInfoWindowLongClickListener onInfoWindowLongClickListener; private MapboxMap.OnInfoWindowCloseListener onInfoWindowCloseListener; - InfoWindowManager() { - this.infoWindows = new ArrayList<>(); - } - void update() { - for (InfoWindow infoWindow : infoWindows) { - infoWindow.update(); + if (!infoWindows.isEmpty()) { + for (InfoWindow infoWindow : infoWindows) { + infoWindow.update(); + } } } 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 8b1ba7b771..01c6da4971 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 @@ -76,6 +76,9 @@ 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); + } } /** @@ -85,7 +88,6 @@ public final class MapFragment extends Fragment { public void onStart() { super.onStart(); map.onStart(); - map.getMapAsync(onMapReadyCallback); } /** @@ -150,6 +152,10 @@ public final class MapFragment extends Fragment { * @param onMapReadyCallback The callback to be invoked. */ public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { - this.onMapReadyCallback = onMapReadyCallback; + if (map == null) { + this.onMapReadyCallback = onMapReadyCallback; + } else { + map.getMapAsync(onMapReadyCallback); + } } } 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 bff5e9bed2..2394e52193 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 @@ -693,4 +693,4 @@ final class MapGestureDetector { void setOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { this.onScrollListener = onScrollListener; } -}
\ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 9fa3126523..12e4c675ee 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -1,34 +1,34 @@ package com.mapbox.mapboxsdk.maps; import android.content.Context; -import android.graphics.Canvas; import android.graphics.PointF; -import android.graphics.SurfaceTexture; +import android.os.Build; +import android.opengl.GLSurfaceView; import android.os.Bundle; -import android.os.Handler; import android.support.annotation.CallSuper; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; +import android.support.v4.util.LongSparseArray; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.TextureView; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; import com.mapbox.mapboxsdk.R; +import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.egl.EGLConfigChooser; +import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; @@ -40,6 +40,16 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; +import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; +import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY; /** * <p> @@ -57,17 +67,26 @@ import java.util.List; */ public class MapView extends FrameLayout { + private final MapCallback mapCallback = new MapCallback(); + private MapboxMap mapboxMap; + private NativeMapView nativeMapView; - private boolean textureMode; + private MapboxMapOptions mapboxMapOptions; private boolean destroyed; - private boolean hasSurface; - private MapboxMap mapboxMap; - private MapCallback mapCallback; + private MyLocationView myLocationView; + private CompassView compassView; + private PointF focalPoint; + private ImageView attrView; + private ImageView logoView; private MapGestureDetector mapGestureDetector; private MapKeyListener mapKeyListener; private MapZoomButtonController mapZoomButtonController; + private Bundle savedInstanceState; + private final CopyOnWriteArrayList<OnMapChangedListener> onMapChangedListeners = new CopyOnWriteArrayList<>(); + + private GLSurfaceView glSurfaceView; @UiThread public MapView(@NonNull Context context) { @@ -98,24 +117,39 @@ public class MapView extends FrameLayout { // in IDE layout editor, just return return; } - - // determine render surface - textureMode = options.getTextureMode(); + mapboxMapOptions = options; // inflate view View view = LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_internal, this); - CompassView compassView = (CompassView) view.findViewById(R.id.compassView); - MyLocationView myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView); - ImageView attrView = (ImageView) view.findViewById(R.id.attributionView); + compassView = (CompassView) view.findViewById(R.id.compassView); + myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView); + attrView = (ImageView) view.findViewById(R.id.attributionView); + logoView = (ImageView) view.findViewById(R.id.logoView); // add accessibility support setContentDescription(context.getString(R.string.mapbox_mapActionDescription)); + setWillNotDraw(false); + + getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + getViewTreeObserver().removeOnGlobalLayoutListener(this); + } else { + getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + initialiseDrawingSurface(); + } + }); + } - // create native Map object - nativeMapView = new NativeMapView(this); + private void initialiseMap() { + Context context = getContext(); + addOnMapChangedListener(mapCallback); // callback for focal point invalidation - FocalPointInvalidator focalPoint = new FocalPointInvalidator(compassView); + final FocalPointInvalidator focalPointInvalidator = new FocalPointInvalidator(); + focalPointInvalidator.addListener(createFocalPointChangeListener()); // callback for registering touch listeners RegisterTouchListener registerTouchListener = new RegisterTouchListener(); @@ -124,30 +158,45 @@ public class MapView extends FrameLayout { CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator(); // callback for camera change events - CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher(); + final CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher(); // setup components for MapboxMap creation Projection proj = new Projection(nativeMapView); - UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, view.findViewById(R.id.logoView)); - TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPoint, zoomInvalidator); - MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, focalPoint); + UiSettings uiSettings = new UiSettings(proj, focalPointInvalidator, compassView, attrView, logoView); + TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPointInvalidator, + zoomInvalidator); + MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, + focalPointInvalidator); + LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>(); MarkerViewManager markerViewManager = new MarkerViewManager((ViewGroup) findViewById(R.id.markerViewContainer)); - AnnotationManager annotations = new AnnotationManager(nativeMapView, this, markerViewManager); - Transform transform = new Transform(nativeMapView, annotations.getMarkerViewManager(), trackingSettings, + IconManager iconManager = new IconManager(nativeMapView); + Annotations annotations = new AnnotationContainer(nativeMapView, annotationsArray); + Markers markers = new MarkerContainer(nativeMapView, this, annotationsArray, iconManager, markerViewManager); + Polygons polygons = new PolygonContainer(nativeMapView, annotationsArray); + Polylines polylines = new PolylineContainer(nativeMapView, annotationsArray); + AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, annotationsArray, + markerViewManager, iconManager, annotations, markers, polygons, polylines); + Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings, cameraChangeDispatcher); + mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj, - registerTouchListener, annotations, cameraChangeDispatcher); + registerTouchListener, annotationManager, cameraChangeDispatcher); + focalPointInvalidator.addListener(mapboxMap.createFocalPointChangeListener()); + + mapCallback.attachMapboxMap(mapboxMap); // user input - mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, annotations, - cameraChangeDispatcher); + mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, + annotationManager, cameraChangeDispatcher); mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings); + mapZoomButtonController = new MapZoomButtonController(new ZoomButtonsController(this)); MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform); - mapZoomButtonController = new MapZoomButtonController(this, uiSettings, zoomListener); + mapZoomButtonController.bind(uiSettings, zoomListener); + compassView.injectCompassAnimationListener(createCompassAnimationListener(cameraChangeDispatcher)); + compassView.setOnClickListener(createCompassClickListener(cameraChangeDispatcher)); // inject widgets with MapboxMap - compassView.setMapboxMap(mapboxMap); myLocationView.setMapboxMap(mapboxMap); attrView.setOnClickListener(new AttributionDialogManager(context, mapboxMap)); @@ -158,14 +207,58 @@ public class MapView extends FrameLayout { setFocusableInTouchMode(true); requestDisallowInterceptTouchEvent(true); - // allow onDraw invocation - setWillNotDraw(false); - // notify Map object about current connectivity state nativeMapView.setReachability(ConnectivityReceiver.instance(context).isConnected(context)); // initialise MapboxMap - mapboxMap.initialise(context, options); + if (savedInstanceState == null) { + mapboxMap.initialise(context, mapboxMapOptions); + } else { + mapboxMap.onRestoreInstanceState(savedInstanceState); + } + } + + private FocalPointChangeListener createFocalPointChangeListener() { + return new FocalPointChangeListener() { + @Override + public void onFocalPointChanged(PointF pointF) { + focalPoint = pointF; + } + }; + } + + private MapboxMap.OnCompassAnimationListener createCompassAnimationListener(final CameraChangeDispatcher + cameraChangeDispatcher) { + return new MapboxMap.OnCompassAnimationListener() { + @Override + public void onCompassAnimation() { + cameraChangeDispatcher.onCameraMove(); + } + + @Override + public void onCompassAnimationFinished() { + compassView.isAnimating(false); + cameraChangeDispatcher.onCameraIdle(); + } + }; + } + + private OnClickListener createCompassClickListener(final CameraChangeDispatcher cameraChangeDispatcher) { + return new OnClickListener() { + @Override + public void onClick(View v) { + if (mapboxMap != null && compassView != null) { + if (focalPoint != null) { + mapboxMap.setFocalBearing(0, focalPoint.x, focalPoint.y, TIME_MAP_NORTH_ANIMATION); + } else { + mapboxMap.setFocalBearing(0, mapboxMap.getWidth() / 2, mapboxMap.getHeight() / 2, TIME_MAP_NORTH_ANIMATION); + } + cameraChangeDispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION); + compassView.isAnimating(true); + compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION); + } + } + }; } // @@ -188,25 +281,40 @@ public class MapView extends FrameLayout { if (savedInstanceState == null) { MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapLoadEvent()); } else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { - mapboxMap.onRestoreInstanceState(savedInstanceState); + this.savedInstanceState = savedInstanceState; } - - initialiseDrawingSurface(textureMode); - addOnMapChangedListener(mapCallback = new MapCallback(mapboxMap)); } - private void initialiseDrawingSurface(boolean textureMode) { - nativeMapView.initializeDisplay(); - nativeMapView.initializeContext(); - if (textureMode) { - TextureView textureView = new TextureView(getContext()); - textureView.setSurfaceTextureListener(new SurfaceTextureListener()); - addView(textureView, 0); - } else { - SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView); - surfaceView.getHolder().addCallback(new SurfaceCallback()); - surfaceView.setVisibility(View.VISIBLE); - } + private void initialiseDrawingSurface() { + glSurfaceView = (GLSurfaceView) findViewById(R.id.surfaceView); + glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop()); + glSurfaceView.setEGLContextClientVersion(2); + glSurfaceView.setEGLConfigChooser(new EGLConfigChooser()); + + MapRenderer mapRenderer = new MapRenderer(getContext(), glSurfaceView) { + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + MapView.this.post(new Runnable() { + @Override + public void run() { + // Initialise only once + if (mapboxMap == null) { + initialiseMap(); + mapboxMap.onStart(); + } + } + }); + + super.onSurfaceCreated(gl, config); + } + }; + + glSurfaceView.setRenderer(mapRenderer); + glSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY); + glSurfaceView.setVisibility(View.VISIBLE); + + nativeMapView = new NativeMapView(this, mapRenderer); + nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight()); } /** @@ -226,8 +334,10 @@ public class MapView extends FrameLayout { */ @UiThread public void onStart() { - mapboxMap.onStart(); ConnectivityReceiver.instance(getContext()).activate(); + if (mapboxMap != null) { + mapboxMap.onStart(); + } } /** @@ -235,7 +345,9 @@ public class MapView extends FrameLayout { */ @UiThread public void onResume() { - // replaced by onStart in v5.0.0 + if (glSurfaceView != null) { + glSurfaceView.onResume(); + } } /** @@ -243,7 +355,9 @@ public class MapView extends FrameLayout { */ @UiThread public void onPause() { - // replaced by onStop in v5.0.0 + if (glSurfaceView != null) { + glSurfaceView.onPause(); + } } /** @@ -261,9 +375,6 @@ public class MapView extends FrameLayout { @UiThread public void onDestroy() { destroyed = true; - nativeMapView.terminateContext(); - nativeMapView.terminateDisplay(); - nativeMapView.destroySurface(); mapCallback.clearOnMapReadyCallbacks(); nativeMapView.destroy(); nativeMapView = null; @@ -328,21 +439,6 @@ public class MapView extends FrameLayout { nativeMapView.onLowMemory(); } - // Called when debug mode is enabled to update a FPS counter - // Called via JNI from NativeMapView - // Forward to any listener - protected void onFpsChanged(final double fps) { - final MapboxMap.OnFpsChangedListener listener = mapboxMap.getOnFpsChangedListener(); - if (listener != null) { - post(new Runnable() { - @Override - public void run() { - listener.onFpsChanged(fps); - } - }); - } - } - /** * <p> * Loads a new map style from the specified URL. @@ -375,7 +471,10 @@ public class MapView extends FrameLayout { if (destroyed) { return; } - + if (nativeMapView == null) { + mapboxMapOptions.styleUrl(url); + return; + } nativeMapView.setStyleUrl(url); } @@ -383,119 +482,17 @@ public class MapView extends FrameLayout { // Rendering // - // Called when the map needs to be rerendered - // Called via JNI from NativeMapView - protected void onInvalidate() { - postInvalidate(); - } - - @Override - public void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (isInEditMode()) { - return; - } - - if (destroyed) { - return; - } - - if (!hasSurface) { - return; - } - - nativeMapView.render(); - } - @Override protected void onSizeChanged(int width, int height, int oldw, int oldh) { if (destroyed) { return; } - if (!isInEditMode()) { + if (!isInEditMode() && nativeMapView != null) { nativeMapView.resizeView(width, height); } } - private class SurfaceCallback implements SurfaceHolder.Callback { - - private Surface surface; - - @Override - public void surfaceCreated(SurfaceHolder holder) { - nativeMapView.createSurface(surface = holder.getSurface()); - hasSurface = true; - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - if (destroyed) { - return; - } - nativeMapView.resizeFramebuffer(width, height); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - hasSurface = false; - - if (nativeMapView != null) { - nativeMapView.destroySurface(); - } - surface.release(); - } - } - - // This class handles TextureView callbacks - private class SurfaceTextureListener implements TextureView.SurfaceTextureListener { - - private Surface surface; - - // Called when the native surface texture has been created - // Must do all EGL/GL ES initialization here - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - nativeMapView.createSurface(this.surface = new Surface(surface)); - nativeMapView.resizeFramebuffer(width, height); - hasSurface = true; - } - - // Called when the native surface texture has been destroyed - // Must do all EGL/GL ES destruction here - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - hasSurface = false; - - if (nativeMapView != null) { - nativeMapView.destroySurface(); - } - this.surface.release(); - return true; - } - - // Called when the format or size of the native surface texture has been changed - // Must handle window resizing here. - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - if (destroyed) { - return; - } - - nativeMapView.resizeFramebuffer(width, height); - } - - // Called when the SurfaceTexure frame is drawn to screen - // Must sync with UI here - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - if (destroyed) { - return; - } - mapboxMap.onUpdateRegionChange(); - } - } - // // View events // @@ -505,26 +502,37 @@ public class MapView extends FrameLayout { @CallSuper protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (mapZoomButtonController != null) { - mapZoomButtonController.setVisible(false); - } + mapZoomButtonController.setVisible(false); } // Called when view is hidden and shown @Override protected void onVisibilityChanged(@NonNull View changedView, int visibility) { - if (isInEditMode() || mapZoomButtonController == null) { + if (isInEditMode()) { return; } - mapZoomButtonController.setVisible(visibility == View.VISIBLE); + + if (mapZoomButtonController != null) { + mapZoomButtonController.setVisible(visibility == View.VISIBLE); + } } // // Map events // + void onMapChange(int rawChange) { + for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { + try { + onMapChangedListener.onMapChanged(rawChange); + } catch (RuntimeException err) { + Timber.e(err, "Exception in MapView.OnMapChangedListener"); + } + } + } + /** - * <p>P + * <p> * Add a callback that's invoked when the displayed map view changes. * </p> * To remove the callback, use {@link MapView#removeOnMapChangedListener(OnMapChangedListener)}. @@ -534,7 +542,7 @@ public class MapView extends FrameLayout { */ public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) { if (listener != null) { - nativeMapView.addOnMapChangedListener(listener); + onMapChangedListeners.add(listener); } } @@ -546,7 +554,7 @@ public class MapView extends FrameLayout { */ public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) { if (listener != null) { - nativeMapView.removeOnMapChangedListener(listener); + onMapChangedListeners.remove(listener); } } @@ -813,10 +821,10 @@ public class MapView extends FrameLayout { private class FocalPointInvalidator implements FocalPointChangeListener { - private final FocalPointChangeListener[] focalPointChangeListeners; + private final List<FocalPointChangeListener> focalPointChangeListeners = new ArrayList<>(); - FocalPointInvalidator(FocalPointChangeListener... listeners) { - focalPointChangeListeners = listeners; + void addListener(FocalPointChangeListener focalPointChangeListener) { + focalPointChangeListeners.add(focalPointChangeListener); } @Override @@ -910,11 +918,11 @@ public class MapView extends FrameLayout { private static class MapCallback implements OnMapChangedListener { - private final MapboxMap mapboxMap; + private MapboxMap mapboxMap; private final List<OnMapReadyCallback> onMapReadyCallbackList = new ArrayList<>(); private boolean initialLoad = true; - MapCallback(MapboxMap mapboxMap) { + void attachMapboxMap(MapboxMap mapboxMap) { this.mapboxMap = mapboxMap; } @@ -922,14 +930,9 @@ public class MapView extends FrameLayout { public void onMapChanged(@MapChange int change) { if (change == DID_FINISH_LOADING_STYLE && initialLoad) { initialLoad = false; - new Handler().post(new Runnable() { - @Override - public void run() { - mapboxMap.onPreMapReady(); - onMapReady(); - mapboxMap.onPostMapReady(); - } - }); + mapboxMap.onPreMapReady(); + onMapReady(); + mapboxMap.onPostMapReady(); } else if (change == DID_FINISH_RENDERING_FRAME || change == DID_FINISH_RENDERING_FRAME_FULLY_RENDERED) { mapboxMap.onUpdateFullyRendered(); } else if (change == REGION_IS_CHANGING || change == REGION_DID_CHANGE || change == DID_FINISH_LOADING_MAP) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java index 16513904c5..018c8eb5bb 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapZoomButtonController.java @@ -1,7 +1,6 @@ package com.mapbox.mapboxsdk.maps; import android.support.annotation.NonNull; -import android.view.View; import android.widget.ZoomButtonsController; import com.mapbox.mapboxsdk.constants.MapboxConstants; @@ -12,21 +11,25 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants; * Allows single touch only devices to zoom in and out. * </p> */ -final class MapZoomButtonController extends ZoomButtonsController { +final class MapZoomButtonController { private UiSettings uiSettings; + private ZoomButtonsController zoomButtonsController; - MapZoomButtonController(@NonNull View ownerView, @NonNull UiSettings uiSettings, @NonNull OnZoomListener listener) { - super(ownerView); + MapZoomButtonController(@NonNull ZoomButtonsController zoomButtonsController) { + this.zoomButtonsController = zoomButtonsController; + this.zoomButtonsController.setZoomSpeed(MapboxConstants.ANIMATION_DURATION); + } + + void bind(UiSettings uiSettings, ZoomButtonsController.OnZoomListener onZoomListener) { this.uiSettings = uiSettings; - setZoomSpeed(MapboxConstants.ANIMATION_DURATION); - setOnZoomListener(listener); + zoomButtonsController.setOnZoomListener(onZoomListener); } - @Override - public void setVisible(boolean visible) { - if (uiSettings.isZoomControlsEnabled()) { - super.setVisible(visible); + void setVisible(boolean visible) { + if (uiSettings != null && !uiSettings.isZoomControlsEnabled()) { + return; } + zoomButtonsController.setVisible(visible); } } 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 e074d938fd..0c820d844c 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 @@ -59,6 +59,7 @@ import timber.log.Timber; * Note: Similar to a View object, a MapboxMap should only be read and modified from the main thread. * </p> */ +@UiThread public final class MapboxMap { private final NativeMapView nativeMapView; @@ -74,6 +75,7 @@ public final class MapboxMap { private final OnRegisterTouchListener onRegisterTouchListener; private MapboxMap.OnFpsChangedListener onFpsChangedListener; + private PointF focalPoint; MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking, MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener, @@ -99,6 +101,7 @@ public final class MapboxMap { setDebugActive(options.getDebugActive()); setApiBaseUrl(options); setStyleUrl(options); + setPrefetchesTiles(options); } /** @@ -209,7 +212,6 @@ public final class MapboxMap { * * @return Duration in milliseconds */ - @UiThread public long getTransitionDuration() { return nativeMapView.getTransitionDuration(); } @@ -219,7 +221,6 @@ public final class MapboxMap { * * @param durationMs Duration in milliseconds */ - @UiThread public void setTransitionDuration(long durationMs) { nativeMapView.setTransitionDuration(durationMs); } @@ -232,7 +233,6 @@ public final class MapboxMap { * * @return Delay in milliseconds */ - @UiThread public long getTransitionDelay() { return nativeMapView.getTransitionDelay(); } @@ -242,17 +242,44 @@ public final class MapboxMap { * * @param delayMs Delay in milliseconds */ - @UiThread public void setTransitionDelay(long delayMs) { nativeMapView.setTransitionDelay(delayMs); } /** + * Sets tile pre-fetching from MapboxOptions. + * + * @param options the options object + */ + private void setPrefetchesTiles(@NonNull MapboxMapOptions options) { + setPrefetchesTiles(options.getPrefetchesTiles()); + } + + /** + * Enable or disable tile pre-fetching. Pre-fetching makes sure that a low-resolution + * tile is rendered as soon as possible at the expense of a little bandwidth. + * + * @param enable true to enable + */ + public void setPrefetchesTiles(boolean enable) { + nativeMapView.setPrefetchesTiles(enable); + } + + /** + * Check whether tile pre-fetching is enabled or not. + * + * @return true if enabled + * @see MapboxMap#setPrefetchesTiles(boolean) + */ + public boolean getPrefetchesTiles() { + return nativeMapView.getPrefetchesTiles(); + } + + /** * Retrieve all the layers in the style * * @return all the layers in the current style */ - @UiThread public List<Layer> getLayers() { return nativeMapView.getLayers(); } @@ -264,7 +291,6 @@ public final class MapboxMap { * @return the layer, if present in the style */ @Nullable - @UiThread public Layer getLayer(@NonNull String layerId) { return nativeMapView.getLayer(layerId); } @@ -277,13 +303,12 @@ public final class MapboxMap { * @return the casted Layer, null if another type */ @Nullable - @UiThread public <T extends Layer> T getLayerAs(@NonNull String layerId) { try { // noinspection unchecked return (T) nativeMapView.getLayer(layerId); } catch (ClassCastException exception) { - Timber.e(String.format("Layer: %s is a different type: %s", layerId, exception)); + Timber.e(exception, "Layer: %s is a different type: ", layerId); return null; } } @@ -293,7 +318,6 @@ public final class MapboxMap { * * @param layer the layer to add */ - @UiThread public void addLayer(@NonNull Layer layer) { nativeMapView.addLayer(layer); } @@ -304,7 +328,6 @@ public final class MapboxMap { * @param layer the layer to add * @param below the layer id to add this layer before */ - @UiThread public void addLayerBelow(@NonNull Layer layer, @NonNull String below) { nativeMapView.addLayerBelow(layer, below); } @@ -315,7 +338,6 @@ public final class MapboxMap { * @param layer the layer to add * @param above the layer id to add this layer above */ - @UiThread public void addLayerAbove(@NonNull Layer layer, @NonNull String above) { nativeMapView.addLayerAbove(layer, above); } @@ -327,7 +349,6 @@ public final class MapboxMap { * @param layer the layer to add * @param index the index to insert the layer at */ - @UiThread public void addLayerAt(@NonNull Layer layer, @IntRange(from = 0) int index) { nativeMapView.addLayerAt(layer, index); } @@ -338,7 +359,6 @@ public final class MapboxMap { * @param layerId the layer to remove * @return the removed layer or null if not found */ - @UiThread @Nullable public Layer removeLayer(@NonNull String layerId) { return nativeMapView.removeLayer(layerId); @@ -350,7 +370,6 @@ public final class MapboxMap { * @param layer the layer to remove * @return the layer */ - @UiThread @Nullable public Layer removeLayer(@NonNull Layer layer) { return nativeMapView.removeLayer(layer); @@ -362,7 +381,6 @@ public final class MapboxMap { * @param index the layer index * @return the removed layer or null if not found */ - @UiThread @Nullable public Layer removeLayerAt(@IntRange(from = 0) int index) { return nativeMapView.removeLayerAt(index); @@ -373,7 +391,6 @@ public final class MapboxMap { * * @return all the sources in the current style */ - @UiThread public List<Source> getSources() { return nativeMapView.getSources(); } @@ -385,7 +402,6 @@ public final class MapboxMap { * @return the source if present in the current style */ @Nullable - @UiThread public Source getSource(@NonNull String sourceId) { return nativeMapView.getSource(sourceId); } @@ -398,13 +414,12 @@ public final class MapboxMap { * @return the casted Source, null if another type */ @Nullable - @UiThread public <T extends Source> T getSourceAs(@NonNull String sourceId) { try { // noinspection unchecked return (T) nativeMapView.getSource(sourceId); } catch (ClassCastException exception) { - Timber.e(String.format("Source: %s is a different type: %s", sourceId, exception)); + Timber.e(exception, "Source: %s is a different type: ", sourceId); return null; } } @@ -414,7 +429,6 @@ public final class MapboxMap { * * @param source the source to add */ - @UiThread public void addSource(@NonNull Source source) { nativeMapView.addSource(source); } @@ -425,7 +439,6 @@ public final class MapboxMap { * @param sourceId the source to remove * @return the source handle or null if the source was not present */ - @UiThread @Nullable public Source removeSource(@NonNull String sourceId) { return nativeMapView.removeSource(sourceId); @@ -437,7 +450,6 @@ public final class MapboxMap { * @param source the source to remove * @return the source */ - @UiThread @Nullable public Source removeSource(@NonNull Source source) { return nativeMapView.removeSource(source); @@ -449,7 +461,6 @@ public final class MapboxMap { * @param name the name of the image * @param image the pre-multiplied Bitmap */ - @UiThread public void addImage(@NonNull String name, @NonNull Bitmap image) { nativeMapView.addImage(name, image); } @@ -459,11 +470,14 @@ public final class MapboxMap { * * @param name the name of the image to remove */ - @UiThread public void removeImage(String name) { nativeMapView.removeImage(name); } + public Bitmap getImage(@NonNull String name) { + return nativeMapView.getImage(name); + } + // // MinZoom // @@ -475,7 +489,6 @@ public final class MapboxMap { * * @param minZoom The new minimum zoom level. */ - @UiThread public void setMinZoomPreference( @FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) { transform.setMinZoom(minZoom); @@ -483,12 +496,11 @@ public final class MapboxMap { /** * <p> - * Gets the maximum zoom level the map can be displayed at. + * Gets the minimum zoom level the map can be displayed at. * </p> * * @return The minimum zoom level. */ - @UiThread public double getMinZoomLevel() { return transform.getMinZoom(); } @@ -501,10 +513,12 @@ public final class MapboxMap { * <p> * Sets the maximum zoom level the map can be displayed at. * </p> + * <p> + * The default maximum zoomn level is 22. The upper bound for this value is 25.5. + * </p> * * @param maxZoom The new maximum zoom level. */ - @UiThread public void setMaxZoomPreference(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) { transform.setMaxZoom(maxZoom); @@ -517,7 +531,6 @@ public final class MapboxMap { * * @return The maximum zoom level. */ - @UiThread public double getMaxZoomLevel() { return transform.getMaxZoom(); } @@ -543,7 +556,10 @@ public final class MapboxMap { * Gets the tracking interface settings for the map. * * @return the TrackingSettings asssociated with this map + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ + @Deprecated public TrackingSettings getTrackingSettings() { return trackingSettings; } @@ -556,7 +572,10 @@ public final class MapboxMap { * Gets the settings of the user location for the map. * * @return the MyLocationViewSettings associated with this map + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ + @Deprecated public MyLocationViewSettings getMyLocationViewSettings() { return myLocationViewSettings; } @@ -594,6 +613,47 @@ public final class MapboxMap { // /** + * Moves the center of the screen to a latitude and longitude specified by a LatLng object. This centers the + * camera on the LatLng object. + * + * @param latLng Target location to change to + */ + public void setLatLng(@NonNull LatLng latLng) { + nativeMapView.setLatLng(latLng); + } + + /** + * Moves the camera viewpoint to a particular zoom level. + * + * @param zoom Zoom level to change to + */ + public void setZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double zoom) { + if (focalPoint == null) { + focalPoint = new PointF(nativeMapView.getWidth() / 2, nativeMapView.getHeight() / 2); + } + nativeMapView.setZoom(zoom, focalPoint, 0); + } + + /** + * Moves the camera viewpoint angle to a particular angle in degrees. + * + * @param tilt Tilt angle to change to + */ + public void setTilt(@FloatRange(from = MapboxConstants.MINIMUM_TILT, to = MapboxConstants.MAXIMUM_TILT) double tilt) { + nativeMapView.setPitch(tilt, 0); + } + + /** + * Moves the camera viewpoint direction to a particular angle in degrees. + * + * @param bearing Direction angle to change to + */ + public void setBearing(@FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, to = MapboxConstants.MAXIMUM_DIRECTION) + double bearing) { + nativeMapView.setBearing(bearing); + } + + /** * Cancels ongoing animations. * <p> * This invokes the {@link CancelableCallback} for ongoing camera updates. @@ -632,7 +692,6 @@ public final class MapboxMap { * * @param update The change that should be applied to the camera. */ - @UiThread public final void moveCamera(CameraUpdate update) { moveCamera(update, null); } @@ -645,7 +704,6 @@ public final class MapboxMap { * @param update The change that should be applied to the camera * @param callback the callback to be invoked when an animation finishes or is canceled */ - @UiThread public final void moveCamera(final CameraUpdate update, final MapboxMap.CancelableCallback callback) { new Handler().post(new Runnable() { @Override @@ -666,7 +724,6 @@ public final class MapboxMap { * @param update The change that should be applied to the camera. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void easeCamera(CameraUpdate update) { easeCamera(update, MapboxConstants.ANIMATION_DURATION); } @@ -681,7 +738,6 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void easeCamera(CameraUpdate update, int durationMs) { easeCamera(update, durationMs, null); } @@ -705,7 +761,6 @@ public final class MapboxMap { * Do not update or ease the camera from within onCancel(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) { easeCamera(update, durationMs, true, callback); } @@ -724,7 +779,6 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @param easingInterpolator True for easing interpolator, false for linear. */ - @UiThread public final void easeCamera(CameraUpdate update, int durationMs, boolean easingInterpolator) { easeCamera(update, durationMs, easingInterpolator, null); } @@ -750,7 +804,6 @@ public final class MapboxMap { * by a later camera movement or a user gesture, onCancel() will be called. * Do not update or ease the camera from within onCancel(). */ - @UiThread public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, final MapboxMap.CancelableCallback callback) { easeCamera(update, durationMs, easingInterpolator, callback, false); @@ -778,7 +831,6 @@ public final class MapboxMap { * Do not update or ease the camera from within onCancel(). * @param isDismissable true will allow animated camera changes dismiss a tracking mode. */ - @UiThread public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, final MapboxMap.CancelableCallback callback, final boolean isDismissable) { new Handler().post(new Runnable() { @@ -798,7 +850,6 @@ public final class MapboxMap { * @param update The change that should be applied to the camera. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void animateCamera(CameraUpdate update) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, null); } @@ -815,7 +866,6 @@ public final class MapboxMap { * called. Do not update or animate the camera from within onCancel(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void animateCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, callback); } @@ -831,7 +881,6 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void animateCamera(CameraUpdate update, int durationMs) { animateCamera(update, durationMs, null); } @@ -854,7 +903,6 @@ public final class MapboxMap { * isn't required, leave it as null. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - @UiThread public final void animateCamera(final CameraUpdate update, final int durationMs, final MapboxMap.CancelableCallback callback) { new Handler().post(new Runnable() { @@ -925,7 +973,6 @@ public final class MapboxMap { * * @return If true, map debug information is currently shown. */ - @UiThread public boolean isDebugActive() { return nativeMapView.getDebug(); } @@ -938,7 +985,6 @@ public final class MapboxMap { * * @param debugActive If true, map debug information is shown. */ - @UiThread public void setDebugActive(boolean debugActive) { nativeMapView.setDebug(debugActive); } @@ -952,7 +998,6 @@ public final class MapboxMap { * * @see #isDebugActive() */ - @UiThread public void cycleDebugOptions() { nativeMapView.cycleDebugOptions(); } @@ -1002,7 +1047,6 @@ public final class MapboxMap { * @param url The URL of the map style * @see Style */ - @UiThread public void setStyleUrl(@NonNull String url) { setStyleUrl(url, null); } @@ -1035,7 +1079,6 @@ public final class MapboxMap { * @param callback The callback that is invoked when the style has loaded. * @see Style */ - @UiThread public void setStyleUrl(@NonNull final String url, @Nullable final OnStyleLoadedListener callback) { if (callback != null) { nativeMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { @@ -1067,7 +1110,6 @@ public final class MapboxMap { * @param style The bundled style. * @see Style */ - @UiThread public void setStyle(@Style.StyleUrl String style) { setStyleUrl(style); } @@ -1084,7 +1126,6 @@ public final class MapboxMap { * @param callback The callback to be invoked when the style has finished loading * @see Style */ - @UiThread public void setStyle(@Style.StyleUrl String style, @Nullable OnStyleLoadedListener callback) { setStyleUrl(style, callback); } @@ -1102,16 +1143,36 @@ public final class MapboxMap { } /** - * Returns the map style currently displayed in the map view. + * Returns the map style url currently displayed in the map view. * * @return The URL of the map style */ - @UiThread @Nullable public String getStyleUrl() { return nativeMapView.getStyleUrl(); } + /** + * Loads a new map style from a json string. + * <p> + * If the style fails to load or an invalid style URL is set, the map view will become blank. + * An error message will be logged in the Android logcat and {@link MapView#DID_FAIL_LOADING_MAP} event will be + * sent. + * </p> + */ + public void setStyleJson(@NonNull String styleJson) { + nativeMapView.setStyleJson(styleJson); + } + + /** + * Returns the map style json currently displayed in the map view. + * + * @return The json of the map style + */ + public String getStyleJson() { + return nativeMapView.getStyleJson(); + } + // // Annotations // @@ -1126,7 +1187,6 @@ public final class MapboxMap { * @param markerOptions A marker options object that defines how to render the marker * @return The {@code Marker} that was added to the map */ - @UiThread @NonNull public Marker addMarker(@NonNull MarkerOptions markerOptions) { return annotationManager.addMarker(markerOptions, this); @@ -1142,7 +1202,6 @@ public final class MapboxMap { * @param markerOptions A marker options object that defines how to render the marker * @return The {@code Marker} that was added to the map */ - @UiThread @NonNull public Marker addMarker(@NonNull BaseMarkerOptions markerOptions) { return annotationManager.addMarker(markerOptions, this); @@ -1157,9 +1216,11 @@ public final class MapboxMap { * * @param markerOptions A marker options object that defines how to render the marker * @return The {@code Marker} that was added to the map + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ - @UiThread @NonNull + @Deprecated public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions) { return annotationManager.addMarker(markerOptions, this, null); } @@ -1174,8 +1235,10 @@ public final class MapboxMap { * @param markerOptions A marker options object that defines how to render the marker * @param onMarkerViewAddedListener Callback invoked when the View has been added to the map * @return The {@code Marker} that was added to the map + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ - @UiThread + @Deprecated @NonNull public MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, final MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) { @@ -1191,9 +1254,11 @@ public final class MapboxMap { * * @param markerViewOptions A list of markerView options objects that defines how to render the markers * @return A list of the {@code MarkerView}s that were added to the map + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ - @UiThread @NonNull + @Deprecated public List<MarkerView> addMarkerViews(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions) { return annotationManager.addMarkerViews(markerViewOptions, this); @@ -1204,9 +1269,11 @@ public final class MapboxMap { * * @param rect the rectangular area on the map to query for markerViews * @return A list of the markerViews that were found in the rectangle + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ - @UiThread @NonNull + @Deprecated public List<MarkerView> getMarkerViewsInRect(@NonNull RectF rect) { return annotationManager.getMarkerViewsInRect(rect); } @@ -1221,7 +1288,6 @@ public final class MapboxMap { * @param markerOptionsList A list of marker options objects that defines how to render the markers * @return A list of the {@code Marker}s that were added to the map */ - @UiThread @NonNull public List<Marker> addMarkers(@NonNull List<? extends BaseMarkerOptions> markerOptionsList) { @@ -1235,9 +1301,8 @@ public final class MapboxMap { * * @param updatedMarker An updated marker object */ - @UiThread public void updateMarker(@NonNull Marker updatedMarker) { - annotationManager.updateMarker(updatedMarker); + annotationManager.updateMarker(updatedMarker, this); } /** @@ -1246,7 +1311,6 @@ public final class MapboxMap { * @param polylineOptions A polyline options object that defines how to render the polyline * @return The {@code Polyine} that was added to the map */ - @UiThread @NonNull public Polyline addPolyline(@NonNull PolylineOptions polylineOptions) { return annotationManager.addPolyline(polylineOptions, this); @@ -1258,7 +1322,6 @@ public final class MapboxMap { * @param polylineOptionsList A list of polyline options objects that defines how to render the polylines. * @return A list of the {@code Polyline}s that were added to the map. */ - @UiThread @NonNull public List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList) { return annotationManager.addPolylines(polylineOptionsList, this); @@ -1269,7 +1332,6 @@ public final class MapboxMap { * * @param polyline An updated polyline object. */ - @UiThread public void updatePolyline(Polyline polyline) { annotationManager.updatePolyline(polyline); } @@ -1280,7 +1342,6 @@ public final class MapboxMap { * @param polygonOptions A polygon options object that defines how to render the polygon. * @return The {@code Polygon} that was added to the map. */ - @UiThread @NonNull public Polygon addPolygon(@NonNull PolygonOptions polygonOptions) { return annotationManager.addPolygon(polygonOptions, this); @@ -1292,7 +1353,6 @@ public final class MapboxMap { * @param polygonOptionsList A list of polygon options objects that defines how to render the polygons * @return A list of the {@code Polygon}s that were added to the map */ - @UiThread @NonNull public List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList) { return annotationManager.addPolygons(polygonOptionsList, this); @@ -1303,7 +1363,6 @@ public final class MapboxMap { * * @param polygon An updated polygon object */ - @UiThread public void updatePolygon(Polygon polygon) { annotationManager.updatePolygon(polygon); } @@ -1316,7 +1375,6 @@ public final class MapboxMap { * * @param marker Marker to remove */ - @UiThread public void removeMarker(@NonNull Marker marker) { annotationManager.removeAnnotation(marker); } @@ -1329,7 +1387,6 @@ public final class MapboxMap { * * @param polyline Polyline to remove */ - @UiThread public void removePolyline(@NonNull Polyline polyline) { annotationManager.removeAnnotation(polyline); } @@ -1342,7 +1399,6 @@ public final class MapboxMap { * * @param polygon Polygon to remove */ - @UiThread public void removePolygon(@NonNull Polygon polygon) { annotationManager.removeAnnotation(polygon); } @@ -1352,7 +1408,6 @@ public final class MapboxMap { * * @param annotation The annotation object to remove. */ - @UiThread public void removeAnnotation(@NonNull Annotation annotation) { annotationManager.removeAnnotation(annotation); } @@ -1362,7 +1417,6 @@ public final class MapboxMap { * * @param id The identifier associated to the annotation to be removed */ - @UiThread public void removeAnnotation(long id) { annotationManager.removeAnnotation(id); } @@ -1372,7 +1426,6 @@ public final class MapboxMap { * * @param annotationList A list of annotation objects to remove. */ - @UiThread public void removeAnnotations(@NonNull List<? extends Annotation> annotationList) { annotationManager.removeAnnotations(annotationList); } @@ -1380,7 +1433,6 @@ public final class MapboxMap { /** * Removes all annotations from the map. */ - @UiThread public void removeAnnotations() { annotationManager.removeAnnotations(); } @@ -1388,7 +1440,6 @@ public final class MapboxMap { /** * Removes all markers, polylines, polygons, overlays, etc from the map. */ - @UiThread public void clear() { annotationManager.removeAnnotations(); } @@ -1454,7 +1505,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user clicks on a marker. * To unset the callback, use null. */ - @UiThread public void setOnMarkerClickListener(@Nullable OnMarkerClickListener listener) { annotationManager.setOnMarkerClickListener(listener); } @@ -1465,7 +1515,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user clicks on a polygon. * To unset the callback, use null. */ - @UiThread public void setOnPolygonClickListener(@Nullable OnPolygonClickListener listener) { annotationManager.setOnPolygonClickListener(listener); } @@ -1476,7 +1525,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user clicks on a polyline. * To unset the callback, use null. */ - @UiThread public void setOnPolylineClickListener(@Nullable OnPolylineClickListener listener) { annotationManager.setOnPolylineClickListener(listener); } @@ -1491,7 +1539,6 @@ public final class MapboxMap { * * @param marker The marker to select. */ - @UiThread public void selectMarker(@NonNull Marker marker) { if (marker == null) { Timber.w("marker was null, so just returning"); @@ -1503,7 +1550,6 @@ public final class MapboxMap { /** * Deselects any currently selected marker. All markers will have it's info window closed. */ - @UiThread public void deselectMarkers() { annotationManager.deselectMarkers(); } @@ -1513,7 +1559,6 @@ public final class MapboxMap { * * @param marker the marker to deselect */ - @UiThread public void deselectMarker(@NonNull Marker marker) { annotationManager.deselectMarker(marker); } @@ -1523,7 +1568,6 @@ public final class MapboxMap { * * @return The currently selected marker. */ - @UiThread public List<Marker> getSelectedMarkers() { return annotationManager.getSelectedMarkers(); } @@ -1551,7 +1595,6 @@ public final class MapboxMap { * @param infoWindowAdapter The callback to be invoked when an info window will be shown. * To unset the callback, use null. */ - @UiThread public void setInfoWindowAdapter(@Nullable InfoWindowAdapter infoWindowAdapter) { annotationManager.getInfoWindowManager().setInfoWindowAdapter(infoWindowAdapter); } @@ -1561,7 +1604,6 @@ public final class MapboxMap { * * @return The callback to be invoked when an info window will be shown. */ - @UiThread @Nullable public InfoWindowAdapter getInfoWindowAdapter() { return annotationManager.getInfoWindowManager().getInfoWindowAdapter(); @@ -1572,7 +1614,6 @@ public final class MapboxMap { * * @param allow If true, map allows concurrent multiple infowindows to be shown. */ - @UiThread public void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) { annotationManager.getInfoWindowManager().setAllowConcurrentMultipleOpenInfoWindows(allow); } @@ -1582,7 +1623,6 @@ public final class MapboxMap { * * @return If true, map allows concurrent multiple infowindows to be shown. */ - @UiThread public boolean isAllowConcurrentMultipleOpenInfoWindows() { return annotationManager.getInfoWindowManager().isAllowConcurrentMultipleOpenInfoWindows(); } @@ -1676,7 +1716,6 @@ public final class MapboxMap { * @param listener The callback that's invoked on every camera change position. * To unset the callback, use null. */ - @UiThread @Deprecated public void setOnCameraChangeListener(@Nullable OnCameraChangeListener listener) { transform.setOnCameraChangeListener(listener); @@ -1687,7 +1726,6 @@ public final class MapboxMap { * * @param listener the listener to notify */ - @UiThread public void setOnCameraIdleListener(@Nullable OnCameraIdleListener listener) { cameraChangeDispatcher.setOnCameraIdleListener(listener); } @@ -1697,7 +1735,6 @@ public final class MapboxMap { * * @param listener the listener to notify */ - @UiThread public void setOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) { cameraChangeDispatcher.setOnCameraMoveCanceledListener(listener); } @@ -1707,7 +1744,6 @@ public final class MapboxMap { * * @param listener the listener to notify */ - @UiThread public void setOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) { cameraChangeDispatcher.setOnCameraMoveStartedListener(listener); } @@ -1717,7 +1753,6 @@ public final class MapboxMap { * * @param listener the listener to notify */ - @UiThread public void setOnCameraMoveListener(@Nullable OnCameraMoveListener listener) { cameraChangeDispatcher.setOnCameraMoveListener(listener); } @@ -1728,9 +1763,9 @@ public final class MapboxMap { * @param listener The callback that's invoked on every frame rendered to the map view. * To unset the callback, use null. */ - @UiThread public void setOnFpsChangedListener(@Nullable OnFpsChangedListener listener) { onFpsChangedListener = listener; + nativeMapView.setOnFpsChangedListener(listener); } // used by MapView @@ -1744,7 +1779,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. */ - @UiThread public void setOnScrollListener(@Nullable OnScrollListener listener) { onRegisterTouchListener.onRegisterScrollListener(listener); } @@ -1755,7 +1789,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the map is flinged. * To unset the callback, use null. */ - @UiThread public void setOnFlingListener(@Nullable OnFlingListener listener) { onRegisterTouchListener.onRegisterFlingListener(listener); } @@ -1766,7 +1799,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user clicks on the map view. * To unset the callback, use null. */ - @UiThread public void setOnMapClickListener(@Nullable OnMapClickListener listener) { onRegisterTouchListener.onRegisterMapClickListener(listener); } @@ -1777,7 +1809,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user long clicks on the map view. * To unset the callback, use null. */ - @UiThread public void setOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { onRegisterTouchListener.onRegisterMapLongClickListener(listener); } @@ -1788,7 +1819,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when the user clicks on an info window. * To unset the callback, use null. */ - @UiThread public void setOnInfoWindowClickListener(@Nullable OnInfoWindowClickListener listener) { annotationManager.getInfoWindowManager().setOnInfoWindowClickListener(listener); } @@ -1798,7 +1828,6 @@ public final class MapboxMap { * * @return Current active InfoWindow Click Listener */ - @UiThread public OnInfoWindowClickListener getOnInfoWindowClickListener() { return annotationManager.getInfoWindowManager().getOnInfoWindowClickListener(); } @@ -1809,7 +1838,6 @@ public final class MapboxMap { * @param listener The callback that's invoked when a marker's info window is long pressed. To unset the callback, * use null. */ - @UiThread public void setOnInfoWindowLongClickListener(@Nullable OnInfoWindowLongClickListener listener) { annotationManager.getInfoWindowManager().setOnInfoWindowLongClickListener(listener); @@ -1838,7 +1866,6 @@ public final class MapboxMap { * * @return Current active InfoWindow Close Listener */ - @UiThread public OnInfoWindowCloseListener getOnInfoWindowCloseListener() { return annotationManager.getInfoWindowManager().getOnInfoWindowCloseListener(); } @@ -1851,8 +1878,10 @@ public final class MapboxMap { * Returns the status of the my-location layer. * * @return True if the my-location layer is enabled, false otherwise. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public boolean isMyLocationEnabled() { return trackingSettings.isMyLocationEnabled(); } @@ -1867,8 +1896,10 @@ public final class MapboxMap { * android.Manifest.permission#ACCESS_COARSE_LOCATION or android.Manifest.permission#ACCESS_FINE_LOCATION. * * @param enabled True to enable; false to disable. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public void setMyLocationEnabled(boolean enabled) { trackingSettings.setMyLocationEnabled(enabled); } @@ -1877,9 +1908,11 @@ public final class MapboxMap { * Returns the currently displayed user location, or null if there is no location data available. * * @return The currently displayed user location. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread @Nullable + @Deprecated public Location getMyLocation() { return trackingSettings.getMyLocation(); } @@ -1890,8 +1923,10 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the user clicks on a marker. * To unset the callback, use null. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener listener) { trackingSettings.setOnMyLocationChangeListener(listener); @@ -1901,8 +1936,10 @@ public final class MapboxMap { * Replaces the location source of the my-location layer. * * @param locationSource A {@link LocationEngine} location source to use in the my-location layer. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public void setLocationSource(@Nullable LocationEngine locationSource) { trackingSettings.setLocationSource(locationSource); } @@ -1912,8 +1949,10 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the location tracking mode changes. * To unset the callback, use null. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public void setOnMyLocationTrackingModeChangeListener( @Nullable MapboxMap.OnMyLocationTrackingModeChangeListener listener) { trackingSettings.setOnMyLocationTrackingModeChangeListener(listener); @@ -1924,8 +1963,10 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the bearing tracking mode changes. * To unset the callback, use null. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ - @UiThread + @Deprecated public void setOnMyBearingTrackingModeChangeListener(@Nullable OnMyBearingTrackingModeChangeListener listener) { trackingSettings.setOnMyBearingTrackingModeChangeListener(listener); } @@ -1939,7 +1980,6 @@ public final class MapboxMap { * * @param callback Callback method invoked when the snapshot is taken. */ - @UiThread public void snapshot(@NonNull SnapshotReadyCallback callback) { nativeMapView.addSnapshotCallback(callback); } @@ -1951,7 +1991,6 @@ public final class MapboxMap { * @param layerIds optionally - only query these layers * @return the list of feature */ - @UiThread @NonNull public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable String... layerIds) { @@ -1966,7 +2005,6 @@ public final class MapboxMap { * @param layerIds optionally - only query these layers * @return the list of feature */ - @UiThread @NonNull public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable Filter.Statement filter, @@ -1981,7 +2019,6 @@ public final class MapboxMap { * @param layerIds optionally - only query these layers * @return the list of feature */ - @UiThread @NonNull public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates, @Nullable String... layerIds) { @@ -1996,7 +2033,6 @@ public final class MapboxMap { * @param layerIds optionally - only query these layers * @return the list of feature */ - @UiThread @NonNull public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates, @Nullable Filter.Statement filter, @@ -2004,6 +2040,15 @@ public final class MapboxMap { return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter); } + FocalPointChangeListener createFocalPointChangeListener() { + return new FocalPointChangeListener() { + @Override + public void onFocalPointChanged(PointF pointF) { + focalPoint = pointF; + } + }; + } + // // Interfaces // @@ -2102,6 +2147,21 @@ public final class MapboxMap { } /** + * Interface definition for a callback to be invoked for when the compass is animating. + */ + public interface OnCompassAnimationListener { + /** + * Called repeatedly as the compass continues to move after clicking on it. + */ + void onCompassAnimation(); + + /** + * Called when compass animation has ended. + */ + void onCompassAnimationFinished(); + } + + /** * Interface definition for a callback to be invoked when a frame is rendered to the map view. * * @see MapboxMap#setOnFpsChangedListener(OnFpsChangedListener) @@ -2266,7 +2326,10 @@ public final class MapboxMap { * Interface definition for a callback to be invoked when an MarkerView will be shown. * * @param <U> the instance type of MarkerView + * @deprecated Use a {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer} instead. An example of converting Android + * SDK views to be used as a symbol see https://github.com/mapbox/mapbox-gl-native/blob/68f32bc104422207c64da8d90e8411b138d87f04/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java */ + @Deprecated public abstract static class MarkerViewAdapter<U extends MarkerView> { private Context context; 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 80b25bf0de..7b979f5563 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 @@ -79,12 +79,12 @@ public class MapboxMapOptions implements Parcelable { private int[] myLocationBackgroundPadding; private int myLocationAccuracyTintColor; private int myLocationAccuracyAlpha; + private float myLocationAccuracyThreshold; + private boolean prefetchesTiles = true; + private boolean zMediaOverlay = false; private String apiBaseUrl; - @Deprecated - private boolean textureMode; - private String style; /** @@ -148,10 +148,13 @@ public class MapboxMapOptions implements Parcelable { myLocationBackgroundPadding = in.createIntArray(); myLocationAccuracyAlpha = in.readInt(); myLocationAccuracyTintColor = in.readInt(); + myLocationAccuracyThreshold = in.readFloat(); style = in.readString(); apiBaseUrl = in.readString(); - textureMode = in.readByte() != 0; + + prefetchesTiles = in.readByte() != 0; + zMediaOverlay = in.readByte() != 0; } static Bitmap getBitmapFromDrawable(Drawable drawable) { @@ -291,8 +294,12 @@ public class MapboxMapOptions implements Parcelable { mapboxMapOptions.myLocationAccuracyTint( typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyTintColor, ColorUtils.getPrimaryColor(context))); - mapboxMapOptions.textureMode( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false)); + mapboxMapOptions.myLocationAccuracyThreshold( + typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyThreshold, 0)); + mapboxMapOptions.setPrefetchesTiles( + typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true)); + mapboxMapOptions.renderSurfaceOnTop( + typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableZMediaOverlay, false)); } finally { typedArray.recycle(); } @@ -681,22 +688,59 @@ public class MapboxMapOptions implements Parcelable { } /** - * Enable TextureView as rendered surface. - * <p> - * Since the 4.2.0 release we replaced our TextureView with an SurfaceView implemenation. - * Enabling this option will use the deprecated TextureView instead. - * </p> + * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value. + * + * @param myLocationAccuracyThreshold Value of accuracy (in meters), below which circle won't be displayed + * @return This + */ + public MapboxMapOptions myLocationAccuracyThreshold(float myLocationAccuracyThreshold) { + this.myLocationAccuracyThreshold = myLocationAccuracyThreshold; + return this; + } + + /** + * Enable tile pre-fetching. Loads tiles at a lower zoom-level to pre-render + * a low resolution preview while more detailed tiles are loaded. + * Enabled by default + * + * @param enable true to enable * - * @param textureMode True to enable texture mode * @return This - * @deprecated As of the 4.2.0 release, using TextureView is deprecated. */ - public MapboxMapOptions textureMode(boolean textureMode) { - this.textureMode = textureMode; + public MapboxMapOptions setPrefetchesTiles(boolean enable) { + this.prefetchesTiles = enable; return this; } /** + * Check whether tile pre-fetching is enabled. + * + * @return true if enabled + */ + public boolean getPrefetchesTiles() { + return prefetchesTiles; + } + + + /** + * Set the flag to render the map surface on top of another surface. + * + * @param renderOnTop true if this map is shown on top of another one, false if bottom. + */ + public void renderSurfaceOnTop(boolean renderOnTop) { + this.zMediaOverlay = renderOnTop; + } + + /** + * Get the flag to render the map surface on top of another surface. + * + * @return true if this map is + */ + public boolean getRenderSurfaceOnTop() { + return zMediaOverlay; + } + + /** * Get the current configured API endpoint base URL. * * @return Base URL to be used API endpoint. @@ -988,22 +1032,21 @@ public class MapboxMapOptions implements Parcelable { } /** - * Get the current configured debug state for a map view. + * Returns current accuracy threshold value (in meters). * - * @return True indicates debug is enabled. + * @return Value of accuracy threshold (in meters), below which circle won't be displayed */ - public boolean getDebugActive() { - return debugActive; + public float getMyLocationAccuracyThreshold() { + return myLocationAccuracyThreshold; } /** - * Returns true if TextureView is being used a render view. + * Get the current configured debug state for a map view. * - * @return True if TextureView is used. - * @deprecated As of the 4.2.0 release, using TextureView is deprecated. + * @return True indicates debug is enabled. */ - public boolean getTextureMode() { - return textureMode; + public boolean getDebugActive() { + return debugActive; } public static final Parcelable.Creator<MapboxMapOptions> CREATOR = new Parcelable.Creator<MapboxMapOptions>() { @@ -1065,10 +1108,13 @@ public class MapboxMapOptions implements Parcelable { dest.writeIntArray(myLocationBackgroundPadding); dest.writeInt(myLocationAccuracyAlpha); dest.writeInt(myLocationAccuracyTintColor); + dest.writeFloat(myLocationAccuracyThreshold); dest.writeString(style); dest.writeString(apiBaseUrl); - dest.writeByte((byte) (textureMode ? 1 : 0)); + + dest.writeByte((byte) (prefetchesTiles ? 1 : 0)); + dest.writeByte((byte) (zMediaOverlay ? 1 : 0)); } @Override @@ -1153,6 +1199,9 @@ public class MapboxMapOptions implements Parcelable { if (myLocationAccuracyAlpha != options.myLocationAccuracyAlpha) { return false; } + if (myLocationAccuracyThreshold != options.myLocationAccuracyThreshold) { + return false; + } if (cameraPosition != null ? !cameraPosition.equals(options.cameraPosition) : options.cameraPosition != null) { return false; } @@ -1189,6 +1238,13 @@ public class MapboxMapOptions implements Parcelable { if (apiBaseUrl != null ? !apiBaseUrl.equals(options.apiBaseUrl) : options.apiBaseUrl != null) { return false; } + if (prefetchesTiles != options.prefetchesTiles) { + return false; + } + if (zMediaOverlay != options.zMediaOverlay) { + return false; + } + return false; } @@ -1230,9 +1286,12 @@ public class MapboxMapOptions implements Parcelable { result = 31 * result + Arrays.hashCode(myLocationBackgroundPadding); result = 31 * result + myLocationAccuracyTintColor; result = 31 * result + myLocationAccuracyAlpha; + result = 31 * result + (myLocationAccuracyThreshold != +0.0f + ? Float.floatToIntBits(myLocationAccuracyThreshold) : 0); result = 31 * result + (apiBaseUrl != null ? apiBaseUrl.hashCode() : 0); - result = 31 * result + (textureMode ? 1 : 0); result = 31 * result + (style != null ? style.hashCode() : 0); + result = 31 * result + (prefetchesTiles ? 1 : 0); + result = 31 * result + (zMediaOverlay ? 1 : 0); return result; } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java new file mode 100644 index 0000000000..072382ce07 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java @@ -0,0 +1,244 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.graphics.RectF; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.util.LongSparseArray; + +import com.mapbox.mapboxsdk.annotations.Annotation; +import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; +import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; +import com.mapbox.mapboxsdk.annotations.Icon; +import com.mapbox.mapboxsdk.annotations.IconFactory; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerView; +import com.mapbox.mapboxsdk.annotations.MarkerViewManager; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates {@link Marker}'s functionality. + */ +class MarkerContainer implements Markers { + + private final NativeMapView nativeMapView; + private final MapView mapView; + private final LongSparseArray<Annotation> annotations; + private final IconManager iconManager; + private final MarkerViewManager markerViewManager; + + MarkerContainer(NativeMapView nativeMapView, MapView mapView, LongSparseArray<Annotation> annotations, IconManager + iconManager, MarkerViewManager markerViewManager) { + this.nativeMapView = nativeMapView; + this.mapView = mapView; + this.annotations = annotations; + this.iconManager = iconManager; + this.markerViewManager = markerViewManager; + } + + @Override + public Marker addBy(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap) { + Marker marker = prepareMarker(markerOptions); + long id = nativeMapView != null ? nativeMapView.addMarker(marker) : 0; + marker.setMapboxMap(mapboxMap); + marker.setId(id); + annotations.put(id, marker); + return marker; + } + + @Override + public List<Marker> addBy(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap + mapboxMap) { + int count = markerOptionsList.size(); + List<Marker> markers = new ArrayList<>(count); + if (nativeMapView != null && count > 0) { + BaseMarkerOptions markerOptions; + Marker marker; + for (int i = 0; i < count; i++) { + markerOptions = markerOptionsList.get(i); + marker = prepareMarker(markerOptions); + markers.add(marker); + } + + if (markers.size() > 0) { + long[] ids = nativeMapView.addMarkers(markers); + for (int i = 0; i < ids.length; i++) { + Marker createdMarker = markers.get(i); + createdMarker.setMapboxMap(mapboxMap); + createdMarker.setId(ids[i]); + annotations.put(ids[i], createdMarker); + } + } + } + return markers; + } + + @Override + public void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) { + ensureIconLoaded(updatedMarker, mapboxMap); + nativeMapView.updateMarker(updatedMarker); + annotations.setValueAt(annotations.indexOfKey(updatedMarker.getId()), updatedMarker); + } + + @Override + public List<Marker> obtainAll() { + List<Marker> markers = new ArrayList<>(); + Annotation annotation; + for (int i = 0; i < annotations.size(); i++) { + annotation = annotations.get(annotations.keyAt(i)); + if (annotation instanceof Marker) { + markers.add((Marker) annotation); + } + } + return markers; + } + + @NonNull + @Override + public List<Marker> obtainAllIn(@NonNull RectF rectangle) { + // convert Rectangle to be density depedent + float pixelRatio = nativeMapView.getPixelRatio(); + RectF rect = new RectF(rectangle.left / pixelRatio, + rectangle.top / pixelRatio, + rectangle.right / pixelRatio, + rectangle.bottom / pixelRatio); + + long[] ids = nativeMapView.queryPointAnnotations(rect); + + List<Long> idsList = new ArrayList<>(ids.length); + for (long id : ids) { + idsList.add(id); + } + + List<Marker> annotations = new ArrayList<>(ids.length); + List<Annotation> annotationList = obtainAnnotations(); + int count = annotationList.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = annotationList.get(i); + if (annotation instanceof com.mapbox.mapboxsdk.annotations.Marker && idsList.contains(annotation.getId())) { + annotations.add((com.mapbox.mapboxsdk.annotations.Marker) annotation); + } + } + + return new ArrayList<>(annotations); + } + + @Override + public MarkerView addViewBy(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap, @Nullable + MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) { + final MarkerView marker = prepareViewMarker(markerOptions); + + // add marker to map + marker.setMapboxMap(mapboxMap); + long id = nativeMapView.addMarker(marker); + marker.setId(id); + annotations.put(id, marker); + + if (onMarkerViewAddedListener != null) { + markerViewManager.addOnMarkerViewAddedListener(marker, onMarkerViewAddedListener); + } + markerViewManager.setEnabled(true); + markerViewManager.setWaitingForRenderInvoke(true); + return marker; + } + + @Override + public List<MarkerView> addViewsBy(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions, @NonNull + MapboxMap mapboxMap) { + List<MarkerView> markers = new ArrayList<>(); + for (BaseMarkerViewOptions markerViewOption : markerViewOptions) { + // if last marker + if (markerViewOptions.indexOf(markerViewOption) == markerViewOptions.size() - 1) { + // get notified when render occurs to invalidate and draw MarkerViews + markerViewManager.setWaitingForRenderInvoke(true); + } + // add marker to map + MarkerView marker = prepareViewMarker(markerViewOption); + marker.setMapboxMap(mapboxMap); + long id = nativeMapView.addMarker(marker); + marker.setId(id); + annotations.put(id, marker); + markers.add(marker); + } + markerViewManager.setEnabled(true); + markerViewManager.update(); + return markers; + } + + @Override + public List<MarkerView> obtainViewsIn(@NonNull RectF rectangle) { + float pixelRatio = nativeMapView.getPixelRatio(); + RectF rect = new RectF(rectangle.left / pixelRatio, + rectangle.top / pixelRatio, + rectangle.right / pixelRatio, + rectangle.bottom / pixelRatio); + + long[] ids = nativeMapView.queryPointAnnotations(rect); + + List<Long> idsList = new ArrayList<>(ids.length); + for (long id : ids) { + idsList.add(id); + } + + List<MarkerView> annotations = new ArrayList<>(ids.length); + List<Annotation> annotationList = obtainAnnotations(); + int count = annotationList.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = annotationList.get(i); + if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) { + annotations.add((MarkerView) annotation); + } + } + + return new ArrayList<>(annotations); + } + + @Override + public void reload() { + iconManager.reloadIcons(); + int count = annotations.size(); + for (int i = 0; i < count; i++) { + Annotation annotation = annotations.get(i); + if (annotation instanceof Marker) { + Marker marker = (Marker) annotation; + nativeMapView.removeAnnotation(annotation.getId()); + long newId = nativeMapView.addMarker(marker); + marker.setId(newId); + } + } + } + + private Marker prepareMarker(BaseMarkerOptions markerOptions) { + Marker marker = markerOptions.getMarker(); + Icon icon = iconManager.loadIconForMarker(marker); + marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon)); + return marker; + } + + private void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) { + if (!(marker instanceof MarkerView)) { + iconManager.ensureIconLoaded(marker, mapboxMap); + } + } + + private List<Annotation> obtainAnnotations() { + List<Annotation> annotations = new ArrayList<>(); + for (int i = 0; i < this.annotations.size(); i++) { + annotations.add(this.annotations.get(this.annotations.keyAt(i))); + } + return annotations; + } + + private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) { + MarkerView marker = markerViewOptions.getMarker(); + Icon icon = markerViewOptions.getIcon(); + if (icon == null) { + icon = IconFactory.getInstance(mapView.getContext()).defaultMarkerView(); + } + iconManager.loadIconForMarkerView(marker); + marker.setIcon(icon); + return marker; + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java new file mode 100644 index 0000000000..d646e0ac49 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java @@ -0,0 +1,39 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.graphics.RectF; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; +import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerView; +import com.mapbox.mapboxsdk.annotations.MarkerViewManager; + +import java.util.List; + +/** + * Interface that defines convenient methods for working with a {@link Marker}'s collection. + */ +interface Markers { + Marker addBy(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap); + + List<Marker> addBy(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap mapboxMap); + + void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap); + + List<Marker> obtainAll(); + + List<Marker> obtainAllIn(@NonNull RectF rectangle); + + MarkerView addViewBy(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap, + @Nullable MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener); + + List<MarkerView> addViewsBy(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions, + @NonNull MapboxMap mapboxMap); + + List<MarkerView> obtainViewsIn(@NonNull RectF rectangle); + + void reload(); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index af3b57151d..3ce6aab581 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -1,27 +1,25 @@ package com.mapbox.mapboxsdk.maps; -import android.app.ActivityManager; import android.content.Context; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.RectF; -import android.os.Build; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.DisplayMetrics; -import android.view.Surface; +import com.mapbox.mapboxsdk.LibraryLoader; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.Polygon; import com.mapbox.mapboxsdk.annotations.Polyline; import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.geometry.ProjectedMeters; +import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.mapboxsdk.style.layers.CannotAddLayerException; import com.mapbox.mapboxsdk.style.layers.Filter; @@ -36,7 +34,6 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import timber.log.Timber; @@ -55,53 +52,32 @@ final class NativeMapView { //Hold a reference to prevent it from being GC'd as long as it's used on the native side private final FileSource fileSource; + // Used to schedule work on the MapRenderer Thread + private MapRenderer mapRenderer; + // Device density private final float pixelRatio; - // Listeners for Map change events - private CopyOnWriteArrayList<MapView.OnMapChangedListener> onMapChangedListeners; - // Listener invoked to return a bitmap of the map private MapboxMap.SnapshotReadyCallback snapshotReadyCallback; - // - // Static methods - // - static { - System.loadLibrary("mapbox-gl"); + LibraryLoader.load(); } // // Constructors // - public NativeMapView(MapView mapView) { + public NativeMapView(final MapView mapView, MapRenderer mapRenderer) { + this.mapRenderer = mapRenderer; + this.mapView = mapView; + Context context = mapView.getContext(); fileSource = FileSource.getInstance(context); - pixelRatio = context.getResources().getDisplayMetrics().density; - int availableProcessors = Runtime.getRuntime().availableProcessors(); - ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); - ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - activityManager.getMemoryInfo(memoryInfo); - long totalMemory = memoryInfo.availMem; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - totalMemory = memoryInfo.totalMem; - } - - if (availableProcessors < 0) { - throw new IllegalArgumentException("availableProcessors cannot be negative."); - } - if (totalMemory < 0) { - throw new IllegalArgumentException("totalMemory cannot be negative."); - } - onMapChangedListeners = new CopyOnWriteArrayList<>(); - this.mapView = mapView; - - String programCacheDir = context.getCacheDir().getAbsolutePath(); - nativeInitialize(this, fileSource, pixelRatio, programCacheDir, availableProcessors, totalMemory); + nativeInitialize(this, fileSource, mapRenderer, pixelRatio); } // @@ -110,9 +86,10 @@ final class NativeMapView { private boolean isDestroyedOn(String callingMethod) { if (destroyed && !TextUtils.isEmpty(callingMethod)) { - Timber.e(String.format(MapboxConstants.MAPBOX_LOCALE, + Timber.e( "You're calling `%s` after the `MapView` was destroyed, were you invoking it after `onDestroy()`?", - callingMethod)); + callingMethod + ); } return destroyed; } @@ -123,60 +100,12 @@ final class NativeMapView { destroyed = true; } - public void initializeDisplay() { - if (isDestroyedOn("initializeDisplay")) { - return; - } - nativeInitializeDisplay(); - } - - public void terminateDisplay() { - if (isDestroyedOn("terminateDisplay")) { - return; - } - nativeTerminateDisplay(); - } - - public void initializeContext() { - if (isDestroyedOn("initializeContext")) { - return; - } - nativeInitializeContext(); - } - - public void terminateContext() { - if (isDestroyedOn("terminateContext")) { - return; - } - nativeTerminateContext(); - } - - public void createSurface(Surface surface) { - if (isDestroyedOn("createSurface")) { - return; - } - nativeCreateSurface(surface); - } - - public void destroySurface() { - if (isDestroyedOn("destroySurface")) { - return; - } - nativeDestroySurface(); - } - public void update() { if (isDestroyedOn("update")) { return; } - nativeUpdate(); - } - public void render() { - if (isDestroyedOn("render")) { - return; - } - nativeRender(); + mapRenderer.requestRender(); } public void resizeView(int width, int height) { @@ -197,41 +126,18 @@ final class NativeMapView { if (width > 65535) { // we have seen edge cases where devices return incorrect values #6111 Timber.e("Device returned an out of range width size, " - + "capping value at 65535 instead of " + width); + + "capping value at 65535 instead of %s", width); width = 65535; } if (height > 65535) { // we have seen edge cases where devices return incorrect values #6111 Timber.e("Device returned an out of range height size, " - + "capping value at 65535 instead of " + height); + + "capping value at 65535 instead of %s", height); height = 65535; } - nativeResizeView(width, height); - } - - public void resizeFramebuffer(int fbWidth, int fbHeight) { - if (isDestroyedOn("resizeFramebuffer")) { - return; - } - if (fbWidth < 0) { - throw new IllegalArgumentException("fbWidth cannot be negative."); - } - - if (fbHeight < 0) { - throw new IllegalArgumentException("fbHeight cannot be negative."); - } - - if (fbWidth > 65535) { - throw new IllegalArgumentException( - "fbWidth cannot be greater than 65535."); - } - if (fbHeight > 65535) { - throw new IllegalArgumentException( - "fbHeight cannot be greater than 65535."); - } - nativeResizeFramebuffer(fbWidth, fbHeight); + nativeResizeView(width, height); } public void setStyleUrl(String url) { @@ -561,6 +467,13 @@ final class NativeMapView { nativeAddAnnotationIcon(symbol, width, height, scale, pixels); } + public void removeAnnotationIcon(String symbol) { + if (isDestroyedOn("removeAnnotationIcon")) { + return; + } + nativeRemoveAnnotationIcon(symbol); + } + public void setVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration) { if (isDestroyedOn("setVisibleCoordinateBounds")) { return; @@ -596,13 +509,6 @@ final class NativeMapView { return nativeGetDebug(); } - public void setEnableFps(boolean enable) { - if (isDestroyedOn("setEnableFps")) { - return; - } - nativeSetEnableFps(enable); - } - public boolean isFullyLoaded() { if (isDestroyedOn("isFullyLoaded")) { return false; @@ -621,7 +527,7 @@ final class NativeMapView { if (isDestroyedOn("getMetersPerPixelAtLatitude")) { return 0; } - return nativeGetMetersPerPixelAtLatitude(lat, getZoom()); + return nativeGetMetersPerPixelAtLatitude(lat, getZoom()) / pixelRatio; } public ProjectedMeters projectedMetersForLatLng(LatLng latLng) { @@ -692,6 +598,20 @@ final class NativeMapView { return nativeGetCameraPosition(); } + public void setPrefetchesTiles(boolean enable) { + if (isDestroyedOn("setPrefetchesTiles")) { + return; + } + nativeSetPrefetchesTiles(enable); + } + + public boolean getPrefetchesTiles() { + if (isDestroyedOn("getPrefetchesTiles")) { + return false; + } + return nativeGetPrefetchesTiles(); + } + // Runtime style Api public long getTransitionDuration() { @@ -841,6 +761,13 @@ final class NativeMapView { nativeRemoveImage(name); } + public Bitmap getImage(String name) { + if (isDestroyedOn("getImage")) { + return null; + } + return nativeGetImage(name); + } + // Feature querying @NonNull @@ -872,13 +799,6 @@ final class NativeMapView { return features != null ? Arrays.asList(features) : new ArrayList<Feature>(); } - public void scheduleTakeSnapshot() { - if (isDestroyedOn("scheduleTakeSnapshot")) { - return; - } - nativeTakeSnapshot(); - } - public void setApiBaseUrl(String baseUrl) { if (isDestroyedOn("setApiBaseUrl")) { return; @@ -901,29 +821,10 @@ final class NativeMapView { // Callbacks // - protected void onInvalidate() { - if (mapView != null) { - mapView.onInvalidate(); - } - } - protected void onMapChanged(int rawChange) { - if (onMapChangedListeners != null) { - for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { - try { - onMapChangedListener.onMapChanged(rawChange); - } catch (RuntimeException err) { - Timber.e("Exception (%s) in MapView.OnMapChangedListener: %s", err.getClass(), err.getMessage()); - } - } - } - } - - protected void onFpsChanged(double fps) { - if (isDestroyedOn("OnFpsChanged")) { - return; + if (mapView != null) { + mapView.onMapChange(rawChange); } - mapView.onFpsChanged(fps); } protected void onSnapshotReady(Bitmap mapContent) { @@ -943,33 +844,13 @@ final class NativeMapView { private native void nativeInitialize(NativeMapView nativeMapView, FileSource fileSource, - float pixelRatio, - String programCacheDir, - int availableProcessors, - long totalMemory); + MapRenderer mapRenderer, + float pixelRatio); private native void nativeDestroy(); - private native void nativeInitializeDisplay(); - - private native void nativeTerminateDisplay(); - - private native void nativeInitializeContext(); - - private native void nativeTerminateContext(); - - private native void nativeCreateSurface(Object surface); - - private native void nativeDestroySurface(); - - private native void nativeUpdate(); - - private native void nativeRender(); - private native void nativeResizeView(int width, int height); - private native void nativeResizeFramebuffer(int fbWidth, int fbHeight); - private native void nativeSetStyleUrl(String url); private native String nativeGetStyleUrl(); @@ -1038,6 +919,8 @@ final class NativeMapView { private native void nativeAddAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels); + private native void nativeRemoveAnnotationIcon(String symbol); + private native void nativeSetVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration); @@ -1049,8 +932,6 @@ final class NativeMapView { private native boolean nativeGetDebug(); - private native void nativeSetEnableFps(boolean enable); - private native boolean nativeIsFullyLoaded(); private native void nativeSetReachability(boolean status); @@ -1117,6 +998,8 @@ final class NativeMapView { private native void nativeRemoveImage(String name); + private native Bitmap nativeGetImage(String name); + private native void nativeUpdatePolygon(long polygonId, Polygon polygon); private native void nativeUpdatePolyline(long polylineId, Polyline polyline); @@ -1134,6 +1017,10 @@ final class NativeMapView { private native Light nativeGetLight(); + private native void nativeSetPrefetchesTiles(boolean enable); + + private native boolean nativeGetPrefetchesTiles(); + int getWidth() { if (isDestroyedOn("")) { return 0; @@ -1153,11 +1040,11 @@ final class NativeMapView { // void addOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) { - onMapChangedListeners.add(listener); + mapView.addOnMapChangedListener(listener); } void removeOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) { - onMapChangedListeners.remove(listener); + mapView.removeOnMapChangedListener(listener); } // @@ -1165,8 +1052,35 @@ final class NativeMapView { // void addSnapshotCallback(@NonNull MapboxMap.SnapshotReadyCallback callback) { + if (isDestroyedOn("addSnapshotCallback")) { + return; + } snapshotReadyCallback = callback; - scheduleTakeSnapshot(); - render(); + nativeTakeSnapshot(); + } + + public void setOnFpsChangedListener(final MapboxMap.OnFpsChangedListener listener) { + mapRenderer.queueEvent(new Runnable() { + + @Override + public void run() { + mapRenderer.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() { + + @Override + public void onFpsChanged(final double fps) { + mapView.post(new Runnable() { + + @Override + public void run() { + listener.onFpsChanged(fps); + } + + }); + } + + }); + } + + }); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java new file mode 100644 index 0000000000..016862bddc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java @@ -0,0 +1,82 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; +import android.support.v4.util.LongSparseArray; + +import com.mapbox.mapboxsdk.annotations.Annotation; +import com.mapbox.mapboxsdk.annotations.Polygon; +import com.mapbox.mapboxsdk.annotations.PolygonOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates {@link Polygon}'s functionality. + */ +class PolygonContainer implements Polygons { + + private final NativeMapView nativeMapView; + private final LongSparseArray<Annotation> annotations; + + PolygonContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) { + this.nativeMapView = nativeMapView; + this.annotations = annotations; + } + + @Override + public Polygon addBy(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) { + Polygon polygon = polygonOptions.getPolygon(); + if (!polygon.getPoints().isEmpty()) { + long id = nativeMapView != null ? nativeMapView.addPolygon(polygon) : 0; + polygon.setId(id); + polygon.setMapboxMap(mapboxMap); + annotations.put(id, polygon); + } + return polygon; + } + + @Override + public List<Polygon> addBy(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) { + int count = polygonOptionsList.size(); + + Polygon polygon; + List<Polygon> polygons = new ArrayList<>(count); + if (nativeMapView != null && count > 0) { + for (PolygonOptions polygonOptions : polygonOptionsList) { + polygon = polygonOptions.getPolygon(); + if (!polygon.getPoints().isEmpty()) { + polygons.add(polygon); + } + } + + long[] ids = nativeMapView.addPolygons(polygons); + for (int i = 0; i < ids.length; i++) { + polygon = polygons.get(i); + polygon.setMapboxMap(mapboxMap); + polygon.setId(ids[i]); + annotations.put(ids[i], polygon); + } + } + return polygons; + } + + @Override + public void update(Polygon polygon) { + nativeMapView.updatePolygon(polygon); + annotations.setValueAt(annotations.indexOfKey(polygon.getId()), polygon); + } + + @Override + public List<Polygon> obtainAll() { + List<Polygon> polygons = new ArrayList<>(); + Annotation annotation; + for (int i = 0; i < annotations.size(); i++) { + annotation = annotations.get(annotations.keyAt(i)); + if (annotation instanceof Polygon) { + polygons.add((Polygon) annotation); + } + } + return polygons; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java new file mode 100644 index 0000000000..2a0190b5ba --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java @@ -0,0 +1,22 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; + +import com.mapbox.mapboxsdk.annotations.Polygon; +import com.mapbox.mapboxsdk.annotations.PolygonOptions; + +import java.util.List; + +/** + * Interface that defines convenient methods for working with a {@link Polygon}'s collection. + */ +interface Polygons { + Polygon addBy(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap); + + List<Polygon> addBy(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap); + + void update(Polygon polygon); + + List<Polygon> obtainAll(); +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java new file mode 100644 index 0000000000..303b25fb55 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java @@ -0,0 +1,81 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; +import android.support.v4.util.LongSparseArray; + +import com.mapbox.mapboxsdk.annotations.Annotation; +import com.mapbox.mapboxsdk.annotations.Polyline; +import com.mapbox.mapboxsdk.annotations.PolylineOptions; + +import java.util.ArrayList; +import java.util.List; + +/** + * Encapsulates {@link Polyline}'s functionality. + */ +class PolylineContainer implements Polylines { + + private final NativeMapView nativeMapView; + private final LongSparseArray<Annotation> annotations; + + PolylineContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) { + this.nativeMapView = nativeMapView; + this.annotations = annotations; + } + + @Override + public Polyline addBy(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) { + Polyline polyline = polylineOptions.getPolyline(); + if (!polyline.getPoints().isEmpty()) { + long id = nativeMapView != null ? nativeMapView.addPolyline(polyline) : 0; + polyline.setMapboxMap(mapboxMap); + polyline.setId(id); + annotations.put(id, polyline); + } + return polyline; + } + + @Override + public List<Polyline> addBy(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) { + int count = polylineOptionsList.size(); + Polyline polyline; + List<Polyline> polylines = new ArrayList<>(count); + if (nativeMapView != null && count > 0) { + for (PolylineOptions options : polylineOptionsList) { + polyline = options.getPolyline(); + if (!polyline.getPoints().isEmpty()) { + polylines.add(polyline); + } + } + + long[] ids = nativeMapView.addPolylines(polylines); + for (int i = 0; i < ids.length; i++) { + Polyline polylineCreated = polylines.get(i); + polylineCreated.setMapboxMap(mapboxMap); + polylineCreated.setId(ids[i]); + annotations.put(ids[i], polylineCreated); + } + } + return polylines; + } + + @Override + public void update(Polyline polyline) { + nativeMapView.updatePolyline(polyline); + annotations.setValueAt(annotations.indexOfKey(polyline.getId()), polyline); + } + + @Override + public List<Polyline> obtainAll() { + List<Polyline> polylines = new ArrayList<>(); + Annotation annotation; + for (int i = 0; i < annotations.size(); i++) { + annotation = annotations.get(annotations.keyAt(i)); + if (annotation instanceof Polyline) { + polylines.add((Polyline) annotation); + } + } + return polylines; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java new file mode 100644 index 0000000000..c9a865cdd0 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java @@ -0,0 +1,22 @@ +package com.mapbox.mapboxsdk.maps; + + +import android.support.annotation.NonNull; + +import com.mapbox.mapboxsdk.annotations.Polyline; +import com.mapbox.mapboxsdk.annotations.PolylineOptions; + +import java.util.List; + +/** + * Interface that defines convenient methods for working with a {@link Polyline}'s collection. + */ +interface Polylines { + Polyline addBy(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap); + + List<Polyline> addBy(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap); + + void update(Polyline polyline); + + List<Polyline> obtainAll(); +} 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 77fea1a14a..6c90cd95ec 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 @@ -76,6 +76,9 @@ 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); + } } /** @@ -85,7 +88,6 @@ public class SupportMapFragment extends Fragment { public void onStart() { super.onStart(); map.onStart(); - map.getMapAsync(onMapReadyCallback); } /** @@ -150,6 +152,10 @@ public class SupportMapFragment extends Fragment { * @param onMapReadyCallback The callback to be invoked. */ public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { - this.onMapReadyCallback = onMapReadyCallback; + if (map == null) { + this.onMapReadyCallback = onMapReadyCallback; + } else { + map.getMapAsync(onMapReadyCallback); + } } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java index bd0bf7c83b..6881ca067b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java @@ -20,7 +20,11 @@ import timber.log.Timber; /** * Settings for the user location and bearing tracking of a MapboxMap. + * + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ +@Deprecated public final class TrackingSettings { private final MyLocationView myLocationView; 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 d788b7772b..6f63c2eba8 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 @@ -346,7 +346,7 @@ final class Transform implements MapView.OnMapChangedListener { void setMinZoom(double minZoom) { if ((minZoom < MapboxConstants.MINIMUM_ZOOM) || (minZoom > MapboxConstants.MAXIMUM_ZOOM)) { - Timber.e("Not setting minZoomPreference, value is in unsupported range: " + minZoom); + Timber.e("Not setting minZoomPreference, value is in unsupported range: %s", minZoom); return; } mapView.setMinZoom(minZoom); @@ -358,7 +358,7 @@ final class Transform implements MapView.OnMapChangedListener { void setMaxZoom(double maxZoom) { if ((maxZoom < MapboxConstants.MINIMUM_ZOOM) || (maxZoom > MapboxConstants.MAXIMUM_ZOOM)) { - Timber.e("Not setting maxZoomPreference, value is in unsupported range: " + maxZoom); + Timber.e("Not setting maxZoomPreference, value is in unsupported range: %s", maxZoom); return; } mapView.setMaxZoom(maxZoom); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java new file mode 100644 index 0000000000..3f43522e01 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java @@ -0,0 +1,145 @@ +package com.mapbox.mapboxsdk.maps.renderer; + +import android.content.Context; +import android.opengl.GLSurfaceView; + +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.storage.FileSource; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +/** + * The {@link MapRenderer} encapsulates the GL thread. + * <p> + * Performs actions on the GL thread to manage the GL resources and + * render on the one end and acts as a scheduler to request work to + * be performed on the GL thread on the other. + */ +public class MapRenderer implements GLSurfaceView.Renderer, MapRendererScheduler { + + // Holds the pointer to the native peer after initialisation + private long nativePtr = 0; + + private final GLSurfaceView glSurfaceView; + + private MapboxMap.OnFpsChangedListener onFpsChangedListener; + + public MapRenderer(Context context, GLSurfaceView glSurfaceView) { + this.glSurfaceView = glSurfaceView; + + FileSource fileSource = FileSource.getInstance(context); + float pixelRatio = context.getResources().getDisplayMetrics().density; + String programCacheDir = context.getCacheDir().getAbsolutePath(); + + // Initialise native peer + nativeInitialize(this, fileSource, pixelRatio, programCacheDir); + } + + public void setOnFpsChangedListener(MapboxMap.OnFpsChangedListener listener) { + onFpsChangedListener = listener; + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + nativeOnSurfaceCreated(); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + if (width < 0) { + throw new IllegalArgumentException("fbWidth cannot be negative."); + } + + if (height < 0) { + throw new IllegalArgumentException("fbHeight cannot be negative."); + } + + if (width > 65535) { + throw new IllegalArgumentException( + "fbWidth cannot be greater than 65535."); + } + + if (height > 65535) { + throw new IllegalArgumentException( + "fbHeight cannot be greater than 65535."); + } + + gl.glViewport(0, 0, width, height); + nativeOnSurfaceChanged(width, height); + } + + @Override + public void onDrawFrame(GL10 gl) { + nativeRender(); + + if (onFpsChangedListener != null) { + updateFps(); + } + } + + /** + * May be called from any thread. + * <p> + * Called from the renderer frontend to schedule a render. + */ + @Override + public void requestRender() { + glSurfaceView.requestRender(); + } + + /** + * May be called from any thread. + * <p> + * Schedules work to be performed on the MapRenderer thread. + * + * @param runnable the runnable to execute + */ + @Override + public void queueEvent(Runnable runnable) { + glSurfaceView.queueEvent(runnable); + } + + /** + * May be called from any thread. + * <p> + * Called from the native peer to schedule work on the GL + * thread. Explicit override for easier to read jni code. + * + * @param runnable the runnable to execute + * @see MapRendererRunnable + */ + void queueEvent(MapRendererRunnable runnable) { + this.queueEvent((Runnable) runnable); + } + + private native void nativeInitialize(MapRenderer self, + FileSource fileSource, + float pixelRatio, + String programCacheDir); + + @Override + protected native void finalize() throws Throwable; + + private native void nativeOnSurfaceCreated(); + + private native void nativeOnSurfaceChanged(int width, int height); + + private native void nativeRender(); + + private long frames; + private long timeElapsed; + + private void updateFps() { + frames++; + long currentTime = System.nanoTime(); + double fps = 0; + if (currentTime - timeElapsed >= 1) { + fps = frames / ((currentTime - timeElapsed) / 1E9); + onFpsChangedListener.onFpsChanged(fps); + timeElapsed = currentTime; + frames = 0; + } + } + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java new file mode 100644 index 0000000000..28246fe578 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java @@ -0,0 +1,29 @@ +package com.mapbox.mapboxsdk.maps.renderer; + +/** + * Peer class for {@link Runnable}s to be scheduled on the {@link MapRenderer} thread. + * The actual work is performed in the native peer. + */ +class MapRendererRunnable implements Runnable { + + // Holds the pointer to the native peer after initialisation + private final long nativePtr; + + /** + * Constructed from the native peer constructor + * + * @param nativePtr the native peer's memory address + */ + MapRendererRunnable(long nativePtr) { + this.nativePtr = nativePtr; + } + + @Override + public native void run(); + + @Override + protected native void finalize() throws Throwable; + + private native void nativeInitialize(); + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java new file mode 100644 index 0000000000..7ad4f124d8 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java @@ -0,0 +1,13 @@ +package com.mapbox.mapboxsdk.maps.renderer; + +/** + * Can be used to schedule work on the map renderer + * thread or request a render. + */ +public interface MapRendererScheduler { + + void requestRender(); + + void queueEvent(Runnable runnable); + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java index 2b327409ae..45f72af1c5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java @@ -1,10 +1,8 @@ package com.mapbox.mapboxsdk.maps.widgets; import android.content.Context; -import android.graphics.PointF; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPropertyAnimatorCompat; import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; @@ -13,11 +11,8 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; -import com.mapbox.mapboxsdk.maps.FocalPointChangeListener; import com.mapbox.mapboxsdk.maps.MapboxMap; -import java.lang.ref.WeakReference; - /** * UI element overlaid on a map to show the map's bearing when it isn't true north (0.0). Tapping * the compass resets the bearing to true north and hides the compass. @@ -27,16 +22,17 @@ import java.lang.ref.WeakReference; * use {@link com.mapbox.mapboxsdk.maps.UiSettings}. * </p> */ -public final class CompassView extends AppCompatImageView implements Runnable, FocalPointChangeListener { +public final class CompassView extends AppCompatImageView implements Runnable { - private static final long TIME_WAIT_IDLE = 500; + public static final long TIME_WAIT_IDLE = 500; + public static final long TIME_MAP_NORTH_ANIMATION = 150; private static final long TIME_FADE_ANIMATION = TIME_WAIT_IDLE; - private static final long TIME_MAP_NORTH_ANIMATION = 150; private float rotation = 0.0f; private boolean fadeCompassViewFacingNorth = true; private ViewPropertyAnimatorCompat fadeAnimator; - private PointF focalPoint; + private MapboxMap.OnCompassAnimationListener compassAnimationListener; + private boolean isAnimating = false; public CompassView(Context context) { super(context); @@ -62,9 +58,12 @@ public final class CompassView extends AppCompatImageView implements Runnable, F setLayoutParams(lp); } - // TODO refactor MapboxMap and replace with interface - public void setMapboxMap(@NonNull MapboxMap mapboxMap) { - setOnClickListener(new CompassClickListener(mapboxMap, this)); + public void injectCompassAnimationListener(@NonNull MapboxMap.OnCompassAnimationListener compassAnimationListener) { + this.compassAnimationListener = compassAnimationListener; + } + + public void isAnimating(boolean isAnimating) { + this.isAnimating = isAnimating; } private void resetAnimation() { @@ -97,11 +96,6 @@ public final class CompassView extends AppCompatImageView implements Runnable, F } } - @Nullable - PointF getFocalPoint() { - return focalPoint; - } - /** * Updates the direction of the compass. * @@ -126,6 +120,7 @@ public final class CompassView extends AppCompatImageView implements Runnable, F setVisibility(View.VISIBLE); } + notifyCompassAnimationListenerWhenAnimating(); setRotation(rotation); } @@ -157,7 +152,8 @@ public final class CompassView extends AppCompatImageView implements Runnable, F @Override public void run() { - if (isFacingNorth() && fadeCompassViewFacingNorth) { + if (isHidden()) { + compassAnimationListener.onCompassAnimationFinished(); resetAnimation(); setLayerType(View.LAYER_TYPE_HARDWARE, null); fadeAnimator = ViewCompat.animate(CompassView.this).alpha(0.0f).setDuration(TIME_FADE_ANIMATION); @@ -172,34 +168,9 @@ public final class CompassView extends AppCompatImageView implements Runnable, F } } - @Override - public void onFocalPointChanged(PointF pointF) { - focalPoint = pointF; - } - - static class CompassClickListener implements View.OnClickListener { - - private WeakReference<MapboxMap> mapboxMap; - private WeakReference<CompassView> compassView; - - CompassClickListener(final MapboxMap mapboxMap, CompassView compassView) { - this.mapboxMap = new WeakReference<>(mapboxMap); - this.compassView = new WeakReference<>(compassView); - } - - @Override - public void onClick(View view) { - final MapboxMap mapboxMap = this.mapboxMap.get(); - final CompassView compassView = this.compassView.get(); - if (mapboxMap != null && compassView != null) { - PointF focalPoint = compassView.getFocalPoint(); - if (focalPoint != null) { - mapboxMap.setFocalBearing(0, focalPoint.x, focalPoint.y, TIME_MAP_NORTH_ANIMATION); - } else { - mapboxMap.setFocalBearing(0, mapboxMap.getWidth() / 2, mapboxMap.getHeight() / 2, TIME_MAP_NORTH_ANIMATION); - } - compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION); - } + private void notifyCompassAnimationListenerWhenAnimating() { + if (isAnimating) { + compassAnimationListener.onCompassAnimation(); } } } 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 f74286705c..983ba2550f 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 @@ -1,6 +1,7 @@ package com.mapbox.mapboxsdk.maps.widgets; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Camera; import android.graphics.Canvas; @@ -48,7 +49,10 @@ import timber.log.Timber; * <p> * Use {@link MyLocationViewSettings} to manipulate the state of this view. * </p> + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ +@Deprecated public class MyLocationView extends View { private static final int UNDEFINED_TINT_COLOR = -1; @@ -71,6 +75,7 @@ public class MyLocationView extends View { private float accuracy; private Paint accuracyPaint; + private float accuracyThreshold; private ValueAnimator locationChangeAnimator; private ValueAnimator accuracyAnimator; @@ -592,6 +597,16 @@ public class MyLocationView extends View { } /** + * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value. + * For internal use only. + * + * @param accuracyThreshold Value below which circle won't be displayed + */ + public void setAccuracyThreshold(float accuracyThreshold) { + this.accuracyThreshold = accuracyThreshold; + } + + /** * Set the bearing tracking mode, for internal use only. * * @param myBearingTrackingMode The bearing tracking mode @@ -764,6 +779,7 @@ public class MyLocationView extends View { locationSource = new WeakReference<>(locationEngine); } + @SuppressLint("MissingPermission") @Override public void onConnected() { MyLocationView locationView = userLocationView.get(); @@ -952,10 +968,11 @@ public class MyLocationView extends View { accuracyAnimator.end(); } - accuracyAnimator = ValueAnimator.ofFloat(accuracy, location.getAccuracy()); + float newAccuracy = location.getAccuracy() >= accuracyThreshold ? location.getAccuracy() : 0f; + accuracyAnimator = ValueAnimator.ofFloat(accuracy, newAccuracy); accuracyAnimator.setDuration(750); accuracyAnimator.start(); - accuracy = location.getAccuracy(); + accuracy = newAccuracy; } abstract void invalidate(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java index fe2f18e4dd..a1d5b13b8b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java @@ -13,7 +13,10 @@ import com.mapbox.mapboxsdk.maps.Projection; /** * Settings to configure the visual appearance of the MyLocationView. + * @deprecated use location layer plugin from + * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. */ +@Deprecated public class MyLocationViewSettings { private Projection projection; @@ -51,6 +54,7 @@ public class MyLocationViewSettings { // private int accuracyAlpha; + private float accuracyThreshold = 0f; @ColorInt private int accuracyTintColor; @@ -93,6 +97,7 @@ public class MyLocationViewSettings { setBackgroundTintColor(options.getMyLocationBackgroundTintColor()); setAccuracyAlpha(options.getMyLocationAccuracyAlpha()); setAccuracyTintColor(options.getMyLocationAccuracyTintColor()); + setAccuracyThreshold(options.getMyLocationAccuracyThreshold()); } /** @@ -293,6 +298,25 @@ public class MyLocationViewSettings { myLocationView.setAccuracyTint(accuracyTintColor); } + /** + * Returns current accuracy threshold value (in meters). + * + * @return Value of accuracy threshold (in meters), below which circle won't be displayed + */ + public float getAccuracyThreshold() { + return accuracyThreshold; + } + + /** + * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value. + * + * @param accuracyThreshold Value of accuracy (in meters), below which circle won't be displayed + */ + public void setAccuracyThreshold(float accuracyThreshold) { + this.accuracyThreshold = accuracyThreshold; + myLocationView.setAccuracyThreshold(accuracyThreshold); + } + public void setTilt(double tilt) { myLocationView.setTilt(tilt); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java index a1bd98b780..817dcdb438 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk.net; +import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -21,6 +22,7 @@ import timber.log.Timber; * Not public api. */ public class ConnectivityReceiver extends BroadcastReceiver { + @SuppressLint("StaticFieldLeak") private static ConnectivityReceiver INSTANCE; /** @@ -82,7 +84,7 @@ public class ConnectivityReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { boolean connected = isConnected(context); - Timber.v("Connected: " + connected); + Timber.v("Connected: %s", connected); // Loop over listeners for (ConnectivityListener listener : listeners) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java index 76ce1de9d7..ae74859228 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java @@ -1,12 +1,14 @@ package com.mapbox.mapboxsdk.net; +import com.mapbox.mapboxsdk.LibraryLoader; + /** * Updates the native library's connectivity state */ class NativeConnectivityListener implements ConnectivityListener { static { - System.loadLibrary("mapbox-gl"); + LibraryLoader.load(); } private long nativePtr; 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 d572d696db..130284e88d 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 @@ -1,10 +1,12 @@ package com.mapbox.mapboxsdk.offline; +import android.annotation.SuppressLint; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; +import com.mapbox.mapboxsdk.LibraryLoader; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; @@ -25,7 +27,7 @@ public class OfflineManager { // static { - System.loadLibrary("mapbox-gl"); + LibraryLoader.load(); } // Native peer pointer @@ -39,6 +41,7 @@ public class OfflineManager { private Handler handler; // This object is implemented as a singleton + @SuppressLint("StaticFieldLeak") private static OfflineManager instance; // The application context @@ -89,11 +92,11 @@ public class OfflineManager { */ private OfflineManager(Context context) { this.context = context.getApplicationContext(); - this.fileSource = FileSource.getInstance(context); + this.fileSource = FileSource.getInstance(this.context); initialize(fileSource); // Delete any existing previous ambient cache database - deleteAmbientDatabase(context); + deleteAmbientDatabase(this.context); } private void deleteAmbientDatabase(final Context context) { @@ -106,10 +109,10 @@ public class OfflineManager { File file = new File(path); if (file.exists()) { file.delete(); - Timber.d("Old ambient cache database deleted to save space: " + path); + Timber.d("Old ambient cache database deleted to save space: %s", path); } } catch (Exception exception) { - Timber.e("Failed to delete old ambient cache database: ", exception); + Timber.e(exception, "Failed to delete old ambient cache database: "); } } }).start(); @@ -235,10 +238,11 @@ public class OfflineManager { return LatLngBounds.world().contains(definition.getBounds()); } - /* - * Changing or bypassing this limit without permission from Mapbox is prohibited - * by the Mapbox Terms of Service. - */ + /** + * Changing or bypassing this limit without permission from Mapbox is prohibited + * by the Mapbox Terms of Service. + * @param limit the new tile count limit. + */ public native void setOfflineMapboxTileCountLimit(long limit); private native void initialize(FileSource fileSource); 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 ee6f8aa87f..f210729037 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 @@ -6,6 +6,7 @@ import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.mapbox.mapboxsdk.LibraryLoader; import com.mapbox.mapboxsdk.storage.FileSource; import java.lang.annotation.Retention; @@ -23,7 +24,7 @@ public class OfflineRegion { // static { - System.loadLibrary("mapbox-gl"); + LibraryLoader.load(); } // Members 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 new file mode 100644 index 0000000000..72df86d80d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java @@ -0,0 +1,260 @@ +package com.mapbox.mapboxsdk.snapshotter; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.UiThread; + +import com.mapbox.mapboxsdk.R; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.storage.FileSource; + +/** + * The map snapshotter creates a bitmap of the map, rendered + * off the UI thread. The snapshotter itself must be used on + * the UI thread (for access to the main looper) + */ +@UiThread +public class MapSnapshotter { + + /** + * Can be used to get notified of errors + * in snapshot generation + * + * @see MapSnapshotter#start(MapboxMap.SnapshotReadyCallback, ErrorHandler) + */ + public interface ErrorHandler { + + /** + * Called on error. Snapshotting will not + * continue + * + * @param error the error message + */ + void onError(String error); + } + + private static final int LOGO_MARGIN_PX = 4; + + // Holds the pointer to JNI NativeMapView + private long nativePtr = 0; + + private final Context context; + private MapboxMap.SnapshotReadyCallback callback; + private ErrorHandler errorHandler; + + /** + * MapSnapshotter options + */ + public static class Options { + private int pixelRatio = 1; + private int width; + private int height; + private String styleUrl = Style.MAPBOX_STREETS; + private LatLngBounds region; + private CameraPosition cameraPosition; + + /** + * @param width the width of the image + * @param height the height of the image + */ + public Options(int width, int height) { + this.width = width; + this.height = height; + } + + /** + * @param url The style URL to use + * @return the mutated {@link Options} + */ + public Options withStyle(String url) { + this.styleUrl = url; + return this; + } + + /** + * @param region the region to show in the snapshot. + * This is applied after the camera position + * @return the mutated {@link Options} + */ + public Options withRegion(LatLngBounds region) { + this.region = region; + return this; + } + + /** + * @param pixelRatio the pixel ratio to use (default: 1) + * @return the mutated {@link Options} + */ + public Options withPixelRatio(int pixelRatio) { + this.pixelRatio = pixelRatio; + return this; + } + + /** + * @param cameraPosition The camera position to use, + * the {@link CameraPosition#target} is overridden + * by region if set in conjunction. + * @return the mutated {@link Options} + */ + public Options withCameraPosition(CameraPosition cameraPosition) { + this.cameraPosition = cameraPosition; + return this; + } + + /** + * @return the width of the image + */ + public int getWidth() { + return width; + } + + /** + * @return the height of the image + */ + public int getHeight() { + return height; + } + + /** + * @return the pixel ratio + */ + public int getPixelRatio() { + return pixelRatio; + } + + /** + * @return the region + */ + @Nullable + public LatLngBounds getRegion() { + return region; + } + + /** + * @return the style url + */ + public String getStyleUrl() { + return styleUrl; + } + + /** + * @return the camera position + */ + @Nullable + public CameraPosition getCameraPosition() { + return cameraPosition; + } + } + + /** + * Creates the Map snapshotter, but doesn't start rendering or + * loading yet. + * + * @param context the Context that is or contains the Application context + * @param options the options to use for the snapshot + */ + public MapSnapshotter(@NonNull Context context, @NonNull Options options) { + this.context = context.getApplicationContext(); + FileSource fileSource = FileSource.getInstance(context); + String programCacheDir = context.getCacheDir().getAbsolutePath(); + + nativeInitialize(this, fileSource, options.pixelRatio, options.width, + options.height, options.styleUrl, options.region, options.cameraPosition, + programCacheDir); + } + + /** + * Starts loading and rendering the snapshot. The callback will be fired + * on the calling thread. + * + * @param callback the callback to use when the snapshot is ready + */ + public void start(@NonNull MapboxMap.SnapshotReadyCallback callback) { + this.start(callback, null); + } + + /** + * Starts loading and rendering the snapshot. The callbacks will be fired + * on the calling thread. + * + * @param callback the callback to use when the snapshot is ready + * @param errorHandler the error handler to use on snapshot errors + */ + public void start(@NonNull MapboxMap.SnapshotReadyCallback callback, ErrorHandler errorHandler) { + if (this.callback != null) { + throw new IllegalStateException("Snapshotter was already started"); + } + + this.callback = callback; + this.errorHandler = errorHandler; + nativeStart(); + } + + /** + * Must be called in on the thread + * the object was created on. + */ + public void cancel() { + callback = null; + nativeCancel(); + } + + protected void addOverlay(Bitmap original) { + float margin = context.getResources().getDisplayMetrics().density * LOGO_MARGIN_PX; + Canvas canvas = new Canvas(original); + Bitmap logo = BitmapFactory.decodeResource(context.getResources(), R.drawable.mapbox_logo_icon, null); + canvas.drawBitmap(logo, margin, original.getHeight() - (logo.getHeight() + margin), null); + } + + /** + * Called by JNI peer when snapshot is ready. + * Always called on the origin (main) thread. + * + * @param bitmap the generated snapshot + */ + protected void onSnapshotReady(Bitmap bitmap) { + if (callback != null) { + addOverlay(bitmap); + callback.onSnapshotReady(bitmap); + reset(); + } + } + + /** + * Called by JNI peer when snapshot has failed. + * Always called on the origin (main) thread. + * + * @param reason the exception string + */ + protected void onSnapshotFailed(String reason) { + if (errorHandler != null) { + errorHandler.onError(reason); + reset(); + } + } + + protected void reset() { + callback = null; + errorHandler = null; + } + + protected native void nativeInitialize(MapSnapshotter mapSnapshotter, + FileSource fileSource, float pixelRatio, + int width, int height, String styleUrl, + LatLngBounds region, CameraPosition position, + String programCacheDir); + + protected native void nativeStart(); + + protected native void nativeCancel(); + + @Override + protected native void finalize() throws Throwable; +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java index eafef80e8d..a968cdf192 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java @@ -72,9 +72,9 @@ public class FileSource { MapboxConstants.KEY_META_DATA_SET_STORAGE_EXTERNAL, MapboxConstants.DEFAULT_SET_STORAGE_EXTERNAL); } catch (PackageManager.NameNotFoundException exception) { - Timber.e("Failed to read the package metadata: ", exception); + Timber.e(exception,"Failed to read the package metadata: "); } catch (Exception exception) { - Timber.e("Failed to read the storage key: ", exception); + Timber.e(exception, "Failed to read the storage key: "); } String cachePath = null; @@ -83,7 +83,7 @@ public class FileSource { // Try getting the external storage path cachePath = context.getExternalFilesDir(null).getAbsolutePath(); } catch (NullPointerException exception) { - Timber.e("Failed to obtain the external storage path: ", exception); + Timber.e(exception, "Failed to obtain the external storage path: "); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java index e1e40821b1..e7bb52ebb3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java @@ -279,7 +279,7 @@ public class Function<I, O> { // noinspection unchecked return (S) stops; } catch (ClassCastException exception) { - Timber.e(String.format("Stops: %s is a different type: %s", stops.getClass(), exception)); + Timber.e(exception, "Stops: %s is a different type: ", stops.getClass()); return null; } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java index 1a7df06031..10663142b5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java @@ -278,6 +278,16 @@ public class CircleLayer extends Layer { } /** + * Get the CirclePitchAlignment property + * + * @return property wrapper value around String + */ + @SuppressWarnings("unchecked") + public PropertyValue<String> getCirclePitchAlignment() { + return (PropertyValue<String>) new PropertyValue("circle-pitch-alignment", nativeGetCirclePitchAlignment()); + } + + /** * Get the CircleStrokeWidth property * * @return property wrapper value around Float @@ -411,6 +421,8 @@ public class CircleLayer extends Layer { private native Object nativeGetCirclePitchScale(); + private native Object nativeGetCirclePitchAlignment(); + private native Object nativeGetCircleStrokeWidth(); private native TransitionOptions nativeGetCircleStrokeWidthTransition(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java index 7807556b78..f77e7280f0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java @@ -13,7 +13,16 @@ public class CustomLayer extends Layer { long initializeFunction, long renderFunction, long deinitializeFunction) { - initialize(id, initializeFunction, renderFunction, deinitializeFunction, context); + this(id, context, initializeFunction, renderFunction, 0L, deinitializeFunction); + } + + public CustomLayer(String id, + long context, + long initializeFunction, + long renderFunction, + long contextLostFunction, + long deinitializeFunction) { + initialize(id, initializeFunction, renderFunction, contextLostFunction, deinitializeFunction, context); } public CustomLayer(long nativePtr) { @@ -24,7 +33,8 @@ public class CustomLayer extends Layer { nativeUpdate(); } - protected native void initialize(String id, long initializeFunction, long renderFunction, long deinitializeFunction, + protected native void initialize(String id, long initializeFunction, long renderFunction, + long contextLostFunction, long deinitializeFunction, long context); protected native void nativeUpdate(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java index 5e345268f9..8d5858217b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java @@ -160,6 +160,88 @@ public final class Property { @Retention(RetentionPolicy.SOURCE) public @interface ICON_TEXT_FIT {} + // ICON_ANCHOR: Part of the icon placed closest to the anchor. + + /** + * The center of the icon is placed closest to the anchor. + */ + public static final String ICON_ANCHOR_CENTER = "center"; + /** + * The left side of the icon is placed closest to the anchor. + */ + public static final String ICON_ANCHOR_LEFT = "left"; + /** + * The right side of the icon is placed closest to the anchor. + */ + public static final String ICON_ANCHOR_RIGHT = "right"; + /** + * The top of the icon is placed closest to the anchor. + */ + public static final String ICON_ANCHOR_TOP = "top"; + /** + * The bottom of the icon is placed closest to the anchor. + */ + public static final String ICON_ANCHOR_BOTTOM = "bottom"; + /** + * The top left corner of the icon is placed closest to the anchor. + */ + public static final String ICON_ANCHOR_TOP_LEFT = "top-left"; + /** + * The top right corner of the icon is placed closest to the anchor. + */ + public static final String ICON_ANCHOR_TOP_RIGHT = "top-right"; + /** + * The bottom left corner of the icon is placed closest to the anchor. + */ + public static final String ICON_ANCHOR_BOTTOM_LEFT = "bottom-left"; + /** + * The bottom right corner of the icon is placed closest to the anchor. + */ + public static final String ICON_ANCHOR_BOTTOM_RIGHT = "bottom-right"; + + /** + * Part of the icon placed closest to the anchor. + */ + @StringDef({ + ICON_ANCHOR_CENTER, + ICON_ANCHOR_LEFT, + ICON_ANCHOR_RIGHT, + ICON_ANCHOR_TOP, + ICON_ANCHOR_BOTTOM, + ICON_ANCHOR_TOP_LEFT, + ICON_ANCHOR_TOP_RIGHT, + ICON_ANCHOR_BOTTOM_LEFT, + ICON_ANCHOR_BOTTOM_RIGHT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ICON_ANCHOR {} + + // ICON_PITCH_ALIGNMENT: Orientation of icon when map is pitched. + + /** + * The icon is aligned to the plane of the map. + */ + public static final String ICON_PITCH_ALIGNMENT_MAP = "map"; + /** + * The icon is aligned to the plane of the viewport. + */ + public static final String ICON_PITCH_ALIGNMENT_VIEWPORT = "viewport"; + /** + * Automatically matches the value of {@link ICON_ROTATION_ALIGNMENT}. + */ + public static final String ICON_PITCH_ALIGNMENT_AUTO = "auto"; + + /** + * Orientation of icon when map is pitched. + */ + @StringDef({ + ICON_PITCH_ALIGNMENT_MAP, + ICON_PITCH_ALIGNMENT_VIEWPORT, + ICON_PITCH_ALIGNMENT_AUTO, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ICON_PITCH_ALIGNMENT {} + // TEXT_PITCH_ALIGNMENT: Orientation of text when map is pitched. /** @@ -446,6 +528,27 @@ public final class Property { @Retention(RetentionPolicy.SOURCE) public @interface CIRCLE_PITCH_SCALE {} + // CIRCLE_PITCH_ALIGNMENT: Orientation of circle when map is pitched. + + /** + * The circle is aligned to the plane of the map. + */ + public static final String CIRCLE_PITCH_ALIGNMENT_MAP = "map"; + /** + * The circle is aligned to the plane of the viewport. + */ + public static final String CIRCLE_PITCH_ALIGNMENT_VIEWPORT = "viewport"; + + /** + * Orientation of circle when map is pitched. + */ + @StringDef({ + CIRCLE_PITCH_ALIGNMENT_MAP, + CIRCLE_PITCH_ALIGNMENT_VIEWPORT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CIRCLE_PITCH_ALIGNMENT {} + // FILL_EXTRUSION_TRANSLATE_ANCHOR: Controls the translation reference point. /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java index e4ea9676fa..d4ddbe48ef 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java @@ -322,11 +322,11 @@ public class PropertyFactory { /** * Stroke thickness. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param <T> the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> lineWidth(CameraFunction<Z, Float> function) { + public static <T> PropertyValue<Function<T, Float>> lineWidth(Function<T, Float> function) { return new PaintPropertyValue<>("line-width", function); } @@ -953,6 +953,28 @@ public class PropertyFactory { } /** + * Orientation of circle when map is pitched. + * + * @param value a String value + * @return property wrapper around String + */ + public static PropertyValue<String> circlePitchAlignment(@Property.CIRCLE_PITCH_ALIGNMENT String value) { + return new PaintPropertyValue<>("circle-pitch-alignment", value); + } + + + /** + * Orientation of circle when map is pitched. + * + * @param <Z> the zoom parameter type + * @param function a wrapper {@link CameraFunction} for String + * @return property wrapper around a String function + */ + public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> circlePitchAlignment(CameraFunction<Z, String> function) { + return new PaintPropertyValue<>("circle-pitch-alignment", function); + } + + /** * The width of the circle's stroke. Strokes are placed outside of the {@link PropertyFactory#circleRadius}. * * @param value a Float value @@ -1460,11 +1482,11 @@ public class PropertyFactory { /** * The display of lines when joining. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param <T> the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> lineJoin(CameraFunction<Z, String> function) { + public static <T> PropertyValue<Function<T, String>> lineJoin(Function<T, String> function) { return new LayoutPropertyValue<>("line-join", function); } @@ -1676,7 +1698,7 @@ public class PropertyFactory { } /** - * Scale factor for icon. 1 is original size, 3 triples the size. + * Scales the original size of the icon by the provided factor. The new pixel size of the image will be the original pixel size multiplied by {@link PropertyFactory#iconSize}. 1 is the original size; 3 triples the size of the image. * * @param value a Float value * @return property wrapper around Float @@ -1688,7 +1710,7 @@ public class PropertyFactory { /** - * Scale factor for icon. 1 is original size, 3 triples the size. + * Scales the original size of the icon by the provided factor. The new pixel size of the image will be the original pixel size multiplied by {@link PropertyFactory#iconSize}. 1 is the original size; 3 triples the size of the image. * * @param <T> the function input type * @param function a wrapper function for Float @@ -1860,6 +1882,52 @@ public class PropertyFactory { } /** + * Part of the icon placed closest to the anchor. + * + * @param value a String value + * @return property wrapper around String + */ + public static PropertyValue<String> iconAnchor(@Property.ICON_ANCHOR String value) { + return new LayoutPropertyValue<>("icon-anchor", value); + } + + + + /** + * Part of the icon placed closest to the anchor. + * + * @param <T> the function input type + * @param function a wrapper function for String + * @return property wrapper around a String function + */ + public static <T> PropertyValue<Function<T, String>> iconAnchor(Function<T, String> function) { + return new LayoutPropertyValue<>("icon-anchor", function); + } + + /** + * Orientation of icon when map is pitched. + * + * @param value a String value + * @return property wrapper around String + */ + public static PropertyValue<String> iconPitchAlignment(@Property.ICON_PITCH_ALIGNMENT String value) { + return new LayoutPropertyValue<>("icon-pitch-alignment", value); + } + + + + /** + * Orientation of icon when map is pitched. + * + * @param <Z> the zoom parameter type + * @param function a wrapper {@link CameraFunction} for String + * @return property wrapper around a String function + */ + public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconPitchAlignment(CameraFunction<Z, String> function) { + return new LayoutPropertyValue<>("icon-pitch-alignment", function); + } + + /** * Orientation of text when map is pitched. * * @param value a String value @@ -1989,11 +2057,11 @@ public class PropertyFactory { /** * The maximum line width for text wrapping. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param <T> the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textMaxWidth(CameraFunction<Z, Float> function) { + public static <T> PropertyValue<Function<T, Float>> textMaxWidth(Function<T, Float> function) { return new LayoutPropertyValue<>("text-max-width", function); } @@ -2035,11 +2103,11 @@ public class PropertyFactory { /** * Text tracking amount. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param <T> the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textLetterSpacing(CameraFunction<Z, Float> function) { + public static <T> PropertyValue<Function<T, Float>> textLetterSpacing(Function<T, Float> function) { return new LayoutPropertyValue<>("text-letter-spacing", function); } @@ -2058,11 +2126,11 @@ public class PropertyFactory { /** * Text justification options. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param <T> the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textJustify(CameraFunction<Z, String> function) { + public static <T> PropertyValue<Function<T, String>> textJustify(Function<T, String> function) { return new LayoutPropertyValue<>("text-justify", function); } @@ -2081,11 +2149,11 @@ public class PropertyFactory { /** * Part of the text placed closest to the anchor. * - * @param <Z> the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param <T> the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textAnchor(CameraFunction<Z, String> function) { + public static <T> PropertyValue<Function<T, String>> textAnchor(Function<T, String> function) { return new LayoutPropertyValue<>("text-anchor", function); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java index 290e162da8..d0fb82dce5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java @@ -252,6 +252,26 @@ public class SymbolLayer extends Layer { } /** + * Get the IconAnchor property + * + * @return property wrapper value around String + */ + @SuppressWarnings("unchecked") + public PropertyValue<String> getIconAnchor() { + return (PropertyValue<String>) new PropertyValue("icon-anchor", nativeGetIconAnchor()); + } + + /** + * Get the IconPitchAlignment property + * + * @return property wrapper value around String + */ + @SuppressWarnings("unchecked") + public PropertyValue<String> getIconPitchAlignment() { + return (PropertyValue<String>) new PropertyValue("icon-pitch-alignment", nativeGetIconPitchAlignment()); + } + + /** * Get the TextPitchAlignment property * * @return property wrapper value around String @@ -891,6 +911,10 @@ public class SymbolLayer extends Layer { private native Object nativeGetIconOffset(); + private native Object nativeGetIconAnchor(); + + private native Object nativeGetIconPitchAlignment(); + private native Object nativeGetTextPitchAlignment(); private native Object nativeGetTextRotationAlignment(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java index cb6465a6b1..8f23e7d01e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java @@ -43,8 +43,7 @@ public class Light { * * @return anchor as String */ - @Property.ANCHOR - public String getAnchor() { + @Property.ANCHOR public String getAnchor() { return nativeGetAnchor(); } @@ -107,7 +106,7 @@ public class Light { * * @return color as String */ - public String getColor() { + public String getColor() { return nativeGetColor(); } @@ -143,7 +142,7 @@ public class Light { * * @return intensity as Float */ - public float getIntensity() { + public float getIntensity() { return nativeGetIntensity(); } @@ -166,30 +165,17 @@ public class Light { } private native void nativeSetAnchor(String anchor); - private native String nativeGetAnchor(); - private native void nativeSetPosition(Position position); - private native Position nativeGetPosition(); - private native TransitionOptions nativeGetPositionTransition(); - private native void nativeSetPositionTransition(long duration, long delay); - private native void nativeSetColor(String color); - private native String nativeGetColor(); - private native TransitionOptions nativeGetColorTransition(); - private native void nativeSetColorTransition(long duration, long delay); - private native void nativeSetIntensity(float intensity); - private native float nativeGetIntensity(); - private native TransitionOptions nativeGetIntensityTransition(); - private native void nativeSetIntensityTransition(long duration, long delay); }
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java new file mode 100644 index 0000000000..84e5e96fa4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java @@ -0,0 +1,137 @@ +package com.mapbox.mapboxsdk.style.sources; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.UiThread; +import android.support.v4.content.ContextCompat; + +import com.mapbox.mapboxsdk.Mapbox; +import com.mapbox.mapboxsdk.geometry.LatLngQuad; + +import java.net.URL; + + +/** + * Image source, allows a georeferenced raster image to be shown on the map. + * <p> + * The georeferenced image scales and rotates as the user zooms and rotates the map. + * The geographic location of the raster image content, supplied with `LatLngQuad`, + * can be non-axis aligned. + * </p> + * * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-image">the style specification</a> + */ +@UiThread +public class ImageSource extends Source { + + /** + * Internal use + * + * @param nativePtr - pointer to native peer + */ + public ImageSource(long nativePtr) { + super(nativePtr); + } + + /** + * Create an ImageSource from coordinates and an image URL + * + * @param id The source id + * @param coordinates The Latitude and Longitude of the four corners of the image + * @param url remote json file + */ + public ImageSource(String id, LatLngQuad coordinates, URL url) { + initialize(id, coordinates); + setUrl(url); + } + + /** + * Create an ImageSource from coordinates and a bitmap image + * + * @param id The source id + * @param coordinates The Latitude and Longitude of the four corners of the image + * @param bitmap A Bitmap image + */ + public ImageSource(String id, LatLngQuad coordinates, @NonNull android.graphics.Bitmap bitmap) { + initialize(id, coordinates); + setImage(bitmap); + } + + /** + * Create an ImageSource from coordinates and a bitmap image resource + * + * @param id The source id + * @param coordinates The Latitude and Longitude of the four corners of the image + * @param resourceId The resource ID of a Bitmap image + */ + public ImageSource(String id, LatLngQuad coordinates, @DrawableRes int resourceId) { + initialize(id, coordinates); + setImage(resourceId); + } + + /** + * Updates the source image url + * + * @param url An Image url + */ + public void setUrl(URL url) { + setUrl(url.toExternalForm()); + } + + /** + * Updates the source image url + * + * @param url An image url + */ + public void setUrl(String url) { + nativeSetUrl(url); + } + + /** + * Updates the source image to a bitmap + * + * @param bitmap A Bitmap image + */ + public void setImage(@NonNull android.graphics.Bitmap bitmap) { + nativeSetImage(bitmap); + } + + /** + * Updates the source image to a bitmap image resource + * + * @param resourceId The resource ID of a Bitmap image + */ + public void setImage(@DrawableRes int resourceId) throws IllegalArgumentException { + Context context = Mapbox.getApplicationContext(); + Drawable drawable = ContextCompat.getDrawable(context, resourceId); + if (drawable instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + nativeSetImage(bitmapDrawable.getBitmap()); + } else { + throw new IllegalArgumentException("Failed to decode image. The resource provided must be a Bitmap."); + } + } + + /** + * @return The url or null + */ + @Nullable + public String getUrl() { + return nativeGetUrl(); + } + + protected native void initialize(String layerId, LatLngQuad payload); + + protected native void nativeSetUrl(String url); + + protected native String nativeGetUrl(); + + protected native void nativeSetImage(Bitmap bitmap); + + @Override + protected native void finalize() throws Throwable; +} 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 new file mode 100644 index 0000000000..c7d7a13a3d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java @@ -0,0 +1,27 @@ +package com.mapbox.mapboxsdk.utils; + +/** + * Comparisons from std sdk, which aren't available in API level <= 15 + */ +public class Compare { + + /** + * @see Integer#compare(int, int) + * @param x left side + * @param y right side + * @return std compare value + */ + public static int compare(int x, int y) { + return (x < y) ? -1 : ((x == y) ? 0 : 1); + } + + /** + * @see Boolean#compare(boolean, boolean) + * @param x left side + * @param y right side + * @return std compare value + */ + public static int compare(boolean x, boolean y) { + return (x == y) ? 0 : (x ? 1 : -1); + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml index f30cb7c27a..40045f851f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml @@ -72,19 +72,21 @@ <public name="mapbox_uiAttributionMarginRight" type="attr" /> <public name="mapbox_uiAttributionMarginBottom" type="attr" /> - <!-- Deprecated to use TextureView--> - <public name="mapbox_renderTextureMode" type="attr" /> + <public name="mapbox_enableTilePrefetch" type="attr" /> + <public name="mapbox_enableZMediaOverlay" type="attr" /> <!-- Exposed content descriptions --> <public name="mapbox_logoContentDescription" type="string" /> <!-- Exposed styles --> <public name="mapbox_style_mapbox_streets" type="string" /> - <public name="mapbox_style_emerald" type="string" /> + <public name="mapbox_style_outdoors" type="string" /> <public name="mapbox_style_light" type="string" /> <public name="mapbox_style_dark" type="string" /> <public name="mapbox_style_satellite" type="string" /> <public name="mapbox_style_satellite_streets" type="string" /> + <public name="mapbox_style_traffic_day" type="string" /> + <public name="mapbox_style_traffic_night" type="string" /> <!-- Exposed strings --> <public name="mapbox_compassContentDescription" type="string" /> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml deleted file mode 100644 index 4c733ed112..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/color/mapbox_material_bg_selector.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="?attr/colorPrimaryDark" android:state_pressed="true" /> - <item android:color="?attr/colorPrimary" /> -</selector> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png b/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png Binary files differdeleted file mode 100644 index 584b320299..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_infowindow_icon_bg.9.png +++ /dev/null diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml index 763bb118e0..f275860d59 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml @@ -9,5 +9,6 @@ android:paddingLeft="24dp" android:paddingRight="24dp" android:textAllCaps="true" + android:textIsSelectable="false" android:textAppearance="?android:attr/textAppearanceButton" android:textColor="@color/mapbox_blue"/> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml index 26c974dc0d..3a35396257 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml @@ -23,6 +23,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="2dp" + android:textIsSelectable="false" android:maxEms="17" android:textColor="@android:color/black" android:textSize="18sp" @@ -34,13 +35,13 @@ android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:layout_marginTop="2dp" + android:textIsSelectable="false" android:lineSpacingExtra="1dp" android:maxEms="17" android:textColor="@color/mapbox_gray" android:textSize="14sp"/> <TextView - android:id="@+id/infowindow_subdescription" android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxEms="17" diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml index 6d07de7baa..df7ccaaca9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> - <SurfaceView + <android.opengl.GLSurfaceView android:id="@+id/surfaceView" android:layout_width="match_parent" android:layout_height="match_parent" @@ -28,6 +28,7 @@ android:contentDescription="@string/mapbox_compassContentDescription"/> <ImageView + android:visibility="gone" android:id="@+id/logoView" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -35,6 +36,7 @@ android:src="@drawable/mapbox_logo_icon"/> <ImageView + android:visibility="gone" android:id="@+id/attributionView" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml index 7e4a079063..51eb46e1d5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/image" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> + android:id="@+id/image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@null"/> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml index e17f01d075..b673224094 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml @@ -40,6 +40,7 @@ <attr name="mapbox_myLocationBackgroundMarginBottom" format="dimension"/> <attr name="mapbox_myLocationAccuracyTintColor" format="color"/> <attr name="mapbox_myLocationAccuracyAlpha" format="integer"/> + <attr name="mapbox_myLocationAccuracyThreshold" format="float"/> <!--Compass--> <attr name="mapbox_uiCompass" format="boolean"/> @@ -113,8 +114,8 @@ <attr name="mapbox_uiAttributionMarginBottom" format="dimension"/> <attr name="mapbox_uiAttributionTintColor" format="color"/> - <!-- Deprecated to use TextureView--> - <attr name="mapbox_renderTextureMode" format="boolean"/> + <attr name="mapbox_enableTilePrefetch" format="boolean"/> + <attr name="mapbox_enableZMediaOverlay" format="boolean"/> </declare-styleable> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml index 69ab7568bb..b51c890e5c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml @@ -2,5 +2,4 @@ <resources> <color name="mapbox_gray">#7D7F80</color> <color name="mapbox_blue">#1E8CAB</color> - <color name="mapbox_my_location_ring">@color/mapbox_blue</color> </resources> diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml index 8edbd47c29..1c6a265587 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml @@ -2,17 +2,8 @@ <resources> <dimen name="mapbox_infowindow_tipview_width">8dp</dimen> <dimen name="mapbox_infowindow_margin">8dp</dimen> - <dimen name="mapbox_infowindow_offset">-2dp</dimen> - <dimen name="mapbox_infowindow_line_width">1.5dp</dimen> - <dimen name="mapbox_attribution_icon_left_padding">@dimen/mapbox_two_dp</dimen> - <dimen name="mapbox_attribution_icon_top_padding">@dimen/mapbox_two_dp</dimen> - <dimen name="mapbox_attribution_icon_right_padding">@dimen/mapbox_two_dp</dimen> - <dimen name="mapbox_attribution_icon_bottom_padding">@dimen/mapbox_two_dp</dimen> - <dimen name="mapbox_two_dp">2dp</dimen> <dimen name="mapbox_four_dp">4dp</dimen> <dimen name="mapbox_eight_dp">8dp</dimen> - <dimen name="mapbox_ten_dp">10dp</dimen> - <dimen name="mapbox_sixteen_dp">16dp</dimen> <dimen name="mapbox_ninety_two_dp">92dp</dimen> <dimen name="mapbox_my_locationview_outer_circle">18dp</dimen> </resources> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java index e05190cd57..e05190cd57 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java index 605e159b84..605e159b84 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java index 1c259af2d0..1c259af2d0 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java index 94b629860e..94b629860e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java index fa571e06b1..fa571e06b1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java index ebd30f5422..ebd30f5422 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java index 3933c68887..3933c68887 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java index 54bb0e8cf4..54bb0e8cf4 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java index 0c5f3a4be2..0c5f3a4be2 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java index aaef7f8a51..cb654aa556 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java @@ -1,4 +1,4 @@ -package com.mapbox.mapboxsdk.testapp.model.constants; +package com.mapbox.mapboxsdk.constants; public class AppConstant { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index bb96c9939d..8d9a360714 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -74,6 +74,15 @@ public class LatLngBoundsTest { } @Test + public void emptySpan() { + latLngBounds = new LatLngBounds.Builder() + .include(LAT_LNG_NOT_NULL_ISLAND) + .include(LAT_LNG_NOT_NULL_ISLAND) + .build(); + assertTrue("Should be empty", latLngBounds.isEmptySpan()); + } + + @Test public void notEmptySpan() { latLngBounds = new LatLngBounds.Builder() .include(LAT_LNG_NOT_NULL_ISLAND) diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java index 12297247cf..12297247cf 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java index 06e93b9d2f..06e93b9d2f 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java index 00fd125a1a..00fd125a1a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java index 12b779de5d..12b779de5d 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java new file mode 100644 index 0000000000..0d592f9bb3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java @@ -0,0 +1,81 @@ +package com.mapbox.mapboxsdk.maps; + +import android.support.v4.util.LongSparseArray; + +import com.mapbox.mapboxsdk.annotations.Annotation; +import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.annotations.MarkerViewManager; +import com.mapbox.mapboxsdk.geometry.LatLng; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class AnnotationManagerTest { + + @Test + public void checksAddAMarker() throws Exception { + NativeMapView aNativeMapView = mock(NativeMapView.class); + MapView aMapView = mock(MapView.class); + LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>(); + MarkerViewManager aMarkerViewManager = mock(MarkerViewManager.class); + IconManager aIconManager = mock(IconManager.class); + Annotations annotations = new AnnotationContainer(aNativeMapView, annotationsArray); + Markers markers = new MarkerContainer(aNativeMapView, aMapView, annotationsArray, aIconManager, aMarkerViewManager); + Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray); + Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray); + AnnotationManager annotationManager = new AnnotationManager(aNativeMapView, aMapView, annotationsArray, + aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines); + Marker aMarker = mock(Marker.class); + long aId = 5L; + when(aNativeMapView.addMarker(aMarker)).thenReturn(aId); + BaseMarkerOptions aMarkerOptions = mock(BaseMarkerOptions.class); + MapboxMap aMapboxMap = mock(MapboxMap.class); + when(aMarkerOptions.getMarker()).thenReturn(aMarker); + + annotationManager.addMarker(aMarkerOptions, aMapboxMap); + + assertEquals(aMarker, annotationManager.getAnnotations().get(0)); + assertEquals(aMarker, annotationManager.getAnnotation(aId)); + } + + @Test + public void checksAddMarkers() throws Exception { + NativeMapView aNativeMapView = mock(NativeMapView.class); + MapView aMapView = mock(MapView.class); + LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>(); + MarkerViewManager aMarkerViewManager = mock(MarkerViewManager.class); + IconManager aIconManager = mock(IconManager.class); + Annotations annotations = new AnnotationContainer(aNativeMapView, annotationsArray); + Markers markers = new MarkerContainer(aNativeMapView, aMapView, annotationsArray, aIconManager, aMarkerViewManager); + Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray); + Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray); + AnnotationManager annotationManager = new AnnotationManager(aNativeMapView, aMapView, annotationsArray, + aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines); + long firstId = 1L; + long secondId = 2L; + List<BaseMarkerOptions> markerList = new ArrayList<>(); + MarkerOptions firstMarkerOption = new MarkerOptions().position(new LatLng()).title("first"); + MarkerOptions secondMarkerOption = new MarkerOptions().position(new LatLng()).title("second"); + markerList.add(firstMarkerOption); + markerList.add(secondMarkerOption); + MapboxMap aMapboxMap = mock(MapboxMap.class); + when(aNativeMapView.addMarker(any(Marker.class))).thenReturn(firstId, secondId); + + annotationManager.addMarkers(markerList, aMapboxMap); + + assertEquals(2, annotationManager.getAnnotations().size()); + assertEquals("first", ((Marker) annotationManager.getAnnotations().get(0)).getTitle()); + assertEquals("second", ((Marker) annotationManager.getAnnotations().get(1)).getTitle()); + assertEquals("first", ((Marker) annotationManager.getAnnotation(firstId)).getTitle()); + assertEquals("second", ((Marker) annotationManager.getAnnotation(secondId)).getTitle()); + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java index ce0cb00b0b..4f929641f3 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java @@ -186,5 +186,15 @@ public class MapboxMapOptionsTest { assertEquals(Color.BLUE, new MapboxMapOptions() .myLocationBackgroundTintColor(Color.BLUE).getMyLocationBackgroundTintColor()); } + + @Test + public void testPrefetchesTiles() { + // Default value + assertTrue(new MapboxMapOptions().getPrefetchesTiles()); + + // Check mutations + assertTrue(new MapboxMapOptions().setPrefetchesTiles(true).getPrefetchesTiles()); + assertFalse(new MapboxMapOptions().setPrefetchesTiles(false).getPrefetchesTiles()); + } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java index de5f364a5b..de5f364a5b 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java index fbe00b4dce..fbe00b4dce 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java index c9ce19dc85..c9ce19dc85 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java index 933bf05b39..933bf05b39 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java index bac1154d62..bac1154d62 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java index 94a6dc2194..94a6dc2194 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java index dd4c7b25ee..dd4c7b25ee 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/platform/android/MapboxGLAndroidSDK/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker index ca6ee9cea8..ca6ee9cea8 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker +++ b/platform/android/MapboxGLAndroidSDK/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/platform/android/MapboxGLAndroidSDKTestApp/.gitignore b/platform/android/MapboxGLAndroidSDKTestApp/.gitignore new file mode 100644 index 0000000000..cec211fe81 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/.gitignore @@ -0,0 +1,2 @@ +lint-baseline.xml +lint/lint-baseline-local.xml
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 56b537e2a2..67939b5144 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -25,15 +25,11 @@ android { } lintOptions { + baseline file("lint-baseline-local.xml") checkAllWarnings true warningsAsErrors true - disable 'MissingTranslation' - disable 'IconDensities' - disable 'InvalidPackage' - } - - testOptions { - unitTests.returnDefaultValues = true + disable 'MissingTranslation', 'GoogleAppIndexingWarning', 'UnpackedNativeCode', 'IconDipSize', 'TypographyQuotes' + abortOnError false } buildTypes { @@ -67,7 +63,6 @@ dependencies { // Leak Canary debugCompile rootProject.ext.dep.leakCanaryDebug releaseCompile rootProject.ext.dep.leakCanaryRelease - testCompile rootProject.ext.dep.leakCanaryTest // Mapbox Android Services (Java component) compile(rootProject.ext.dep.mapboxJavaServices) { @@ -75,8 +70,6 @@ dependencies { } // Testing dependencies - testCompile rootProject.ext.dep.junit - testCompile rootProject.ext.dep.mockito androidTestCompile rootProject.ext.dep.testSpoonRunner androidTestCompile rootProject.ext.dep.supportAnnotations androidTestCompile rootProject.ext.dep.testRunner @@ -90,5 +83,6 @@ apply from: 'gradle-config.gradle' apply from: 'gradle-device-farm.gradle' apply from: 'gradle-spoon.gradle' apply from: 'gradle-checkstyle.gradle' +apply from: '../gradle-lint.gradle' diff --git a/platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle b/platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle index 1068e5e69e..8346806633 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/gradle-config.gradle @@ -5,14 +5,15 @@ task accessToken { def tokenFile = new File("${projectDir}/src/main/res/values/developer-config.xml") if (!tokenFile.exists()) { + String mapboxAccessToken = "$System.env.MAPBOX_ACCESS_TOKEN" + if (mapboxAccessToken == "null") { + System.out.println("You should set the MAPBOX_ACCESS_TOKEN environment variable.") + mapboxAccessToken = "YOUR_MAPBOX_ACCESS_TOKEN_GOES_HERE" + } String tokenFileContents = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<resources>\n" + - " <string name=\"mapbox_access_token\">" + "$System.env.MAPBOX_ACCESS_TOKEN" + "</string>\n" + + " <string name=\"mapbox_access_token\">" + mapboxAccessToken + "</string>\n" + "</resources>" - - if (tokenFileContents == null) { - throw new InvalidUserDataException("You must set the MAPBOX_ACCESS_TOKEN environment variable.") - } tokenFile.write(tokenFileContents) } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml b/platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml new file mode 100644 index 0000000000..e3c5abce4f --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="4" by="lint 2.3.1"> + + <issue + id="UnusedResources" + message="The resource `R.string.mapbox_access_token` appears to be unused" + errorLine1=" <string name="mapbox_access_token">YOUR_MAPBOX_ACCESS_TOKEN_GOES_HERE</string>" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="src/main/res/values/developer-config.xml" + line="3" + column="13"/> + </issue> + + <issue + id="Overdraw" + message="Possible overdraw: Root element paints background `#cccc` with a theme that also paints a background (inferred theme is `@style/AppTheme`)" + errorLine1=" android:background="#cccc"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="src/main/res/layout/drawer_navigation_drawer.xml" + line="4" + column="5"/> + </issue> + + <issue + id="Overdraw" + message="Possible overdraw: Root element paints background `?android:attr/selectableItemBackground` with a theme that also paints a background (inferred theme is `@style/AppTheme`)" + errorLine1=" android:background="?android:attr/selectableItemBackground"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="src/main/res/layout/item_main_feature.xml" + line="6" + column="5"/> + </issue> + + <issue + id="Overdraw" + message="Possible overdraw: Root element paints background `@color/mapboxGreen` with a theme that also paints a background (inferred theme is `@style/AppTheme`)" + errorLine1=" android:background="@color/mapboxGreen">" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="src/main/res/layout/view_text_marker.xml" + line="5" + column="5"/> + </issue> + + <issue + id="TypographyQuotes" + message="Replace straight quotes ('') with directional quotes (‘’, &#8216; and &#8217;) ?" + errorLine1=" <string name="mapbox_attributionTelemetryMessage">Estàs ajudant a millorar els mapes d\'OpenStreetMap i de Mapbox aportant dades d\'ús anònimes.</string>" + errorLine2=" ^"> + <location + file="src/main/res/values-ca/strings.xml" + line="9" + column="55"/> + </issue> + + <issue + id="IconDuplicatesConfig" + message="The `icon_burned.png` icon has identical contents in the following configuration folders: drawable-hdpi, drawable-xhdpi"> + <location + file="src/main/res/drawable-xhdpi/icon_burned.png"/> + <location + file="src/main/res/drawable-hdpi/icon_burned.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-hdpi/ic_launcher_round.png`: expected 72x72, but was 216x216"> + <location + file="src/main/res/drawable-hdpi/ic_launcher_round.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-mdpi/ic_launcher_round.png`: expected 48x48, but was 144x144"> + <location + file="src/main/res/drawable-mdpi/ic_launcher_round.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xhdpi/ic_launcher_round.png`: expected 96x96, but was 288x288"> + <location + file="src/main/res/drawable-xhdpi/ic_launcher_round.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xxhdpi/ic_launcher_round.png`: expected 144x144, but was 432x432"> + <location + file="src/main/res/drawable-xxhdpi/ic_launcher_round.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xxxhdpi/ic_launcher_round.png`: expected 192x192, but was 576x576"> + <location + file="src/main/res/drawable-xxxhdpi/ic_launcher_round.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-hdpi/icon.png`: expected 72x72, but was 215x212"> + <location + file="src/main/res/drawable-hdpi/icon.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-mdpi/icon.png`: expected 48x48, but was 143x141"> + <location + file="src/main/res/drawable-mdpi/icon.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xhdpi/icon.png`: expected 96x96, but was 286x282"> + <location + file="src/main/res/drawable-xhdpi/icon.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xxhdpi/icon.png`: expected 144x144, but was 429x423"> + <location + file="src/main/res/drawable-xxhdpi/icon.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xxxhdpi/icon.png`: expected 192x192, but was 572x564"> + <location + file="src/main/res/drawable-xxxhdpi/icon.png"/> + </issue> + + <issue + id="IconDensities" + message="Missing the following drawables in `drawable-hdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)"> + <location + file="src/main/res/drawable-hdpi"/> + </issue> + + <issue + id="IconDensities" + message="Missing the following drawables in `drawable-mdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png"> + <location + file="src/main/res/drawable-mdpi"/> + </issue> + + <issue + id="IconDensities" + message="Missing the following drawables in `drawable-xhdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)"> + <location + file="src/main/res/drawable-xhdpi"/> + </issue> + + <issue + id="IconDensities" + message="Missing the following drawables in `drawable-xxhdpi`: ic_car_top.png, ic_taxi_top.png, southeast_radar_0.png, southeast_radar_1.png, southeast_radar_2.png... (1 more)"> + <location + file="src/main/res/drawable-xxhdpi"/> + </issue> + +</issues> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml b/platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml new file mode 100644 index 0000000000..64e3d41bcc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- REMEMBER! First you run Lint locally you'll need to move lint-baseline-local.xml.xml file + generated into the lint folder and called it lint-baseline-local.xml + If you remove any error when running Lint locally, you'll get a warning from the command + line advising you to remove it from the baseline. If you remove it (remember to remove it + from lint-baseline-local.xml file) you should remove it too from + lint-baseline-ci.xml (THIS FILE) which is the only one included in the repo. + Eventually, it'll be removed (when we remove all current lint errors included). --> +<issues by="lint 2.3.1" format="4"> + + <issue + errorLine1=" android:background="#cccc"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~" + id="Overdraw" + message="Possible overdraw: Root element paints background `#cccc` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"> + <location + column="5" + file="src/main/res/layout/drawer_navigation_drawer.xml" + line="4"/> + </issue> + + <issue + errorLine1=" android:background="?android:attr/selectableItemBackground"" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + id="Overdraw" + message="Possible overdraw: Root element paints background `?android:attr/selectableItemBackground` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"> + <location + column="5" + file="src/main/res/layout/item_main_feature.xml" + line="6"/> + </issue> + + <issue + errorLine1=" android:background="@color/mapboxGreen">" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + id="Overdraw" + message="Possible overdraw: Root element paints background `@color/mapboxGreen` with a theme that also paints a background (inferred theme is `@style/AppTheme`)"> + <location + column="5" + file="src/main/res/layout/view_text_marker.xml" + line="5"/> + </issue> + + <issue + errorLine1=" <string name="mapbox_attributionTelemetryMessage">Estàs ajudant a millorar els mapes d\'OpenStreetMap i de Mapbox aportant dades d\'ús anònimes.</string>" + errorLine2=" ^" + id="TypographyQuotes" + message="Replace straight quotes ('') with directional quotes (‘’, &#8216; and &#8217;) ?"> + <location + column="55" + file="src/main/res/values-ca/strings.xml" + line="9"/> + </issue> + + <issue + id="IconDipSize" + message="The image `icon_burned.png` varies significantly in its density-independent (dip) size across the various density versions: drawable-hdpi/icon_burned.png: 64x64 dp (96x96 px), drawable-xxxhdpi/icon_burned.png: 48x48 dp (192x192 px), drawable-xxhdpi/icon_burned.png: 48x48 dp (144x144 px), drawable-xhdpi/icon_burned.png: 48x48 dp (96x96 px), drawable-mdpi/icon_burned.png: 48x48 dp (48x48 px)"> + <location + file="src/main/res/drawable-mdpi/icon_burned.png"/> + <location + file="src/main/res/drawable-xhdpi/icon_burned.png"/> + <location + file="src/main/res/drawable-xxhdpi/icon_burned.png"/> + <location + file="src/main/res/drawable-xxxhdpi/icon_burned.png"/> + <location + file="src/main/res/drawable-hdpi/icon_burned.png"/> + </issue> + + <issue + id="IconDuplicatesConfig" + message="The `icon_burned.png` icon has identical contents in the following configuration folders: drawable-hdpi, drawable-xhdpi"> + <location + file="src/main/res/drawable-xhdpi/icon_burned.png"/> + <location + file="src/main/res/drawable-hdpi/icon_burned.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-hdpi/ic_launcher_round.png`: expected 72x72, but was 216x216"> + <location + file="src/main/res/drawable-hdpi/ic_launcher_round.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-mdpi/ic_launcher_round.png`: expected 48x48, but was 144x144"> + <location + file="src/main/res/drawable-mdpi/ic_launcher_round.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xhdpi/ic_launcher_round.png`: expected 96x96, but was 288x288"> + <location + file="src/main/res/drawable-xhdpi/ic_launcher_round.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xxhdpi/ic_launcher_round.png`: expected 144x144, but was 432x432"> + <location + file="src/main/res/drawable-xxhdpi/ic_launcher_round.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xxxhdpi/ic_launcher_round.png`: expected 192x192, but was 576x576"> + <location + file="src/main/res/drawable-xxxhdpi/ic_launcher_round.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-hdpi/icon.png`: expected 72x72, but was 215x212"> + <location + file="src/main/res/drawable-hdpi/icon.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-mdpi/icon.png`: expected 48x48, but was 143x141"> + <location + file="src/main/res/drawable-mdpi/icon.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xhdpi/icon.png`: expected 96x96, but was 286x282"> + <location + file="src/main/res/drawable-xhdpi/icon.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xxhdpi/icon.png`: expected 144x144, but was 429x423"> + <location + file="src/main/res/drawable-xxhdpi/icon.png"/> + </issue> + + <issue + id="IconExpectedSize" + message="Incorrect icon size for `drawable-xxxhdpi/icon.png`: expected 192x192, but was 572x564"> + <location + file="src/main/res/drawable-xxxhdpi/icon.png"/> + </issue> + + <issue + id="IconDensities" + message="Missing the following drawables in `drawable-hdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)"> + <location + file="src/main/res/drawable-hdpi"/> + </issue> + + <issue + id="IconDensities" + message="Missing the following drawables in `drawable-mdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png"> + <location + file="src/main/res/drawable-mdpi"/> + </issue> + + <issue + id="IconDensities" + message="Missing the following drawables in `drawable-xhdpi`: ic_car_top.png, ic_taxi_top.png, ic_us.png, southeast_radar_0.png, southeast_radar_1.png... (2 more)"> + <location + file="src/main/res/drawable-xhdpi"/> + </issue> + + <issue + id="IconDensities" + message="Missing the following drawables in `drawable-xxhdpi`: ic_car_top.png, ic_taxi_top.png, southeast_radar_0.png, southeast_radar_1.png, southeast_radar_2.png... (1 more)"> + <location + file="src/main/res/drawable-xxhdpi"/> + </issue> + +</issues> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java new file mode 100644 index 0000000000..3e226a7ec5 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java @@ -0,0 +1,42 @@ +package com.mapbox.mapboxsdk.maps; + +import com.mapbox.mapboxsdk.annotations.Icon; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +import timber.log.Timber; + +public class IconManagerResolver { + + private IconManager iconManager; + + public IconManagerResolver(MapboxMap mapboxMap) { + try { + Field annotationManagerField = MapboxMap.class.getDeclaredField("annotationManager"); + annotationManagerField.setAccessible(true); + AnnotationManager annotationManager = (AnnotationManager) annotationManagerField.get(mapboxMap); + + Field iconManagerField = AnnotationManager.class.getDeclaredField("iconManager"); + iconManagerField.setAccessible(true); + iconManager = (IconManager) iconManagerField.get(annotationManager); + } catch (Exception exception) { + Timber.e(exception, "Could not create IconManagerResolver, unable to reflect."); + } + } + + @SuppressWarnings("unchecked") + public Map<Icon, Integer> getIconMap() { + try { + Field field = IconManager.class.getDeclaredField("iconMap"); + field.setAccessible(true); + return (Map<Icon, Integer>) field.get(iconManager); + } catch (NoSuchFieldException exception) { + Timber.e(exception, "Could not getIconMap, unable to reflect."); + } catch (IllegalAccessException exception) { + Timber.e(exception, "Could not getIconMap, unable to reflect."); + } + return new HashMap<>(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java index a813b7f368..294d57bce1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java @@ -779,6 +779,22 @@ public class MapboxMapTest extends BaseActivityTest { })); } + // Tile pre-fetching + + @Test + public void testTilePrefetch() { + validateTestSetup(); + onView(withId(R.id.mapView)).perform(new MapboxMapAction(new InvokeViewAction() { + @Override + public void onViewAction(UiController uiController, View view) { + mapboxMap.setPrefetchesTiles(true); + assertTrue(mapboxMap.getPrefetchesTiles()); + mapboxMap.setPrefetchesTiles(false); + assertFalse(mapboxMap.getPrefetchesTiles()); + } + })); + } + private class MapboxMapAction implements ViewAction { private InvokeViewAction invokeViewAction; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java new file mode 100644 index 0000000000..33a946d0a1 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java @@ -0,0 +1,146 @@ +package com.mapbox.mapboxsdk.testapp.annotations; + +import android.app.Activity; +import android.support.v4.content.res.ResourcesCompat; + +import com.mapbox.mapboxsdk.annotations.Icon; +import com.mapbox.mapboxsdk.annotations.IconFactory; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.IconManagerResolver; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; +import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity; +import com.mapbox.mapboxsdk.testapp.utils.IconUtils; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; + +/** + * Tests integration between Icons and Markers + */ +public class IconTest extends BaseActivityTest { + + private Map<Icon, Integer> iconMap; + + @Before + public void beforeTest() { + super.beforeTest(); + iconMap = new IconManagerResolver(getMapboxMap()).getIconMap(); + } + + @Test + public void testEmpty() throws Exception { + assertTrue(iconMap.isEmpty()); + } + + @Test + public void testAddSameIconMarker() throws Exception { + Icon defaultMarker = IconFactory.getInstance(rule.getActivity()).defaultMarker(); + getMapboxMap().addMarker(new MarkerOptions().position(new LatLng())); + getMapboxMap().addMarker(new MarkerOptions().position(new LatLng(1, 1))); + assertEquals(1, iconMap.size()); + assertEquals(2, iconMap.get(defaultMarker), 0); + } + + @Test + public void testAddDifferentIconMarker() throws Exception { + Icon icon = IconFactory.getInstance(rule.getActivity()).fromResource(R.drawable.mapbox_logo_icon); + getMapboxMap().addMarker(new MarkerOptions().icon(icon).position(new LatLng())); + getMapboxMap().addMarker(new MarkerOptions().position(new LatLng(1, 1))); + assertEquals(iconMap.size(), 2); + assertTrue(iconMap.containsKey(icon)); + assertTrue(iconMap.get(icon) == 1); + } + + @Test + public void testAddRemoveIconMarker() throws Exception { + MapboxMap mapboxMap = getMapboxMap(); + + Icon icon = IconFactory.getInstance(rule.getActivity()).fromResource(R.drawable.mapbox_logo_icon); + Marker marker = mapboxMap.addMarker(new MarkerOptions().icon(icon).position(new LatLng())); + mapboxMap.addMarker(new MarkerOptions().position(new LatLng(1, 1))); + assertEquals(iconMap.size(), 2); + assertTrue(iconMap.containsKey(icon)); + assertTrue(iconMap.get(icon) == 1); + + mapboxMap.removeMarker(marker); + assertEquals(iconMap.size(), 1); + assertFalse(iconMap.containsKey(icon)); + } + + @Test + public void testAddRemoveDefaultMarker() throws Exception { + MapboxMap mapboxMap = getMapboxMap(); + + Marker marker = mapboxMap.addMarker(new MarkerOptions().position(new LatLng(1, 1))); + assertEquals(iconMap.size(), 1); + + mapboxMap.removeMarker(marker); + assertEquals(iconMap.size(), 0); + + mapboxMap.addMarker(new MarkerOptions().position(new LatLng())); + assertEquals(iconMap.size(), 1); + } + + @Test + public void testAddRemoveMany() throws Exception { + Activity activity = rule.getActivity(); + MapboxMap mapboxMap = getMapboxMap(); + IconFactory iconFactory = IconFactory.getInstance(activity); + + // add 2 default icon markers + Marker defaultMarkerOne = mapboxMap.addMarker(new MarkerOptions().position(new LatLng(1, 1))); + Marker defaultMarkerTwo = mapboxMap.addMarker(new MarkerOptions().position(new LatLng(2, 1))); + + // add 4 unique icon markers + mapboxMap.addMarker(new MarkerOptions() + .icon(iconFactory.fromResource(R.drawable.mapbox_logo_icon)) + .position(new LatLng(3, 1)) + ); + mapboxMap.addMarker(new MarkerOptions() + .icon(iconFactory.fromResource(R.drawable.mapbox_compass_icon)) + .position(new LatLng(4, 1)) + ); + mapboxMap.addMarker(new MarkerOptions() + .icon(IconUtils.drawableToIcon(activity, R.drawable.ic_stars, + ResourcesCompat.getColor(activity.getResources(), + R.color.blueAccent, activity.getTheme()))) + .position(new LatLng(5, 1)) + ); + mapboxMap.addMarker(new MarkerOptions() + .icon(iconFactory.fromResource(R.drawable.ic_android)) + .position(new LatLng(6, 1)) + ); + + assertEquals("Amount of icons should match 5", 5, iconMap.size()); + assertEquals("Refcounter of default marker should match 2", 2, iconMap.get(iconFactory.defaultMarker()), 0); + + mapboxMap.removeMarker(defaultMarkerOne); + + assertEquals("Amount of icons should match 5", 5, iconMap.size()); + assertEquals("Refcounter of default marker should match 1", 1, iconMap.get(iconFactory.defaultMarker()), 0); + + mapboxMap.removeMarker(defaultMarkerTwo); + + assertEquals("Amount of icons should match 4", 4, iconMap.size()); + assertNull("DefaultMarker shouldn't exist anymore", iconMap.get(iconFactory.defaultMarker())); + + mapboxMap.clear(); + assertEquals("Amount of icons should match 0", 0, iconMap.size()); + } + + @Override + protected Class getActivityClass() { + return EspressoTestActivity.class; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java index fa19235ad0..ec7105c321 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk.testapp.maps.widgets; +import android.annotation.SuppressLint; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -97,6 +98,7 @@ public class MyLocationViewTest extends BaseActivityTest { return getClass().getSimpleName(); } + @SuppressLint("MissingPermission") @Override public void perform(UiController uiController, View view) { if (isEnabled) { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java index c8f9640433..559e446307 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java @@ -1048,6 +1048,54 @@ public class CircleLayerTest extends BaseActivityTest { } @Test + public void testCirclePitchAlignmentAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("circle-pitch-alignment"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + layer.setProperties(circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP)); + assertEquals((String) layer.getCirclePitchAlignment().getValue(), (String) CIRCLE_PITCH_ALIGNMENT_MAP); + } + }); + } + + @Test + public void testCirclePitchAlignmentAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("circle-pitch-alignment"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + circlePitchAlignment( + zoom( + interval( + stop(2, circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getCirclePitchAlignment()); + assertNotNull(layer.getCirclePitchAlignment().getFunction()); + assertEquals(CameraFunction.class, layer.getCirclePitchAlignment().getFunction().getClass()); + assertEquals(IntervalStops.class, layer.getCirclePitchAlignment().getFunction().getStops().getClass()); + assertEquals(1, ((IntervalStops) layer.getCirclePitchAlignment().getFunction().getStops()).size()); + } + }); + } + + @Test public void testCircleStrokeWidthTransition() { validateTestSetup(); setupLayer(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java index be2fc9ab9c..5d10cfa38a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java @@ -1,6 +1,5 @@ package com.mapbox.mapboxsdk.testapp.style; -import android.content.res.Resources; import android.support.annotation.RawRes; import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; @@ -13,6 +12,7 @@ import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity; +import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; import com.mapbox.services.commons.geojson.Feature; import com.mapbox.services.commons.geojson.FeatureCollection; import com.mapbox.services.commons.geojson.Point; @@ -21,18 +21,13 @@ import org.hamcrest.Matcher; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; + +import timber.log.Timber; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static org.junit.Assert.fail; /** * Tests for {@link GeoJsonSource} @@ -46,19 +41,22 @@ public class GeoJsonSourceTests extends BaseActivityTest { } @Test - public void testFeatureCollection() { + public void testFeatureCollection() throws Exception { validateTestSetup(); onView(withId(R.id.mapView)).perform(new BaseViewAction() { @Override public void perform(UiController uiController, View view) { - GeoJsonSource source = new GeoJsonSource("source", FeatureCollection - .fromJson(readRawResource(rule.getActivity().getResources(), R.raw.test_feature_collection))); + GeoJsonSource source = null; + try { + source = new GeoJsonSource("source", FeatureCollection + .fromJson(ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_collection))); + } catch (IOException exception) { + Timber.e(exception); + } mapboxMap.addSource(source); - mapboxMap.addLayer(new CircleLayer("layer", source.getId())); } - }); } @@ -79,14 +77,19 @@ public class GeoJsonSourceTests extends BaseActivityTest { } @Test - public void testFeatureProperties() { + public void testFeatureProperties() throws IOException { validateTestSetup(); onView(withId(R.id.mapView)).perform(new BaseViewAction() { @Override public void perform(UiController uiController, View view) { - GeoJsonSource source = new GeoJsonSource("source", - readRawResource(rule.getActivity().getResources(), R.raw.test_feature_properties)); + GeoJsonSource source = null; + try { + source = new GeoJsonSource("source", + ResourceUtils.readRawResource(rule.getActivity(), R.raw.test_feature_properties)); + } catch (IOException exception) { + Timber.e(exception); + } mapboxMap.addSource(source); mapboxMap.addLayer(new CircleLayer("layer", source.getId())); @@ -141,8 +144,11 @@ public class GeoJsonSourceTests extends BaseActivityTest { Layer layer = new CircleLayer("layer", source.getId()); mapboxMap.addLayer(layer); - source.setGeoJson(Feature.fromJson( - readRawResource(rule.getActivity().getResources(), resource))); + try { + source.setGeoJson(Feature.fromJson(ResourceUtils.readRawResource(rule.getActivity(), resource))); + } catch (IOException exception) { + Timber.e(exception); + } mapboxMap.removeLayer(layer); mapboxMap.removeSource(source); @@ -151,27 +157,6 @@ public class GeoJsonSourceTests extends BaseActivityTest { }); } - private String readRawResource(Resources resources, @RawRes int rawResource) { - InputStream is = resources.openRawResource(rawResource); - Writer writer = new StringWriter(); - char[] buffer = new char[1024]; - try { - try { - Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); - int numRead; - while ((numRead = reader.read(buffer)) != -1) { - writer.write(buffer, 0, numRead); - } - } finally { - is.close(); - } - } catch (IOException err) { - fail(err.getMessage()); - } - - return writer.toString(); - } - public abstract class BaseViewAction implements ViewAction { @Override diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java new file mode 100644 index 0000000000..60cf4ced3d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.java @@ -0,0 +1,54 @@ +package com.mapbox.mapboxsdk.testapp.style; + +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.support.test.espresso.UiController; +import android.support.test.runner.AndroidJUnit4; + +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction; +import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; +import com.mapbox.mapboxsdk.testapp.activity.style.RuntimeStyleTestActivity; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * CRUD tests around Image + */ +@RunWith(AndroidJUnit4.class) +public class ImageTest extends BaseActivityTest { + + private static final String IMAGE_ID = "test.image"; + + @Override + protected Class getActivityClass() { + return RuntimeStyleTestActivity.class; + } + + @Test + public void testAddGetImage() { + validateTestSetup(); + MapboxMapAction.invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + Drawable drawable = rule.getActivity().getResources().getDrawable(R.drawable.ic_launcher_round); + assertTrue(drawable instanceof BitmapDrawable); + + Bitmap bitmapSet = ((BitmapDrawable) drawable).getBitmap(); + mapboxMap.addImage(IMAGE_ID, bitmapSet); + + Bitmap bitmapGet = mapboxMap.getImage(IMAGE_ID); + assertTrue(bitmapGet.sameAs(bitmapSet)); + + mapboxMap.removeImage(IMAGE_ID); + assertNull(mapboxMap.getImage(IMAGE_ID)); + } + }); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java index 595a2e43dc..8123d24be8 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java @@ -204,6 +204,63 @@ public class LineLayerTest extends BaseActivityTest { } @Test + public void testLineJoinAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("line-join"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + lineJoin(property("FeaturePropertyA", Stops.<String>identity())) + ); + + // Verify + assertNotNull(layer.getLineJoin()); + assertNotNull(layer.getLineJoin().getFunction()); + assertEquals(SourceFunction.class, layer.getLineJoin().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineJoin().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getLineJoin().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testLineJoinAsIntervalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("line-join"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + lineJoin( + property( + "FeaturePropertyA", + interval( + stop(1, lineJoin(LINE_JOIN_BEVEL)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getLineJoin()); + assertNotNull(layer.getLineJoin().getFunction()); + assertEquals(SourceFunction.class, layer.getLineJoin().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineJoin().getFunction()).getProperty()); + assertEquals(IntervalStops.class, layer.getLineJoin().getFunction().getStops().getClass()); + } + }); + } + + @Test public void testLineMiterLimitAsConstant() { validateTestSetup(); setupLayer(); @@ -861,6 +918,139 @@ public class LineLayerTest extends BaseActivityTest { } @Test + public void testLineWidthAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("line-width"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + lineWidth(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getLineWidth()); + assertNotNull(layer.getLineWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getLineWidth().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testLineWidthAsExponentialSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("line-width"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + lineWidth( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, lineWidth(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getLineWidth()); + assertNotNull(layer.getLineWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testLineWidthAsCategoricalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("line-width"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + lineWidth( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, lineWidth(0.3f)) + ) + ).withDefaultValue(lineWidth(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getLineWidth()); + assertNotNull(layer.getLineWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getLineWidth().getFunction().getStops().getClass()); + assertNotNull(((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue()); + assertNotNull(((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue().getValue()); + assertEquals(0.3f, ((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue().getValue()); + } + }); + + } + + @Test + public void testLineWidthAsCompositeFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("line-width"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + lineWidth( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, lineWidth(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(lineWidth(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getLineWidth()); + assertNotNull(layer.getLineWidth().getFunction()); + assertEquals(CompositeFunction.class, layer.getLineWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getLineWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getLineWidth().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getLineWidth().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + }); + } + + @Test public void testLineGapWidthTransition() { validateTestSetup(); setupLayer(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java index b0854f4a47..f8248ae4a7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java @@ -1214,6 +1214,159 @@ public class SymbolLayerTest extends BaseActivityTest { } @Test + public void testIconAnchorAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("icon-anchor"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + layer.setProperties(iconAnchor(ICON_ANCHOR_CENTER)); + assertEquals((String) layer.getIconAnchor().getValue(), (String) ICON_ANCHOR_CENTER); + } + }); + } + + @Test + public void testIconAnchorAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("icon-anchor"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + iconAnchor( + zoom( + interval( + stop(2, iconAnchor(ICON_ANCHOR_CENTER)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconAnchor()); + assertNotNull(layer.getIconAnchor().getFunction()); + assertEquals(CameraFunction.class, layer.getIconAnchor().getFunction().getClass()); + assertEquals(IntervalStops.class, layer.getIconAnchor().getFunction().getStops().getClass()); + assertEquals(1, ((IntervalStops) layer.getIconAnchor().getFunction().getStops()).size()); + } + }); + } + + @Test + public void testIconAnchorAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("icon-anchor"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + iconAnchor(property("FeaturePropertyA", Stops.<String>identity())) + ); + + // Verify + assertNotNull(layer.getIconAnchor()); + assertNotNull(layer.getIconAnchor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconAnchor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconAnchor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconAnchor().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testIconAnchorAsIntervalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("icon-anchor"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + iconAnchor( + property( + "FeaturePropertyA", + interval( + stop(1, iconAnchor(ICON_ANCHOR_CENTER)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconAnchor()); + assertNotNull(layer.getIconAnchor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconAnchor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconAnchor().getFunction()).getProperty()); + assertEquals(IntervalStops.class, layer.getIconAnchor().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testIconPitchAlignmentAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("icon-pitch-alignment"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + layer.setProperties(iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP)); + assertEquals((String) layer.getIconPitchAlignment().getValue(), (String) ICON_PITCH_ALIGNMENT_MAP); + } + }); + } + + @Test + public void testIconPitchAlignmentAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("icon-pitch-alignment"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + iconPitchAlignment( + zoom( + interval( + stop(2, iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconPitchAlignment()); + assertNotNull(layer.getIconPitchAlignment().getFunction()); + assertEquals(CameraFunction.class, layer.getIconPitchAlignment().getFunction().getClass()); + assertEquals(IntervalStops.class, layer.getIconPitchAlignment().getFunction().getStops().getClass()); + assertEquals(1, ((IntervalStops) layer.getIconPitchAlignment().getFunction().getStops()).size()); + } + }); + } + + @Test public void testTextPitchAlignmentAsConstant() { validateTestSetup(); setupLayer(); @@ -1694,6 +1847,139 @@ public class SymbolLayerTest extends BaseActivityTest { } @Test + public void testTextMaxWidthAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-max-width"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textMaxWidth(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getTextMaxWidth()); + assertNotNull(layer.getTextMaxWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testTextMaxWidthAsExponentialSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-max-width"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textMaxWidth( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, textMaxWidth(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextMaxWidth()); + assertNotNull(layer.getTextMaxWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testTextMaxWidthAsCategoricalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-max-width"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textMaxWidth( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, textMaxWidth(0.3f)) + ) + ).withDefaultValue(textMaxWidth(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getTextMaxWidth()); + assertNotNull(layer.getTextMaxWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass()); + assertNotNull(((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue()); + assertNotNull(((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue().getValue()); + assertEquals(0.3f, ((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue().getValue()); + } + }); + + } + + @Test + public void testTextMaxWidthAsCompositeFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-max-width"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textMaxWidth( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, textMaxWidth(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(textMaxWidth(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getTextMaxWidth()); + assertNotNull(layer.getTextMaxWidth().getFunction()); + assertEquals(CompositeFunction.class, layer.getTextMaxWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextMaxWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getTextMaxWidth().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextMaxWidth().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + }); + } + + @Test public void testTextLineHeightAsConstant() { validateTestSetup(); setupLayer(); @@ -1792,6 +2078,139 @@ public class SymbolLayerTest extends BaseActivityTest { } @Test + public void testTextLetterSpacingAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-letter-spacing"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textLetterSpacing(property("FeaturePropertyA", Stops.<Float>identity())) + ); + + // Verify + assertNotNull(layer.getTextLetterSpacing()); + assertNotNull(layer.getTextLetterSpacing().getFunction()); + assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testTextLetterSpacingAsExponentialSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-letter-spacing"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textLetterSpacing( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, textLetterSpacing(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextLetterSpacing()); + assertNotNull(layer.getTextLetterSpacing().getFunction()); + assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testTextLetterSpacingAsCategoricalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-letter-spacing"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textLetterSpacing( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, textLetterSpacing(0.3f)) + ) + ).withDefaultValue(textLetterSpacing(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getTextLetterSpacing()); + assertNotNull(layer.getTextLetterSpacing().getFunction()); + assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass()); + assertNotNull(((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue()); + assertNotNull(((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue().getValue()); + assertEquals(0.3f, ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue().getValue()); + } + }); + + } + + @Test + public void testTextLetterSpacingAsCompositeFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-letter-spacing"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textLetterSpacing( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, textLetterSpacing(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(textLetterSpacing(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getTextLetterSpacing()); + assertNotNull(layer.getTextLetterSpacing().getFunction()); + assertEquals(CompositeFunction.class, layer.getTextLetterSpacing().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextLetterSpacing().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getTextLetterSpacing().getFunction().getStops()).size()); + + ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops = + (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextLetterSpacing().getFunction().getStops(); + Stop<Stop.CompositeValue<Float, Float>, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + }); + } + + @Test public void testTextJustifyAsConstant() { validateTestSetup(); setupLayer(); @@ -1840,6 +2259,63 @@ public class SymbolLayerTest extends BaseActivityTest { } @Test + public void testTextJustifyAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-justify"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textJustify(property("FeaturePropertyA", Stops.<String>identity())) + ); + + // Verify + assertNotNull(layer.getTextJustify()); + assertNotNull(layer.getTextJustify().getFunction()); + assertEquals(SourceFunction.class, layer.getTextJustify().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextJustify().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextJustify().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testTextJustifyAsIntervalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-justify"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textJustify( + property( + "FeaturePropertyA", + interval( + stop(1, textJustify(TEXT_JUSTIFY_LEFT)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextJustify()); + assertNotNull(layer.getTextJustify().getFunction()); + assertEquals(SourceFunction.class, layer.getTextJustify().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextJustify().getFunction()).getProperty()); + assertEquals(IntervalStops.class, layer.getTextJustify().getFunction().getStops().getClass()); + } + }); + } + + @Test public void testTextAnchorAsConstant() { validateTestSetup(); setupLayer(); @@ -1888,6 +2364,63 @@ public class SymbolLayerTest extends BaseActivityTest { } @Test + public void testTextAnchorAsIdentitySourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-anchor"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textAnchor(property("FeaturePropertyA", Stops.<String>identity())) + ); + + // Verify + assertNotNull(layer.getTextAnchor()); + assertNotNull(layer.getTextAnchor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextAnchor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextAnchor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextAnchor().getFunction().getStops().getClass()); + } + }); + } + + @Test + public void testTextAnchorAsIntervalSourceFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("text-anchor"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + textAnchor( + property( + "FeaturePropertyA", + interval( + stop(1, textAnchor(TEXT_ANCHOR_CENTER)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextAnchor()); + assertNotNull(layer.getTextAnchor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextAnchor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextAnchor().getFunction()).getProperty()); + assertEquals(IntervalStops.class, layer.getTextAnchor().getFunction().getStops().getClass()); + } + }); + } + + @Test public void testTextMaxAngleAsConstant() { validateTestSetup(); setupLayer(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java index 6e582c6a3a..1c4981ca5e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/OnMapReadyIdlingResource.java @@ -3,20 +3,28 @@ package com.mapbox.mapboxsdk.testapp.utils; import android.app.Activity; import android.support.test.espresso.IdlingResource; -import timber.log.Timber; - +import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import java.lang.reflect.Field; -public class OnMapReadyIdlingResource implements IdlingResource { +public class OnMapReadyIdlingResource implements IdlingResource, OnMapReadyCallback { - private final Activity activity; private MapboxMap mapboxMap; private IdlingResource.ResourceCallback resourceCallback; + private Activity activity; public OnMapReadyIdlingResource(Activity activity) { this.activity = activity; + try { + Field field = activity.getClass().getDeclaredField("mapView"); + field.setAccessible(true); + ((MapView) field.get(activity)).getMapAsync(this); + } catch (Exception err) { + throw new RuntimeException(err); + } + } @Override @@ -26,11 +34,7 @@ public class OnMapReadyIdlingResource implements IdlingResource { @Override public boolean isIdleNow() { - boolean idle = isMapboxMapReady(); - if (idle && resourceCallback != null) { - resourceCallback.onTransitionToIdle(); - } - return idle; + return mapboxMap != null; } @Override @@ -38,20 +42,15 @@ public class OnMapReadyIdlingResource implements IdlingResource { this.resourceCallback = resourceCallback; } - private boolean isMapboxMapReady() { - try { - Field field = activity.getClass().getDeclaredField("mapboxMap"); - field.setAccessible(true); - mapboxMap = (MapboxMap) field.get(activity); - Timber.e("isMapboxReady called with value " + (mapboxMap != null)); - return mapboxMap != null; - } catch (Exception exception) { - Timber.e("could not reflect", exception); - return false; - } - } - public MapboxMap getMapboxMap() { return mapboxMap; } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + if (resourceCallback != null) { + resourceCallback.onTransitionToIdle(); + } + } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 1a70e4548a..bf97749b9e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -113,6 +113,17 @@ android:value=".activity.FeatureOverviewActivity"/> </activity> <activity + android:name=".activity.camera.CameraAnimatorActivity" + android:description="@string/description_camera_animator" + android:label="@string/activity_camera_animator"> + <meta-data + android:name="@string/category" + android:value="@string/category_camera"/> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".activity.FeatureOverviewActivity"/> + </activity> + <activity android:name=".activity.camera.CameraPositionActivity" android:description="@string/description_cameraposition" android:label="@string/activity_camera_position"> @@ -305,14 +316,12 @@ </activity> <activity android:name=".activity.maplayout.DebugModeActivity" + android:configChanges="orientation|keyboardHidden|screenSize" android:description="@string/description_debug_mode" android:label="@string/activity_debug_mode"> <meta-data android:name="@string/category" - android:value="@string/category_maplayout"/> - <meta-data - android:name="android.support.PARENT_ACTIVITY" - android:value=".activity.FeatureOverviewActivity"/> + android:value="@string/category_basic"/> </activity> <activity android:name=".activity.offline.OfflineActivity" @@ -358,6 +367,16 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activity.FeatureOverviewActivity"/> </activity> + <activity android:name=".activity.snapshot.MapSnapshotterActivity" + android:description="@string/description_map_snapshotter" + android:label="@string/activity_map_snapshotter"> + <meta-data + android:name="@string/category" + android:value="@string/category_imagegenerator"/> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".activity.FeatureOverviewActivity"/> + </activity> <activity android:name=".activity.maplayout.DoubleMapActivity" android:description="@string/description_doublemap" @@ -392,10 +411,20 @@ android:value=".activity.FeatureOverviewActivity"/> </activity> <activity - android:name=".activity.maplayout.NavigationDrawerActivity" - android:description="@string/description_navigation_drawer" - android:label="@string/activity_navigation_drawer" - android:theme="@style/AppTheme.ActionBar.Transparent"> + android:name=".activity.maplayout.SimpleMapActivity" + android:description="@string/description_simple_map" + android:label="@string/activity_simple_map"> + <meta-data + android:name="@string/category" + android:value="@string/category_basic"/> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".activity.FeatureOverviewActivity"/> + </activity> + <activity + android:name=".activity.maplayout.MapChangeActivity" + android:description="@string/description_map_change" + android:label="@string/activity_map_change"> <meta-data android:name="@string/category" android:value="@string/category_maplayout"/> @@ -404,12 +433,12 @@ android:value=".activity.FeatureOverviewActivity"/> </activity> <activity - android:name=".activity.maplayout.SimpleMapActivity" - android:description="@string/description_simple_map" - android:label="@string/activity_simple_map"> + android:name=".activity.maplayout.VisibilityChangeActivity" + android:description="@string/description_visibility_map" + android:label="@string/activity_map_visibility"> <meta-data android:name="@string/category" - android:value="@string/category_basic"/> + android:value="@string/category_maplayout"/> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".activity.FeatureOverviewActivity"/> @@ -535,6 +564,17 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activity.FeatureOverviewActivity"/> </activity> + <activity + android:name=".activity.style.AnimatedImageSourceActivity" + android:label="@string/activity_animated_image_source" + android:description="@string/description_animated_image_source"> + <meta-data + android:name="@string/category" + android:value="@string/category_style"/> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".activity.FeatureOverviewActivity"/> + </activity> <!-- Features --> <activity @@ -593,12 +633,23 @@ android:value=".activity.FeatureOverviewActivity"/> </activity> <activity - android:name=".activity.annotation.AddRemoveMarkerActivity" + android:name=".activity.style.SymbolGeneratorActivity" + android:description="@string/description_symbol_generator" + android:label="@string/activity_symbol_generator"> + <meta-data + android:name="@string/category" + android:value="@string/category_style"/> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".activity.FeatureOverviewActivity"/> + </activity> + <activity + android:name=".activity.style.ZoomFunctionSymbolLayerActivity" android:description="@string/description_add_remove_markers" android:label="@string/activity_add_remove_markers"> <meta-data android:name="@string/category" - android:value="@string/category_annotation"/> + android:value="@string/category_style"/> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".activity.FeatureOverviewActivity"/> @@ -649,7 +700,13 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activity.FeatureOverviewActivity"/> </activity> - + <activity android:name=".activity.maplayout.BottomSheetActivity" + android:description="@string/description_bottom_sheet" + android:label="@string/activity_bottom_sheet"> + <meta-data + android:name="@string/category" + android:value="@string/category_maplayout"/> + </activity> <!-- For Instrumentation tests --> <activity @@ -678,4 +735,4 @@ <!-- android:value="true" /> --> </application> -</manifest> +</manifest>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java index e344343627..fba33bb380 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java @@ -2,8 +2,10 @@ package com.mapbox.mapboxsdk.testapp; import android.app.Application; import android.os.StrictMode; +import android.text.TextUtils; import com.mapbox.mapboxsdk.Mapbox; +import com.mapbox.mapboxsdk.testapp.utils.TokenUtils; import com.squareup.leakcanary.LeakCanary; import timber.log.Timber; @@ -18,6 +20,12 @@ import static timber.log.Timber.DebugTree; */ public class MapboxApplication extends Application { + private static final String DEFAULT_MAPBOX_ACCESS_TOKEN = "YOUR_MAPBOX_ACCESS_TOKEN_GOES_HERE"; + private static final String ACCESS_TOKEN_NOT_SET_MESSAGE = "In order to run the Test App you need to set a valid " + + "access token. During development, you can set the MAPBOX_ACCESS_TOKEN environment variable for the SDK to " + + "automatically include it in the Test App. Otherwise, you can manually include it in the " + + "res/values/developer-config.xml file in the MapboxGLAndroidSDKTestApp folder."; + @Override public void onCreate() { super.onCreate(); @@ -43,7 +51,12 @@ public class MapboxApplication extends Application { .penaltyDeath() .build()); - Mapbox.getInstance(getApplicationContext(), getString(R.string.mapbox_access_token)); + String mapboxAccessToken = TokenUtils.getMapboxAccessToken(getApplicationContext()); + if (TextUtils.isEmpty(mapboxAccessToken) || mapboxAccessToken.equals(DEFAULT_MAPBOX_ACCESS_TOKEN)) { + Timber.e(ACCESS_TOKEN_NOT_SET_MESSAGE); + } + + Mapbox.getInstance(getApplicationContext(), mapboxAccessToken); } private void initializeLogger() { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java index f8617366a0..3f20f19f5d 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java @@ -92,7 +92,7 @@ public class FeatureOverviewActivity extends AppCompatActivity implements Permis getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA)); } catch (PackageManager.NameNotFoundException exception) { - Timber.e("Could not resolve package info", exception); + Timber.e(exception, "Could not resolve package info"); } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java deleted file mode 100644 index 27958c3d0c..0000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AddRemoveMarkerActivity.java +++ /dev/null @@ -1,188 +0,0 @@ -package com.mapbox.mapboxsdk.testapp.activity.annotation; - -import android.annotation.SuppressLint; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.os.Bundle; -import android.support.annotation.DrawableRes; -import android.support.v7.app.AppCompatActivity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.IconFactory; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerOptions; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; -import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.mapboxsdk.testapp.utils.ViewToBitmapUtil; - -import timber.log.Timber; - -/** - * Test activity showcasing updating a Marker image when changing zoom levels - */ -public class AddRemoveMarkerActivity extends AppCompatActivity { - - public static final double THRESHOLD = 5.0; - - private MapView mapView; - private MapboxMap mapboxMap; - private double lastZoom; - private boolean isShowingHighThresholdMarker; - private boolean isShowingLowThresholdMarker; - - private MarkerOptions lowThresholdMarker; - private MarkerOptions highThresholdMarker; - private Marker activeMarker; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_add_remove_marker); - - View lowThresholdView = generateView("Low", R.drawable.ic_circle); - Bitmap lowThresholdBitmap = ViewToBitmapUtil.convertToBitmap(lowThresholdView); - Icon lowThresholdIcon = IconFactory.getInstance(this).fromBitmap(lowThresholdBitmap); - - lowThresholdMarker = new MarkerOptions() - .icon(lowThresholdIcon) - .position(new LatLng(0.1, 0)); - - View highThesholdView = generateView("High", R.drawable.ic_circle); - Bitmap highThresholdBitmap = ViewToBitmapUtil.convertToBitmap(highThesholdView); - Icon highThresholdIcon = IconFactory.getInstance(this).fromBitmap(highThresholdBitmap); - - highThresholdMarker = new MarkerOptions() - .icon(highThresholdIcon) - .position(new LatLng(0.1, 0)); - - mapView = (MapView) findViewById(R.id.mapView); - mapView.onCreate(savedInstanceState); - mapView.getMapAsync(new OnMapReadyCallback() { - @Override - public void onMapReady(MapboxMap mapboxMap) { - AddRemoveMarkerActivity.this.mapboxMap = mapboxMap; - updateZoom(mapboxMap.getCameraPosition().zoom); - mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(4.9f)); - mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() { - @Override - public void onCameraChange(CameraPosition position) { - updateZoom(position.zoom); - } - }); - } - }); - } - - @SuppressLint("InflateParams") - private View generateView(String text, @DrawableRes int drawableRes) { - View view = LayoutInflater.from(this).inflate(R.layout.view_custom_marker, null); - TextView textView = (TextView) view.findViewById(R.id.textView); - textView.setText(text); - textView.setTextColor(Color.WHITE); - ImageView imageView = (ImageView) view.findViewById(R.id.imageView); - imageView.setImageResource(drawableRes); - return view; - } - - private void updateZoom(double zoom) { - if (lastZoom == zoom) { - return; - } - - lastZoom = zoom; - if (zoom > THRESHOLD) { - showHighThresholdMarker(); - } else { - showLowThresholdMarker(); - } - } - - private void showLowThresholdMarker() { - if (isShowingLowThresholdMarker) { - return; - } - - isShowingLowThresholdMarker = true; - isShowingHighThresholdMarker = false; - - if (activeMarker != null) { - Timber.d("Remove marker with " + activeMarker.getId()); - mapboxMap.removeMarker(activeMarker); - } else { - Timber.e("active marker is null"); - } - - activeMarker = mapboxMap.addMarker(lowThresholdMarker); - Timber.d("showLowThresholdMarker() " + activeMarker.getId()); - } - - private void showHighThresholdMarker() { - if (isShowingHighThresholdMarker) { - return; - } - - isShowingLowThresholdMarker = false; - isShowingHighThresholdMarker = true; - - if (activeMarker != null) { - Timber.d("Remove marker with " + activeMarker.getId()); - mapboxMap.removeMarker(activeMarker); - } else { - Timber.e("active marker is null"); - } - - activeMarker = mapboxMap.addMarker(highThresholdMarker); - Timber.d("showHighThresholdMarker() " + activeMarker.getId()); - } - - @Override - protected void onStart() { - super.onStart(); - mapView.onStart(); - } - - @Override - protected void onResume() { - super.onResume(); - mapView.onResume(); - } - - @Override - protected void onPause() { - super.onPause(); - mapView.onPause(); - } - - @Override - protected void onStop() { - super.onStop(); - mapView.onStop(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - mapView.onSaveInstanceState(outState); - } - - @Override - public void onLowMemory() { - super.onLowMemory(); - mapView.onLowMemory(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mapView.onDestroy(); - } -} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java index 8b238e49a8..50adeb2d74 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java @@ -274,7 +274,7 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView String json = GeoParseUtil.loadStringFromAssets(activity.getApplicationContext(), "points.geojson"); return GeoParseUtil.parseGeoJsonCoordinates(json); } catch (IOException | JSONException exception) { - Timber.e("Could not add markers,", exception); + Timber.e(exception, "Could not add markers"); return null; } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java index f2f82865d1..61ece0a94f 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java @@ -36,6 +36,7 @@ import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerViewOptions; import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerView; import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerViewOptions; +import java.util.Locale; import java.util.Random; /** @@ -150,7 +151,11 @@ public class MarkerViewActivity extends AppCompatActivity { public void onMapChanged(@MapView.MapChange int change) { if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) { if (!markerViewManager.getMarkerViewAdapters().isEmpty() && viewCountView != null) { - viewCountView.setText("ViewCache size " + markerViewContainer.getChildCount()); + viewCountView.setText(String.format( + Locale.getDefault(), + getString(R.string.viewcache_size), + markerViewContainer.getChildCount()) + ); } } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java index 266db3fdd1..848eab9a3c 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewsInRectangleActivity.java @@ -19,6 +19,9 @@ import java.util.List; import timber.log.Timber; +/** + * Test activity showcasing counting MarkerViews in a rectangle. + */ public class MarkerViewsInRectangleActivity extends AppCompatActivity implements OnMapReadyCallback, View.OnClickListener { @@ -53,7 +56,7 @@ public class MarkerViewsInRectangleActivity extends AppCompatActivity implements int top = selectionBox.getTop() - mapView.getTop(); int left = selectionBox.getLeft() - mapView.getLeft(); RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight()); - Timber.i(String.format("Querying box %s", box)); + Timber.i("Querying box %s", box); List<MarkerView> markers = mapboxMap.getMarkerViewsInRect(box); // Show count diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java index fecfe2a842..49c9663672 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java @@ -57,8 +57,6 @@ public class PolygonActivity extends AppCompatActivity implements OnMapReadyCall // configure inital map state MapboxMapOptions options = new MapboxMapOptions() .attributionTintColor(RED_COLOR) - // deprecated feature! - .textureMode(true) .compassFadesWhenFacingNorth(false) .styleUrl(Style.MAPBOX_STREETS) .camera(new CameraPosition.Builder() diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java new file mode 100644 index 0000000000..cc44ac9715 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java @@ -0,0 +1,174 @@ +package com.mapbox.mapboxsdk.testapp.activity.camera; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; +import android.os.Bundle; +import android.support.v4.view.animation.FastOutLinearInInterpolator; +import android.support.v4.view.animation.FastOutSlowInInterpolator; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.view.animation.AnticipateOvershootInterpolator; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing using Android SDK animators to animate camera position changes. + */ +public class CameraAnimatorActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final double ANIMATION_DELAY_FACTOR = 1.5; + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_camera_animator); + + mapView = (MapView) findViewById(R.id.mapView); + if (mapView != null) { + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + } + + @Override + public void onMapReady(final MapboxMap map) { + mapboxMap = map; + findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + view.setVisibility(View.GONE); + createAnimator(mapboxMap.getCameraPosition()).start(); + } + }); + } + + private Animator createAnimator(CameraPosition currentPosition) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.play(createLatLngAnimator(currentPosition.target)); + animatorSet.play(createZoomAnimator(currentPosition.zoom)); + animatorSet.play(createBearingAnimator(currentPosition.bearing)); + animatorSet.play(createTiltAnimator(currentPosition.tilt)); + return animatorSet; + } + + private Animator createLatLngAnimator(LatLng currentPosition) { + LatLng target = new LatLng(37.789992, -122.402214); + ValueAnimator latLngAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), currentPosition, target); + latLngAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR)); + latLngAnimator.setInterpolator(new FastOutSlowInInterpolator()); + latLngAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mapboxMap.setLatLng((LatLng) animation.getAnimatedValue()); + } + }); + return latLngAnimator; + } + + private Animator createZoomAnimator(double currentZoom) { + ValueAnimator zoomAnimator = ValueAnimator.ofFloat((float) currentZoom, 14.5f); + zoomAnimator.setDuration((long) (2200 * ANIMATION_DELAY_FACTOR)); + zoomAnimator.setStartDelay((long) (600 * ANIMATION_DELAY_FACTOR)); + zoomAnimator.setInterpolator(new AnticipateOvershootInterpolator()); + zoomAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mapboxMap.setZoom((Float) animation.getAnimatedValue()); + } + }); + return zoomAnimator; + } + + private Animator createBearingAnimator(double currentBearing) { + ValueAnimator bearingAnimator = ValueAnimator.ofFloat((float) currentBearing, 135); + bearingAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR)); + bearingAnimator.setStartDelay((long) (1000 * ANIMATION_DELAY_FACTOR)); + bearingAnimator.setInterpolator(new FastOutLinearInInterpolator()); + bearingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mapboxMap.setBearing((Float) animation.getAnimatedValue()); + } + }); + return bearingAnimator; + } + + private Animator createTiltAnimator(double currentTilt) { + ValueAnimator tiltAnimator = ValueAnimator.ofFloat((float) currentTilt, 60); + tiltAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR)); + tiltAnimator.setStartDelay((long) (1500 * ANIMATION_DELAY_FACTOR)); + tiltAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mapboxMap.setTilt((Float) animation.getAnimatedValue()); + } + }); + return tiltAnimator; + } + + private static class LatLngEvaluator implements TypeEvaluator<LatLng> { + + private final LatLng latLng = new LatLng(); + + @Override + public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { + latLng.setLatitude(startValue.getLatitude() + + ((endValue.getLatitude() - startValue.getLatitude()) * fraction)); + latLng.setLongitude(startValue.getLongitude() + + ((endValue.getLongitude() - startValue.getLongitude()) * fraction)); + return latLng; + } + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java index 10464b087a..2820bdbd53 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk.testapp.activity.camera; +import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; @@ -24,6 +25,9 @@ import com.mapbox.mapboxsdk.testapp.R; import timber.log.Timber; +/** + * Test activity showcasing how to listen to camera change events. + */ public class CameraPositionActivity extends AppCompatActivity implements OnMapReadyCallback { private MapView mapView; @@ -83,6 +87,7 @@ public class CameraPositionActivity extends AppCompatActivity implements OnMapRe fab = (FloatingActionButton) findViewById(R.id.fab); fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, R.color.primary)); fab.setOnClickListener(new View.OnClickListener() { + @SuppressLint("InflateParams") @Override public void onClick(View view) { Context context = view.getContext(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java index d81538f323..e3a9551b8e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.java @@ -1,75 +1,122 @@ package com.mapbox.mapboxsdk.testapp.activity.camera; import android.os.Bundle; -import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.design.widget.BottomSheetBehavior; import android.support.v7.app.AppCompatActivity; +import android.view.View; import com.mapbox.mapboxsdk.annotations.MarkerOptions; -import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.view.LockableBottomSheetBehavior; + +import java.util.ArrayList; +import java.util.List; /** * Test activity showcasing using the LatLngBounds camera API. - * <p> - * This activity opens the map at zoom level 0 and animates into a bounds set by Los Angeles and New York - * with some additional padding and an animation duration of 1500 ms. - * </p> */ -public class LatLngBoundsActivity extends AppCompatActivity implements OnMapReadyCallback { - - private static final LatLng LOS_ANGELES = new LatLng(34.053940, -118.242622); - private static final LatLng NEW_YORK = new LatLng(40.712730, -74.005953); - - private final LatLng CHINA_BOTTOM_LEFT = new LatLng(15.68169, 73.499857); - private final LatLng CHINA_TOP_RIGHT = new LatLng(53.560711, 134.77281); +public class LatLngBoundsActivity extends AppCompatActivity implements View.OnClickListener { + + private static final List<LatLng> LOCATIONS = new ArrayList<LatLng>() { + { + add(new LatLng(37.806866, -122.422502)); + add(new LatLng(37.812905, -122.477605)); + add(new LatLng(37.826944, -122.423188)); + add(new LatLng(37.752676, -122.447736)); + add(new LatLng(37.769305, -122.479322)); + add(new LatLng(37.749834, -122.417867)); + add(new LatLng(37.756149, -122.405679)); + add(new LatLng(37.751403, -122.387397)); + add(new LatLng(37.793064, -122.391517)); + add(new LatLng(37.769122, -122.427394)); + } + }; + private static final LatLngBounds BOUNDS = new LatLngBounds.Builder().includes(LOCATIONS).build(); + private static final int ANIMATION_DURATION_LONG = 450; + private static final int ANIMATION_DURATION_SHORT = 250; + private static final int BOUNDS_PADDING_DIVIDER_SMALL = 3; + private static final int BOUNDS_PADDING_DIVIDER_LARGE = 9; private MapView mapView; private MapboxMap mapboxMap; + private View bottomSheet; + private LockableBottomSheetBehavior bottomSheetBehavior; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_visible_bounds); - + setContentView(R.layout.activity_latlngbounds); mapView = (MapView) findViewById(R.id.mapView); - mapView.setStyleUrl(Style.DARK); mapView.onCreate(savedInstanceState); - mapView.getMapAsync(this); + mapView.getMapAsync(new OnMapReadyCallback() { + @Override + public void onMapReady(final MapboxMap map) { + mapboxMap = map; + initMap(); + } + }); + } + + private void initMap() { + addMarkers(); + initFab(); + initBottomSheet(); + moveToBounds(bottomSheet.getMeasuredHeight(), BOUNDS_PADDING_DIVIDER_SMALL, 0); + } + + private void addMarkers() { + for (LatLng location : LOCATIONS) { + mapboxMap.addMarker(new MarkerOptions().position(location)); + } + } + + private void initFab() { + findViewById(R.id.fab).setOnClickListener(this); } @Override - public void onMapReady(final MapboxMap map) { - mapboxMap = map; - moveToBounds(new LatLngBounds.Builder().include(NEW_YORK).include(LOS_ANGELES).build(), new int[] {0, 0, 0, 0}); - new Handler().postDelayed(new Runnable() { + public void onClick(View v) { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + v.animate().alpha(0.0f).setDuration(ANIMATION_DURATION_SHORT); + } + + private void initBottomSheet() { + bottomSheet = findViewById(R.id.bottom_sheet); + bottomSheetBehavior = (LockableBottomSheetBehavior) BottomSheetBehavior.from(bottomSheet); + bottomSheetBehavior.setLocked(true); + bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { + @Override + public void onStateChanged(@NonNull View bottomSheet, int newState) { + if (newState == BottomSheetBehavior.STATE_SETTLING) { + moveToBounds(0, BOUNDS_PADDING_DIVIDER_LARGE, ANIMATION_DURATION_LONG); + } + } + @Override - public void run() { - moveToBounds(new LatLngBounds.Builder().include(CHINA_BOTTOM_LEFT).include(CHINA_TOP_RIGHT).build(), - new int[] {100, 100, 100, 100 }); + public void onSlide(@NonNull View bottomSheet, float slideOffset) { + } - }, 5000); + }); } - private void moveToBounds(LatLngBounds latLngBounds, int[] padding) { - mapboxMap.clear(); - mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getNorthEast())); - mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getSouthEast())); - mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getSouthWest())); - mapboxMap.addMarker(new MarkerOptions().position(latLngBounds.getNorthWest())); - CameraUpdate update = - CameraUpdateFactory.newLatLngBounds(latLngBounds, - padding[0], - padding[1], - padding[2], - padding[3]); - mapboxMap.moveCamera(update); + private void moveToBounds(int verticalOffset, int boundsPaddingDivider, int duration) { + int paddingHorizontal = mapView.getMeasuredWidth() / boundsPaddingDivider; + int paddingVertical = (mapView.getMeasuredHeight() - verticalOffset) / boundsPaddingDivider; + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngBounds( + BOUNDS, + paddingHorizontal, + paddingVertical, + paddingHorizontal, + paddingVertical + verticalOffset), + duration + ); } @Override @@ -97,9 +144,9 @@ public class LatLngBoundsActivity extends AppCompatActivity implements OnMapRead } @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - mapView.onSaveInstanceState(outState); + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); } @Override @@ -109,8 +156,8 @@ public class LatLngBoundsActivity extends AppCompatActivity implements OnMapRead } @Override - public void onLowMemory() { - super.onLowMemory(); - mapView.onLowMemory(); + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java index 014743df96..53a5800f26 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java @@ -13,6 +13,9 @@ import com.mapbox.mapboxsdk.testapp.R; import timber.log.Timber; +/** + * Test activity showcasing using maximum and minimum zoom levels to restrict camera movement. + */ public class MaxMinZoomActivity extends AppCompatActivity implements OnMapReadyCallback { private MapView mapView; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java index dde22db2db..1b49e9e3d6 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java @@ -69,6 +69,7 @@ public class CustomLayerActivity extends AppCompatActivity { ExampleCustomLayer.createContext(), ExampleCustomLayer.InitializeFunction, ExampleCustomLayer.RenderFunction, + ExampleCustomLayer.ContextLostFunction, // Optional ExampleCustomLayer.DeinitializeFunction); mapboxMap.addLayerBelow(customLayer, "building"); fab.setImageResource(R.drawable.ic_layers_clear); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java index 9c031ad32a..20174daeaa 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java @@ -48,7 +48,7 @@ public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity { int top = selectionBox.getTop() - mapView.getTop(); int left = selectionBox.getLeft() - mapView.getLeft(); RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight()); - Timber.i(String.format("Querying box %s", box)); + Timber.i("Querying box %s", box); List<Feature> features = mapboxMap.queryRenderedFeatures(box); // Show count @@ -66,22 +66,21 @@ public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity { } private void debugOutput(List<Feature> features) { - Timber.i(String.format("Got %s features", features.size())); + Timber.i("Got %s features", features.size()); for (Feature feature : features) { if (feature != null) { - Timber.i(String.format("Got feature %s with %s properties and Geometry %s", + Timber.i("Got feature %s with %s properties and Geometry %s", feature.getId(), feature.getProperties() != null ? feature.getProperties().entrySet().size() : "<null>", - feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>") + feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>" ); if (feature.getProperties() != null) { for (Map.Entry<String, JsonElement> entry : feature.getProperties().entrySet()) { - Timber.i(String.format("Prop %s - %s", entry.getKey(), entry.getValue())); + Timber.i("Prop %s - %s", entry.getKey(), entry.getValue()); } } } else { - // TODO Question: Why not formatting here?? - Timber.i("Got NULL feature %s"); + Timber.i("Got 0 features"); } } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java index 1d15efef84..480e48437a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java @@ -46,6 +46,15 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity @Override public void onMapReady(final MapboxMap mapboxMap) { QueryRenderedFeaturesBoxHighlightActivity.this.mapboxMap = mapboxMap; + + // Add layer / source + final GeoJsonSource source = new GeoJsonSource("highlighted-shapes-source"); + mapboxMap.addSource(source); + mapboxMap.addLayer( + new FillLayer("highlighted-shapes-layer", "highlighted-shapes-source") + .withProperties(fillColor(Color.RED)) + ); + selectionBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -53,7 +62,7 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity int top = selectionBox.getTop() - mapView.getTop(); int left = selectionBox.getLeft() - mapView.getLeft(); RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight()); - Timber.i(String.format("Querying box %s for buildings", box)); + Timber.i("Querying box %s for buildings", box); List<Feature> features = mapboxMap.queryRenderedFeatures(box, Filter.lt("height", 10), "building"); // Show count @@ -62,17 +71,8 @@ public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity String.format("%s features in box", features.size()), Toast.LENGTH_SHORT).show(); - // remove layer / source if already added - mapboxMap.removeSource("highlighted-shapes-source"); - mapboxMap.removeLayer("highlighted-shapes-layer"); - - // Add layer / source - mapboxMap.addSource( - new GeoJsonSource("highlighted-shapes-source", - FeatureCollection.fromFeatures(features)) - ); - mapboxMap.addLayer(new FillLayer("highlighted-shapes-layer", "highlighted-shapes-source") - .withProperties(fillColor(Color.RED))); + // Update source data + source.setGeoJson(FeatureCollection.fromFeatures(features)); } }); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java index 8c2f3a8fb5..220137d07d 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java @@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.testapp.activity.feature; import android.graphics.BitmapFactory; import android.graphics.RectF; import android.os.Bundle; -import android.support.annotation.RawRes; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; @@ -14,15 +13,10 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; import com.mapbox.services.commons.geojson.Feature; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; import java.util.List; import timber.log.Timber; @@ -57,9 +51,10 @@ public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivi // Add a symbol layer (also works with annotations) try { - mapboxMap.addSource(new GeoJsonSource("symbols-source", readRawResource(R.raw.test_points_utrecht))); + mapboxMap.addSource(new GeoJsonSource("symbols-source", ResourceUtils.readRawResource( + QueryRenderedFeaturesBoxSymbolCountActivity.this, R.raw.test_points_utrecht))); } catch (IOException ioException) { - Timber.e("Could not load geojson: " + ioException.getMessage()); + Timber.e(ioException, "Could not load geojson"); return; } mapboxMap.addImage( @@ -76,7 +71,7 @@ public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivi int top = selectionBox.getTop() - mapView.getTop(); int left = selectionBox.getLeft() - mapView.getLeft(); RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight()); - Timber.i(String.format("Querying box %s", box)); + Timber.i("Querying box %s", box); List<Feature> features = mapboxMap.queryRenderedFeatures(box, "symbols-layer"); // Show count @@ -94,23 +89,6 @@ public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivi }); } - private String readRawResource(@RawRes int rawResource) throws IOException { - InputStream is = getResources().openRawResource(rawResource); - Writer writer = new StringWriter(); - char[] buffer = new char[1024]; - try { - Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); - int numRead; - while ((numRead = reader.read(buffer)) != -1) { - writer.write(buffer, 0, numRead); - } - } finally { - is.close(); - } - - return writer.toString(); - } - public MapboxMap getMapboxMap() { return mapboxMap; } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java index 8b83db3069..1bd6a34b7c 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java @@ -59,9 +59,9 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity { public void onMapClick(@NonNull LatLng point) { // Query final PointF pixel = mapboxMap.getProjection().toScreenLocation(point); - Timber.i(String.format( + Timber.i( "Requesting features for %sx%s (%sx%s adjusted for density)", - pixel.x, pixel.y, pixel.x / density, pixel.y / density) + pixel.x, pixel.y, pixel.x / density, pixel.y / density ); List<Feature> features = mapboxMap.queryRenderedFeatures(pixel); @@ -84,22 +84,21 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity { } private void debugOutput(List<Feature> features) { - Timber.i(String.format("Got %s features", features.size())); + Timber.i("Got %s features", features.size()); for (Feature feature : features) { if (feature != null) { - Timber.i(String.format("Got feature %s with %s properties and Geometry %s", + Timber.i("Got feature %s with %s properties and Geometry %s", feature.getId(), feature.getProperties() != null ? feature.getProperties().entrySet().size() : "<null>", - feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>") + feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>" ); if (feature.getProperties() != null) { for (Map.Entry<String, JsonElement> entry : feature.getProperties().entrySet()) { - Timber.i(String.format("Prop %s - %s", entry.getKey(), entry.getValue())); + Timber.i("Prop %s - %s", entry.getKey(), entry.getValue()); } } } else { - // TODO Question: Why not formatting here?? - Timber.i("Got NULL feature %s"); + Timber.i("Got NULL feature"); } } } @@ -185,7 +184,7 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity { private final List<Feature> features; - public CustomMarker(BaseMarkerOptions baseMarkerOptions, List<Feature> features) { + CustomMarker(BaseMarkerOptions baseMarkerOptions, List<Feature> features) { super(baseMarkerOptions); this.features = features; } @@ -201,7 +200,7 @@ public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity { return this; } - public CustomMarkerOptions() { + CustomMarkerOptions() { } private CustomMarkerOptions(Parcel in) { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java index ee16b251c5..4f828060ee 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java @@ -50,7 +50,7 @@ public class SupportMapFragmentActivity extends AppCompatActivity implements OnM .zoom(11) .build()); - mapFragment = SupportMapFragment.newInstance(); + mapFragment = SupportMapFragment.newInstance(options); transaction.add(R.id.fragment_container, mapFragment, "com.mapbox.map"); transaction.commit(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java index 8025832429..bbdf450505 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.java @@ -31,11 +31,11 @@ public class ViewPagerActivity extends AppCompatActivity { } } - public static class MapFragmentAdapter extends FragmentPagerAdapter { + static class MapFragmentAdapter extends FragmentPagerAdapter { private static int NUM_ITEMS = 3; - public MapFragmentAdapter(FragmentManager fragmentManager) { + MapFragmentAdapter(FragmentManager fragmentManager) { super(fragmentManager); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java index 2be47b4e25..655012f799 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java @@ -15,6 +15,8 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.testapp.R; +import java.util.Locale; + /** * Test activity showcasing the Snapshot API to create and display a bitmap of the current shown Map. */ @@ -56,7 +58,7 @@ public class SnapshotActivity extends AppCompatActivity implements OnMapReadyCal snapshotView.setImageBitmap(snapshot); Toast.makeText( SnapshotActivity.this, - String.format("Snapshot taken in %d ms", duration), + String.format(Locale.getDefault(), "Snapshot taken in %d ms", duration), Toast.LENGTH_LONG).show(); } }); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java index 18db2ba8a8..7fa4792a38 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java @@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.testapp.activity.infowindow; import android.graphics.Color; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.v4.content.res.ResourcesCompat; import android.support.v7.app.AppCompatActivity; import android.view.View; @@ -21,16 +20,18 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.utils.IconUtils; +import java.util.Locale; + /** * Test activity showcasing how to dynamically update InfoWindow when Using an MapboxMap.InfoWindowAdapter. */ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implements OnMapReadyCallback { + private static final LatLng PARIS = new LatLng(48.864716, 2.349014); + private MapboxMap mapboxMap; private MapView mapView; - private LatLng paris = new LatLng(48.864716, 2.349014); - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -43,7 +44,6 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen @Override public void onMapReady(MapboxMap map) { - mapboxMap = map; // Add info window adapter @@ -64,27 +64,32 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen double distanceKm = marker.getPosition().distanceTo(point) / 1000; // Get the info window - InfoWindow infoWindow = marker.getInfoWindow(); + final InfoWindow infoWindow = marker.getInfoWindow(); // Get the view from the info window if (infoWindow != null && infoWindow.getView() != null) { // Set the new text on the text view in the info window - ((TextView) infoWindow.getView()).setText(String.format("%.2fkm", distanceKm)); - - // Update the info window position (as the text length changes) - infoWindow.update(); + TextView textView = (TextView) infoWindow.getView(); + textView.setText(String.format(Locale.getDefault(), "%.2fkm", distanceKm)); + textView.post(new Runnable() { + @Override + public void run() { + // Update the info window position (as the text length changes) + infoWindow.update(); + } + }); } } }); // Focus on Paris - mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(paris)); + mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(PARIS)); } private MarkerView addMarker(MapboxMap mapboxMap) { return mapboxMap.addMarker( new MarkerViewOptions() - .position(paris) + .position(PARIS) .icon(IconUtils.drawableToIcon(this, R.drawable.ic_location_city, ResourcesCompat.getColor(getResources(), R.color.mapbox_blue, getTheme())) )); @@ -94,15 +99,13 @@ public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implemen final int padding = (int) getResources().getDimension(R.dimen.attr_margin); mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() { - @Nullable @Override public View getInfoWindow(@NonNull Marker marker) { TextView textView = new TextView(DynamicInfoWindowAdapterActivity.this); textView.setText(marker.getTitle()); textView.setBackgroundColor(Color.WHITE); - textView.setText("Click the map to calculate the distance"); + textView.setText(R.string.action_calculate_distance); textView.setPadding(padding, padding, padding, padding); - return textView; } }); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java index 7026a797d5..22347f8a92 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java @@ -18,6 +18,9 @@ import com.mapbox.mapboxsdk.testapp.model.annotations.CityStateMarker; import com.mapbox.mapboxsdk.testapp.model.annotations.CityStateMarkerOptions; import com.mapbox.mapboxsdk.testapp.utils.IconUtils; +/** + * Test activity showcasing using an InfoWindowAdapter to provide a custom InfoWindow content. + */ public class InfoWindowAdapterActivity extends AppCompatActivity { private MapView mapView; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java new file mode 100644 index 0000000000..2c83f6d908 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java @@ -0,0 +1,288 @@ +package com.mapbox.mapboxsdk.testapp.activity.maplayout; + +import android.content.Context; +import android.location.Location; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.BottomSheetBehavior; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.MapboxMapOptions; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.utils.MapFragmentUtils; + +/** + * Test activity showcasing using a bottomView with a MapView and stacking map fragments below. + */ +public class BottomSheetActivity extends AppCompatActivity { + + private static final String TAG_MAIN_FRAGMENT = "com.mapbox.mapboxsdk.fragment.tag.main"; + private static final String TAG_BOTTOM_FRAGMENT = "com.mapbox.mapboxsdk.fragment.tag.bottom"; + + private boolean bottomSheetFragmentAdded; + private int mapViewCounter; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_bottom_sheet); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); + } + + findViewById(R.id.fabFragment).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + addMapFragment(); + } + }); + + findViewById(R.id.fabBottomSheet).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toggleBottomSheetMapFragment(); + } + }); + + BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet)); + bottomSheetBehavior.setPeekHeight((int) (64 * getResources().getDisplayMetrics().density)); + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + toggleBottomSheetMapFragment(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + if (mapViewCounter > 0) { + mapViewCounter--; + Toast.makeText(this, "Amount of main map fragments: " + mapViewCounter, Toast.LENGTH_SHORT).show(); + } + } + + private void addMapFragment() { + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + MainMapFragment mainMapFragment = MainMapFragment.newInstance(mapViewCounter); + if (mapViewCounter == 0) { + fragmentTransaction.add(R.id.fragment_container, mainMapFragment, TAG_MAIN_FRAGMENT); + } else { + fragmentTransaction.replace(R.id.fragment_container, mainMapFragment, TAG_MAIN_FRAGMENT); + } + fragmentTransaction.addToBackStack(null); + fragmentTransaction.commit(); + mapViewCounter++; + Toast.makeText(this, "Amount of main map fragments: " + mapViewCounter, Toast.LENGTH_SHORT).show(); + } + + private void toggleBottomSheetMapFragment() { + if (!bottomSheetFragmentAdded) { + addBottomSheetMapFragment(); + } else { + removeBottomSheetFragment(); + } + bottomSheetFragmentAdded = !bottomSheetFragmentAdded; + } + + private void addBottomSheetMapFragment() { + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + fragmentTransaction.add(R.id.fragment_container_bottom, BottomSheetFragment.newInstance(), TAG_BOTTOM_FRAGMENT); + fragmentTransaction.commit(); + } + + private void removeBottomSheetFragment() { + Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_BOTTOM_FRAGMENT); + if (fragment != null) { + getSupportFragmentManager().beginTransaction().remove(fragment).commit(); + } + } + + public static class MainMapFragment extends Fragment implements OnMapReadyCallback { + + private static final String[] STYLES = new String[] { + Style.MAPBOX_STREETS, + Style.SATELLITE_STREETS, + Style.LIGHT, + Style.DARK, + Style.SATELLITE, + Style.OUTDOORS + }; + + private MapView map; + + public static MainMapFragment newInstance(int mapCounter) { + MainMapFragment mapFragment = new MainMapFragment(); + MapboxMapOptions mapboxMapOptions = new MapboxMapOptions(); + mapboxMapOptions.styleUrl(STYLES[Math.min(Math.max(mapCounter, 0), STYLES.length - 1)]); + mapFragment.setArguments(MapFragmentUtils.createFragmentArgs(mapboxMapOptions)); + return mapFragment; + } + + @Override + 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())); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + map.onCreate(savedInstanceState); + map.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + Location location = mapboxMap.getMyLocation(); + if (location != null) { + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 15)); + } + } + + @Override + public void onStart() { + super.onStart(); + map.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + map.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + map.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + map.onStop(); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + map.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + map.onLowMemory(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + map.onDestroy(); + } + } + + public static class BottomSheetFragment extends Fragment implements OnMapReadyCallback { + + private MapView map; + + public static BottomSheetFragment newInstance() { + BottomSheetFragment mapFragment = new BottomSheetFragment(); + MapboxMapOptions mapboxMapOptions = new MapboxMapOptions(); + mapboxMapOptions.locationEnabled(true); + mapboxMapOptions.renderSurfaceOnTop(true); + mapboxMapOptions.styleUrl(Style.LIGHT); + mapFragment.setArguments(MapFragmentUtils.createFragmentArgs(mapboxMapOptions)); + return mapFragment; + } + + @Override + 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())); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + map.onCreate(savedInstanceState); + map.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + Location location = mapboxMap.getMyLocation(); + if (location != null) { + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 15)); + } + } + + @Override + public void onStart() { + super.onStart(); + map.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + map.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + map.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + map.onStop(); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + map.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + map.onLowMemory(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + map.onDestroy(); + } + } +} 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 18d6fadcb8..6134b4cb7a 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 @@ -1,10 +1,19 @@ package com.mapbox.mapboxsdk.testapp.activity.maplayout; +import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.ListView; import android.widget.TextView; import com.mapbox.mapboxsdk.camera.CameraPosition; @@ -12,18 +21,25 @@ import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.layers.Property; import com.mapbox.mapboxsdk.testapp.R; +import java.util.List; +import java.util.Locale; + import timber.log.Timber; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; + /** - * Test Activity showcasing the different debug modes and allows to cycle between the default map styles. + * Test activity showcasing the different debug modes and allows to cycle between the default map styles. */ -public class DebugModeActivity extends AppCompatActivity { +public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCallback { private MapView mapView; private MapboxMap mapboxMap; - + private ActionBarDrawerToggle actionBarDrawerToggle; private int currentStyleIndex = 0; private static final String[] STYLES = new String[] { @@ -32,49 +48,128 @@ public class DebugModeActivity extends AppCompatActivity { Style.LIGHT, Style.DARK, Style.SATELLITE, - Style.SATELLITE_STREETS + Style.SATELLITE_STREETS, + Style.TRAFFIC_DAY, + Style.TRAFFIC_NIGHT }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_debug_mode); + setupToolbar(); + setupMapView(savedInstanceState); + setupDebugChangeView(); + setupStyleChangeView(); + } + + private void setupToolbar() { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + + DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + actionBarDrawerToggle = new ActionBarDrawerToggle(this, + drawerLayout, + R.string.navigation_drawer_open, + R.string.navigation_drawer_close + ); + actionBarDrawerToggle.setDrawerIndicatorEnabled(true); + actionBarDrawerToggle.syncState(); + } + } + private void setupMapView(Bundle savedInstanceState) { mapView = (MapView) findViewById(R.id.mapView); + mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { + @Override + public void onMapChanged(int change) { + if (change == MapView.DID_FINISH_LOADING_STYLE && mapboxMap != null) { + Timber.v("New style loaded with JSON: %s", mapboxMap.getStyleJson()); + setupNavigationView(mapboxMap.getLayers()); + } + } + }); + mapView.setTag(true); mapView.setStyleUrl(STYLES[currentStyleIndex]); mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } - mapView.getMapAsync(new OnMapReadyCallback() { - @Override - public void onMapReady(@NonNull MapboxMap map) { - mapboxMap = map; + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + mapboxMap.getUiSettings().setZoomControlsEnabled(true); - mapboxMap.getUiSettings().setZoomControlsEnabled(true); + setupNavigationView(mapboxMap.getLayers()); + setupZoomView(); + setFpsView(); + } - // show current zoom level on screen - final TextView textView = (TextView) findViewById(R.id.textZoom); - mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() { - @Override - public void onCameraChange(CameraPosition position) { - textView.setText(String.format(getString(R.string.debug_zoom), position.zoom)); - } - }); + private void setFpsView() { + final TextView fpsView = (TextView) findViewById(R.id.fpsView); + mapboxMap.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() { + @Override + public void onFpsChanged(double fps) { + fpsView.setText(String.format(Locale.US,"FPS: %4.2f", fps)); } }); + } + + private void setupNavigationView(List<Layer> layerList) { + final LayerListAdapter adapter = new LayerListAdapter(this, layerList); + ListView listView = (ListView) findViewById(R.id.listView); + listView.setAdapter(adapter); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + Layer clickedLayer = adapter.getItem(position); + toggleLayerVisibility(clickedLayer); + closeNavigationView(); + } + }); + } + private void toggleLayerVisibility(Layer layer) { + boolean isVisible = layer.getVisibility().getValue().equals(Property.VISIBLE); + layer.setProperties( + visibility( + isVisible ? Property.NONE : Property.VISIBLE + ) + ); + } + + private void closeNavigationView() { + DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + drawerLayout.closeDrawers(); + } + + private void setupZoomView() { + final TextView textView = (TextView) findViewById(R.id.textZoom); + mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() { + @Override + public void onCameraChange(CameraPosition position) { + textView.setText(String.format(getString(R.string.debug_zoom), position.zoom)); + } + }); + } + private void setupDebugChangeView() { FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabDebug); fabDebug.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mapboxMap != null) { - Timber.d("Debug FAB: isDebug Active? " + mapboxMap.isDebugActive()); + Timber.d("Debug FAB: isDebug Active? %s", mapboxMap.isDebugActive()); mapboxMap.cycleDebugOptions(); } } }); + } + private void setupStyleChangeView() { FloatingActionButton fabStyles = (FloatingActionButton) findViewById(R.id.fabStyles); fabStyles.setOnClickListener(new View.OnClickListener() { @Override @@ -96,6 +191,11 @@ public class DebugModeActivity extends AppCompatActivity { } @Override + public boolean onOptionsItemSelected(MenuItem item) { + return actionBarDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item); + } + + @Override protected void onStart() { super.onStart(); mapView.onStart(); @@ -136,4 +236,58 @@ public class DebugModeActivity extends AppCompatActivity { super.onLowMemory(); mapView.onLowMemory(); } + + private static class LayerListAdapter extends BaseAdapter { + + private LayoutInflater layoutInflater; + private List<Layer> layers; + + LayerListAdapter(Context context, List<Layer> layers) { + this.layoutInflater = LayoutInflater.from(context); + this.layers = layers; + } + + @Override + public int getCount() { + return layers.size(); + } + + @Override + public Layer getItem(int position) { + return layers.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Layer layer = layers.get(position); + View view = convertView; + if (view == null) { + view = layoutInflater.inflate(android.R.layout.simple_list_item_2, parent, false); + ViewHolder holder = new ViewHolder( + (TextView) view.findViewById(android.R.id.text1), + (TextView) view.findViewById(android.R.id.text2) + ); + view.setTag(holder); + } + ViewHolder holder = (ViewHolder) view.getTag(); + holder.text.setText(layer.getClass().getSimpleName()); + holder.subText.setText(layer.getId()); + return view; + } + + private static class ViewHolder { + final TextView text; + final TextView subText; + + ViewHolder(TextView text, TextView subText) { + this.text = text; + this.subText = subText; + } + } + } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java index 7ec9cb9242..462c0c8025 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java @@ -8,7 +8,6 @@ import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; -import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; @@ -23,6 +22,12 @@ import com.mapbox.mapboxsdk.maps.TrackingSettings; import com.mapbox.mapboxsdk.maps.UiSettings; import com.mapbox.mapboxsdk.testapp.R; +/** + * Test activity showcasing having 2 maps on top of each other. + * <p> + * The small map is using the `mapbox_enableZMediaOverlay="true"` configuration + * </p> + */ public class DoubleMapActivity extends AppCompatActivity { private static final String TAG_FRAGMENT = "map"; @@ -45,11 +50,11 @@ public class DoubleMapActivity extends AppCompatActivity { public void setMapboxMap(MapboxMap map) { // we need to set mapboxmap on the parent activity, // for auto-generated ui tests - mapboxMap = map; mapboxMap.setStyleUrl(Style.DARK); mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(18)); try { + mapboxMap.setMyLocationEnabled(true); TrackingSettings settings = mapboxMap.getTrackingSettings(); settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW); } catch (SecurityException securityException) { @@ -58,6 +63,9 @@ public class DoubleMapActivity extends AppCompatActivity { } } + /** + * Custom fragment containing 2 MapViews. + */ public static class DoubleMapFragment extends Fragment { private DoubleMapActivity activity; @@ -107,6 +115,7 @@ public class DoubleMapActivity extends AppCompatActivity { uiSettings.setLogoEnabled(false); try { + mapboxMap.setMyLocationEnabled(true); TrackingSettings settings = mapboxMap.getTrackingSettings(); settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW); } catch (SecurityException securityException) { @@ -123,9 +132,6 @@ public class DoubleMapActivity extends AppCompatActivity { }); } }); - - SurfaceView surfaceViewMini = (SurfaceView) mapViewMini.findViewById(R.id.surfaceView); - surfaceViewMini.setZOrderMediaOverlay(true); } @Override @@ -157,8 +163,8 @@ public class DoubleMapActivity extends AppCompatActivity { } @Override - public void onDestroy() { - super.onDestroy(); + public void onDestroyView() { + super.onDestroyView(); mapView.onDestroy(); mapViewMini.onDestroy(); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java index 9ac87deb0d..24a167e260 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java @@ -16,7 +16,7 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.testapp.R; /** - * Test Activity showcasing restricting user gestures to a bounds around Iceland. + * Test activity showcasing restricting user gestures to a bounds around Iceland. */ public class LatLngBoundsForCameraActivity extends AppCompatActivity implements OnMapReadyCallback { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java new file mode 100644 index 0000000000..32344248bc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java @@ -0,0 +1,111 @@ +package com.mapbox.mapboxsdk.testapp.activity.maplayout; + +import android.os.Bundle; +import android.support.v4.util.LongSparseArray; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; + +import timber.log.Timber; + +/** + * Test activity showcasing how to listen to map change events. + */ +public class MapChangeActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_simple); + + final LongSparseArray<String> mapChangeMap = buildMapChangeStringValueSparseArray(); + mapView = (MapView) findViewById(R.id.mapView); + mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { + @Override + public void onMapChanged(int change) { + Timber.e("OnMapChange: %s, %s", change, mapChangeMap.get(change)); + } + }); + + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(new OnMapReadyCallback() { + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom( + new LatLng(55.754020, 37.620948), 12), 9000); + } + }); + } + + private LongSparseArray<String> buildMapChangeStringValueSparseArray() { + LongSparseArray<String> mapChangeArray = new LongSparseArray<>(); + mapChangeArray.put(MapView.REGION_WILL_CHANGE, "Region will change"); + mapChangeArray.put(MapView.REGION_WILL_CHANGE_ANIMATED, "Region will change animated"); + mapChangeArray.put(MapView.REGION_IS_CHANGING, "Region is changing"); + mapChangeArray.put(MapView.REGION_DID_CHANGE, "Region did change"); + mapChangeArray.put(MapView.REGION_DID_CHANGE_ANIMATED, "Region did change animated"); + mapChangeArray.put(MapView.WILL_START_LOADING_MAP, "Will start loading map"); + mapChangeArray.put(MapView.DID_FINISH_LOADING_MAP, "Did finish loading map"); + mapChangeArray.put(MapView.DID_FAIL_LOADING_MAP, "Did fail loading map"); + mapChangeArray.put(MapView.WILL_START_RENDERING_FRAME, "Will start rendering frame"); + mapChangeArray.put(MapView.DID_FINISH_RENDERING_FRAME, "Did finish rendering frame"); + mapChangeArray.put(MapView.DID_FINISH_RENDERING_FRAME_FULLY_RENDERED, "Did finish rendering frame fully rendered"); + mapChangeArray.put(MapView.WILL_START_RENDERING_MAP, "Will start rendering map"); + mapChangeArray.put(MapView.DID_FINISH_RENDERING_MAP, "Did finish rendering map"); + mapChangeArray.put(MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED, "Did finish rendering map fully rendered"); + mapChangeArray.put(MapView.DID_FINISH_LOADING_STYLE, "Did finish loading style"); + mapChangeArray.put(MapView.SOURCE_DID_CHANGE, "Source did change"); + return mapChangeArray; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java index 495360a168..f0827c65cc 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java @@ -1,6 +1,8 @@ package com.mapbox.mapboxsdk.testapp.activity.maplayout; +import android.app.Dialog; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentManager; @@ -65,6 +67,22 @@ public class MapInDialogActivity extends AppCompatActivity { mapView.onCreate(savedInstanceState); } + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new Dialog(getActivity(), getTheme()) { + boolean destroyed = false; + @Override + public void dismiss() { + if (mapView != null && !destroyed) { + mapView.onDestroy(); + destroyed = true; + } + super.dismiss(); + } + }; + } + @Override public void onStart() { super.onStart(); @@ -90,12 +108,6 @@ public class MapInDialogActivity extends AppCompatActivity { } @Override - public void onDestroy() { - super.onDestroy(); - mapView.onDestroy(); - } - - @Override public void onLowMemory() { super.onLowMemory(); mapView.onLowMemory(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java deleted file mode 100644 index 888482b219..0000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/NavigationDrawerActivity.java +++ /dev/null @@ -1,252 +0,0 @@ -package com.mapbox.mapboxsdk.testapp.activity.maplayout; - -import android.app.Activity; -import android.app.Fragment; -import android.app.FragmentManager; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.design.widget.Snackbar; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ListView; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.constants.Style; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapFragment; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.MapboxMapOptions; -import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; -import com.mapbox.mapboxsdk.testapp.R; - -public class NavigationDrawerActivity extends AppCompatActivity { - - private boolean firstStyle = true; - private MapboxMap mapboxMap; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_navigation_drawer); - - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - NavigationDrawerFragment navigationDrawerFragment; - - getFragmentManager() - .beginTransaction() - .add(R.id.navigation_drawer, navigationDrawerFragment = new NavigationDrawerFragment()) - .commit(); - - navigationDrawerFragment.setUp(this, - R.id.navigation_drawer, - (DrawerLayout) findViewById(R.id.drawer_layout), - getSupportActionBar()); - } - - public void onNavigationDrawerItemSelected(int position) { - // update the main content by replacing fragments - switch (position) { - case 0: - // options - MapboxMapOptions options = new MapboxMapOptions(); - options.styleUrl(firstStyle ? Style.LIGHT : Style.SATELLITE); - options.camera(new CameraPosition.Builder() - .target(new LatLng(39.913271, 116.413891)) - .zoom(12) - .build()); - - // fragment - MapFragment mapFragment = MapFragment.newInstance(options); - FragmentManager fragmentManager = getFragmentManager(); - fragmentManager.beginTransaction() - .replace(R.id.container, mapFragment) - .commit(); - - // get callback when map is ready - mapFragment.getMapAsync(new OnMapReadyCallback() { - @Override - public void onMapReady(MapboxMap map) { - mapboxMap = map; - } - }); - - firstStyle = !firstStyle; - break; - case 1: - Snackbar.make( - findViewById(android.R.id.content), - "Hello from snackbar", - Snackbar.LENGTH_LONG) - .show(); - break; - } - } - - public static class NavigationDrawerFragment extends Fragment { - - private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position"; - private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; - - private ActionBarDrawerToggle drawerToggle; - - private DrawerLayout drawerLayout; - private ListView drawerListView; - private View fragmentContainerView; - - private int currentSelectedPosition = 0; - private boolean fromSavedInstanceState; - private boolean userLearnedDrawer; - - public NavigationDrawerFragment() { - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); - userLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false); - - if (savedInstanceState != null) { - currentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION); - fromSavedInstanceState = true; - } - selectItem(currentSelectedPosition); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - setHasOptionsMenu(true); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - drawerListView = (ListView) inflater.inflate( - R.layout.drawer_navigation_drawer, container, false); - drawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - selectItem(position); - } - }); - drawerListView.setAdapter(new ArrayAdapter<>( - inflater.getContext(), - android.R.layout.simple_list_item_activated_1, - android.R.id.text1, - new String[] { - "Different style", - "Show snackbar" - })); - drawerListView.setItemChecked(currentSelectedPosition, true); - return drawerListView; - } - - /** - * Users of this fragment must call this method to set up the navigation drawer interactions. - * - * @param fragmentId The android:id of this fragment in its activity's layout. - * @param drawerLayout The DrawerLayout containing this fragment's UI. - */ - public void setUp(Activity activity, int fragmentId, DrawerLayout drawerLayout, ActionBar actionBar) { - fragmentContainerView = activity.findViewById(fragmentId); - this.drawerLayout = drawerLayout; - // drawerLayout.setScrimColor(Color.TRANSPARENT); - - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - - drawerToggle = new ActionBarDrawerToggle( - activity, - NavigationDrawerFragment.this.drawerLayout, - R.string.navigation_drawer_open, - R.string.navigation_drawer_close - ) { - @Override - public void onDrawerClosed(View drawerView) { - super.onDrawerClosed(drawerView); - if (!isAdded()) { - return; - } - getActivity().invalidateOptionsMenu(); - } - - @Override - public void onDrawerOpened(View drawerView) { - super.onDrawerOpened(drawerView); - if (!isAdded()) { - return; - } - - if (!userLearnedDrawer) { - userLearnedDrawer = true; - SharedPreferences sp = PreferenceManager - .getDefaultSharedPreferences(getActivity()); - sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply(); - } - getActivity().invalidateOptionsMenu(); - } - }; - - if (!userLearnedDrawer && !fromSavedInstanceState) { - this.drawerLayout.openDrawer(fragmentContainerView); - } - this.drawerLayout.post(new Runnable() { - @Override - public void run() { - drawerToggle.syncState(); - } - }); - this.drawerLayout.setDrawerListener(drawerToggle); - } - - private void selectItem(int position) { - currentSelectedPosition = position; - if (drawerListView != null) { - drawerListView.setItemChecked(position, true); - } - if (drawerLayout != null) { - drawerLayout.closeDrawer(fragmentContainerView); - } - - Activity activity = getActivity(); - if (activity != null && activity instanceof NavigationDrawerActivity) { - NavigationDrawerActivity navActivity = (NavigationDrawerActivity) activity; - navActivity.onNavigationDrawerItemSelected(position); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putInt(STATE_SELECTED_POSITION, currentSelectedPosition); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - drawerToggle.onConfigurationChanged(newConfig); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (drawerToggle.onOptionsItemSelected(item)) { - return true; - } - return super.onOptionsItemSelected(item); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java index badb6718cf..8f8a5af3cc 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java @@ -1,18 +1,11 @@ package com.mapbox.mapboxsdk.testapp.activity.maplayout; import android.os.Bundle; -import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; -import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; -import com.mapbox.mapboxsdk.maps.Projection; import com.mapbox.mapboxsdk.testapp.R; -import timber.log.Timber; - /** * Test activity showcasing a simple MapView without any MapboxMap interaction. */ @@ -27,19 +20,6 @@ public class SimpleMapActivity extends AppCompatActivity { mapView = (MapView) findViewById(R.id.mapView); mapView.onCreate(savedInstanceState); - mapView.getMapAsync(new OnMapReadyCallback() { - @Override - public void onMapReady(MapboxMap mapboxMap) { - final Projection projection = mapboxMap.getProjection(); - - mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() { - @Override - public void onMapClick(@NonNull LatLng point) { - Timber.e(projection.getVisibleRegion().toString()); - } - }); - } - }); } @Override diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java new file mode 100644 index 0000000000..393abb7cd4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java @@ -0,0 +1,146 @@ +package com.mapbox.mapboxsdk.testapp.activity.maplayout; + +import android.os.Bundle; +import android.os.Handler; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing visibility changes to the mapview. + */ +public class VisibilityChangeActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + private Handler handler = new Handler(); + private Runnable runnable; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_visibility); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(new OnMapReadyCallback() { + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom( + new LatLng(55.754020, 37.620948), 12), 9000); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + handler.post(runnable = new VisibilityRunner(mapView, findViewById(R.id.viewParent), handler)); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + private static class VisibilityRunner implements Runnable { + + private MapView mapView; + private View viewParent; + private Handler handler; + private int currentStep; + + VisibilityRunner(MapView mapView, View viewParent, Handler handler) { + this.mapView = mapView; + this.viewParent = viewParent; + this.handler = handler; + } + + @Override + public void run() { + if (isViewHiearchyReady()) { + if (isEvenStep()) { + viewParent.setVisibility(View.VISIBLE); + mapView.setVisibility(View.VISIBLE); + } else if (isFirstOrThirdStep()) { + mapView.setVisibility(getVisibilityForStep()); + } else if (isFifthOrSeventhStep()) { + viewParent.setVisibility(getVisibilityForStep()); + } + updateStep(); + } + handler.postDelayed(this, 1500); + } + + private void updateStep() { + if (currentStep == 7) { + currentStep = 0; + } else { + currentStep++; + } + } + + private int getVisibilityForStep() { + return (currentStep == 1 || currentStep == 5) ? View.GONE : View.INVISIBLE; + } + + private boolean isFifthOrSeventhStep() { + return currentStep == 5 || currentStep == 7; + } + + private boolean isFirstOrThirdStep() { + return currentStep == 1 || currentStep == 3; + } + + private boolean isEvenStep() { + return currentStep == 0 || currentStep % 2 == 0; + } + + private boolean isViewHiearchyReady() { + return mapView != null && viewParent != null; + } + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + if (runnable != null) { + handler.removeCallbacks(runnable); + runnable = null; + } + mapView.onStop(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java index 344e9e140a..5bffd4d930 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java @@ -12,7 +12,6 @@ import android.widget.Toast; import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; @@ -75,8 +74,7 @@ public class OfflineActivity extends AppCompatActivity // state of your app. This will override any checks performed via the ConnectivityManager. // Mapbox.getInstance().setConnected(false); Boolean connected = Mapbox.isConnected(); - Timber.d(String.format(MapboxConstants.MAPBOX_LOCALE, - "Mapbox is connected: %b", connected)); + Timber.d("Mapbox is connected: %s", connected); // Set up map mapView = (MapView) findViewById(R.id.mapView); @@ -207,7 +205,7 @@ public class OfflineActivity extends AppCompatActivity @Override public void onError(String error) { - Timber.e("Error: " + error); + Timber.e("Error: %s" , error); } }); } @@ -223,7 +221,7 @@ public class OfflineActivity extends AppCompatActivity } // Start progress bar - Timber.d("Download started: " + regionName); + Timber.d("Download started: %s", regionName); startProgress(); // Definition @@ -241,14 +239,14 @@ public class OfflineActivity extends AppCompatActivity offlineManager.createOfflineRegion(definition, metadata, new OfflineManager.CreateOfflineRegionCallback() { @Override public void onCreate(OfflineRegion offlineRegion) { - Timber.d("Offline region created: " + regionName); + Timber.d("Offline region created: %s" , regionName); OfflineActivity.this.offlineRegion = offlineRegion; launchDownload(); } @Override public void onError(String error) { - Timber.e("Error: " + error); + Timber.e("Error: %s", error); } }); } 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 new file mode 100644 index 0000000000..6b1cb920fc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java @@ -0,0 +1,131 @@ +package com.mapbox.mapboxsdk.testapp.activity.snapshot; + +import android.graphics.Bitmap; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.ViewTreeObserver; +import android.widget.GridLayout; +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.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import timber.log.Timber; + +/** + * Test activity showing how to use a the {@link com.mapbox.mapboxsdk.snapshotter.MapSnapshotter} + */ +public class MapSnapshotterActivity extends AppCompatActivity { + + private GridLayout grid; + private List<MapSnapshotter> snapshotters = new ArrayList<>(); + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_snapshotter); + + // Find the grid view and start snapshotting as soon + // as the view is measured + grid = (GridLayout) findViewById(R.id.snapshot_grid); + grid.getViewTreeObserver() + .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + //noinspection deprecation + grid.getViewTreeObserver().removeGlobalOnLayoutListener(this); + addSnapshots(); + } + }); + } + + private void addSnapshots() { + Timber.i("Creating snapshotters"); + + for (int row = 0; row < grid.getRowCount(); row++) { + for (int column = 0; column < grid.getColumnCount(); column++) { + startSnapShot(row, column); + } + } + } + + private void startSnapShot(final int row, final int column) { + + // Define the dimensions + MapSnapshotter.Options options = new MapSnapshotter.Options( + grid.getMeasuredWidth() / grid.getColumnCount(), + grid.getMeasuredHeight() / grid.getRowCount() + ) + // Optionally the pixel ratio + .withPixelRatio(1) + + // Optionally the style + .withStyle((column + row) % 2 == 0 ? Style.TRAFFIC_DAY : Style.DARK); + + // Optionally the visible region + if (row % 2 == 0) { + options.withRegion(new LatLngBounds.Builder() + .include(new LatLng(randomInRange(-80, 80), randomInRange(-160, 160))) + .include(new LatLng(randomInRange(-80, 80), randomInRange(-160, 160))) + .build() + ); + } + + // Optionally the camera options + if (column % 2 == 0) { + options.withCameraPosition(new CameraPosition.Builder() + .target(options.getRegion() != null + ? options.getRegion().getCenter() + : new LatLng(randomInRange(-80, 80), randomInRange(-160, 160))) + .bearing(randomInRange(0, 360)) + .tilt(randomInRange(0, 60)) + .zoom(randomInRange(0, 20)) + .build() + ); + } + + MapSnapshotter snapshotter = new MapSnapshotter(MapSnapshotterActivity.this, options); + + snapshotter.start(new MapboxMap.SnapshotReadyCallback() { + @Override + public void onSnapshotReady(Bitmap snapshot) { + Timber.i("Got the snapshot"); + ImageView imageView = new ImageView(MapSnapshotterActivity.this); + imageView.setImageBitmap(snapshot); + grid.addView( + imageView, + new GridLayout.LayoutParams(GridLayout.spec(row), GridLayout.spec(column)) + ); + } + }); + snapshotters.add(snapshotter); + } + + @Override + public void onPause() { + super.onPause(); + + // Make sure to stop the snapshotters on pause + for (MapSnapshotter snapshotter : snapshotters) { + snapshotter.cancel(); + } + snapshotters.clear(); + } + + private static Random random = new Random(); + + public static float randomInRange(float min, float max) { + return (random.nextFloat() * (max - min)) + min; + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java new file mode 100644 index 0000000000..1beba632b0 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java @@ -0,0 +1,148 @@ +package com.mapbox.mapboxsdk.testapp.activity.style; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.Mapbox; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.LatLngQuad; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.layers.RasterLayer; +import com.mapbox.mapboxsdk.style.sources.ImageSource; + +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showing how to use a series of images to create an animation + * with an ImageSource + * <p> + * GL-native equivalent of https://www.mapbox.com/mapbox-gl-js/example/animate-images/ + * </p> + */ +public class AnimatedImageSourceActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final String ID_IMAGE_SOURCE = "animated_image_source"; + private static final String ID_IMAGE_LAYER = "animated_image_layer"; + + private MapView mapView; + private MapboxMap mapboxMap; + + private Handler handler; + private Runnable runnable; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_animated_image_source); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(@NonNull final MapboxMap map) { + mapboxMap = map; + + // add source + LatLngQuad quad = new LatLngQuad( + new LatLng(46.437, -80.425), + new LatLng(46.437, -71.516), + new LatLng(37.936, -71.516), + new LatLng(37.936, -80.425)); + mapboxMap.addSource(new ImageSource(ID_IMAGE_SOURCE, quad, R.drawable.southeast_radar_0)); + + // add layer + RasterLayer layer = new RasterLayer(ID_IMAGE_LAYER, ID_IMAGE_SOURCE); + mapboxMap.addLayer(layer); + + // loop refresh geojson + handler = new Handler(); + runnable = new RefreshImageRunnable(mapboxMap, handler); + handler.postDelayed(runnable, 100); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + handler.removeCallbacks(runnable); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + private static class RefreshImageRunnable implements Runnable { + + private MapboxMap mapboxMap; + private Handler handler; + private Bitmap[] drawables; + private int drawableIndex; + + Bitmap getBitmap(int resourceId) { + Context context = Mapbox.getApplicationContext(); + Drawable drawable = ContextCompat.getDrawable(context, resourceId); + if (drawable instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + return bitmapDrawable.getBitmap(); + } + return null; + } + + RefreshImageRunnable(MapboxMap mapboxMap, Handler handler) { + this.mapboxMap = mapboxMap; + this.handler = handler; + drawables = new Bitmap[4]; + drawables[0] = getBitmap(R.drawable.southeast_radar_0); + drawables[1] = getBitmap(R.drawable.southeast_radar_1); + drawables[2] = getBitmap(R.drawable.southeast_radar_2); + drawables[3] = getBitmap(R.drawable.southeast_radar_3); + drawableIndex = 1; + } + + @Override + public void run() { + ((ImageSource) mapboxMap.getSource(ID_IMAGE_SOURCE)).setImage(drawables[drawableIndex++]); + if (drawableIndex > 3) { + drawableIndex = 0; + } + handler.postDelayed(this, 1000); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java index 2238d1d5fe..f99a1fdb96 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java @@ -74,7 +74,7 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli source = new GeoJsonSource("bus_stop", new URL("https://raw.githubusercontent.com/cheeaun/busrouter-sg/master/data/2/bus-stops.geojson")); } catch (MalformedURLException exception) { - Timber.e("That's not an url... ", exception); + Timber.e(exception, "That's not an url... "); } mapboxMap.addSource(source); } @@ -132,7 +132,7 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli new URL("https://gist.githubusercontent.com/tobrun/7fbc0fe7e9ffea509f7608cda2601d5d/raw/" + "a4b8cc289020f91fa2e1553524820054395e36f5/line.geojson"))); } catch (MalformedURLException malformedUrlException) { - Timber.e("That's not an url... ", malformedUrlException); + Timber.e(malformedUrlException, "That's not an url... "); } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java index 3a5b30f71f..571b48daa6 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java @@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.testapp.activity.style; import android.graphics.Color; import android.os.Bundle; -import android.support.annotation.RawRes; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; @@ -18,14 +17,9 @@ import com.mapbox.mapboxsdk.style.layers.FillLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; import timber.log.Timber; @@ -355,7 +349,7 @@ public class DataDrivenStyleActivity extends AppCompatActivity { // Add a source Source source; try { - source = new GeoJsonSource("amsterdam-parks-source", readRawResource(R.raw.amsterdam)); + source = new GeoJsonSource("amsterdam-parks-source", ResourceUtils.readRawResource(this, R.raw.amsterdam)); mapboxMap.addSource(source); } catch (IOException ioException) { Toast.makeText( @@ -375,21 +369,4 @@ public class DataDrivenStyleActivity extends AppCompatActivity { ) ); } - - private String readRawResource(@RawRes int rawResource) throws IOException { - InputStream is = getResources().openRawResource(rawResource); - Writer writer = new StringWriter(); - char[] buffer = new char[1024]; - try { - Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); - int numRead; - while ((numRead = reader.read(buffer)) != -1) { - writer.write(buffer, 0, numRead); - } - } finally { - is.close(); - } - - return writer.toString(); - } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java index 1ff0b0e8e1..24914fcbb2 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java @@ -1,6 +1,5 @@ package com.mapbox.mapboxsdk.testapp.activity.style; - import android.os.Bundle; import android.support.v7.app.AppCompatActivity; @@ -9,6 +8,9 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.testapp.R; +/** + * Test activity used for instrumentation tests of fill extrusion. + */ public class FillExtrusionStyleTestActivity extends AppCompatActivity { public MapView mapView; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java index 80dfe777cb..a2111bc304 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java @@ -128,7 +128,7 @@ public class GeoJsonClusteringActivity extends AppCompatActivity { ) ); } catch (MalformedURLException malformedUrlException) { - Timber.e("That's not an url... " + malformedUrlException.getMessage()); + Timber.e(malformedUrlException,"That's not an url... "); } // Add unclustered layer diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java index b9f7ebce35..b9f1dfe810 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java @@ -55,7 +55,7 @@ public class RealTimeGeoJsonActivity extends AppCompatActivity implements OnMapR try { mapboxMap.addSource(new GeoJsonSource(ID_GEOJSON_SOURCE, new URL(URL_GEOJSON_SOURCE))); } catch (MalformedURLException malformedUrlException) { - Timber.e("Invalid URL", malformedUrlException); + Timber.e(malformedUrlException, "Invalid URL"); } // add layer diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java index f6754af0f9..adce889007 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java @@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.testapp.activity.style; import android.graphics.Color; import android.os.Bundle; import android.os.Handler; -import android.support.annotation.RawRes; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; @@ -32,16 +31,11 @@ import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.style.sources.TileSet; import com.mapbox.mapboxsdk.style.sources.VectorSource; import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; import com.mapbox.services.commons.geojson.Feature; import com.mapbox.services.commons.geojson.FeatureCollection; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -285,7 +279,7 @@ public class RuntimeStyleActivity extends AppCompatActivity { // Add a source Source source; try { - source = new GeoJsonSource("amsterdam-spots", readRawResource(R.raw.amsterdam)); + source = new GeoJsonSource("amsterdam-spots", ResourceUtils.readRawResource(this, R.raw.amsterdam)); } catch (IOException ioException) { Toast.makeText( RuntimeStyleActivity.this, @@ -317,12 +311,12 @@ public class RuntimeStyleActivity extends AppCompatActivity { layer = mapboxMap.getLayerAs("parksLayer"); // And get some properties PropertyValue<Boolean> fillAntialias = layer.getFillAntialias(); - Timber.d("Fill anti alias: " + fillAntialias.getValue()); + Timber.d("Fill anti alias: %s", fillAntialias.getValue()); layer.setProperties(fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP)); PropertyValue<String> fillTranslateAnchor = layer.getFillTranslateAnchor(); - Timber.d("Fill translate anchor: " + fillTranslateAnchor.getValue()); + Timber.d("Fill translate anchor: %s", fillTranslateAnchor.getValue()); PropertyValue<String> visibility = layer.getVisibility(); - Timber.d("Visibility: " + visibility.getValue()); + Timber.d("Visibility: %s", visibility.getValue()); // Get a good look at it all mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(12)); @@ -332,7 +326,7 @@ public class RuntimeStyleActivity extends AppCompatActivity { // Load some data FeatureCollection parks; try { - String json = readRawResource(R.raw.amsterdam); + String json = ResourceUtils.readRawResource(this, R.raw.amsterdam); parks = FeatureCollection.fromJson(json); } catch (IOException ioException) { Toast.makeText( @@ -477,33 +471,16 @@ public class RuntimeStyleActivity extends AppCompatActivity { Function<Float, String> function = (Function<Float, String>) fillColor.getFunction(); if (function != null) { ExponentialStops<Float, String> stops = (ExponentialStops) function.getStops(); - Timber.d("Fill color base: " + stops.getBase()); - Timber.d("Fill color #stops: " + stops.size()); + Timber.d("Fill color base: %s", stops.getBase()); + Timber.d("Fill color #stops: %s", stops.size()); if (function.getStops() != null) { for (Stop<Float, String> stop : stops) { - Timber.d("Fill color #stops: " + stop); + Timber.d("Fill color #stops: %s", stop); } } } } - private String readRawResource(@RawRes int rawResource) throws IOException { - InputStream is = getResources().openRawResource(rawResource); - Writer writer = new StringWriter(); - char[] buffer = new char[1024]; - try { - Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); - int numRead; - while ((numRead = reader.read(buffer)) != -1) { - writer.write(buffer, 0, numRead); - } - } finally { - is.close(); - } - - return writer.toString(); - } - private void addCustomTileSource() { // Add a source Source source = new VectorSource("custom-tile-source", new TileSet("2.1.0", "https://vector.mapzen.com/osm/all/{z}/{x}/{y}.mvt?api_key=vector-tiles-LM25tq4")); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java index 6906e90f6e..49015ec1e9 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java @@ -1,9 +1,9 @@ package com.mapbox.mapboxsdk.testapp.activity.style; +import android.content.Context; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.annotation.RawRes; import android.support.design.widget.FloatingActionButton; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; @@ -14,22 +14,18 @@ import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; -import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; +import java.lang.ref.WeakReference; import timber.log.Timber; /** - * Test activity showcasing how to use a file:// resource for the style.json + * Test activity showcasing how to use a file:// resource for the style.json and how to use MapboxMap#setStyleJson. */ public class StyleFileActivity extends AppCompatActivity { @@ -48,13 +44,21 @@ public class StyleFileActivity extends AppCompatActivity { public void onMapReady(@NonNull final MapboxMap map) { mapboxMap = map; - FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_file); fab.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary)); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - Timber.i("Loading style file"); - new CreateStyleFileTask().execute(); + new CreateStyleFileTask(view.getContext(), mapboxMap).execute(); + } + }); + + FloatingActionButton fabStyleJson = (FloatingActionButton) findViewById(R.id.fab_style_json); + fabStyleJson.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary)); + fabStyleJson.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + new LoadStyleFileTask(view.getContext(), mapboxMap).execute(); } }); } @@ -62,44 +66,74 @@ public class StyleFileActivity extends AppCompatActivity { } /** + * Task to read a style file from the raw folder + */ + private static class LoadStyleFileTask extends AsyncTask<Void, Void, String> { + private WeakReference<Context> context; + private WeakReference<MapboxMap> mapboxMap; + + LoadStyleFileTask(Context context, MapboxMap mapboxMap) { + this.context = new WeakReference<>(context); + this.mapboxMap = new WeakReference<>(mapboxMap); + } + + @Override + protected String doInBackground(Void... voids) { + String styleJson = ""; + try { + styleJson = ResourceUtils.readRawResource(context.get(), R.raw.sat_style); + } catch (Exception exception) { + Timber.e(exception, "Can't load local file style"); + } + return styleJson; + } + + @Override + protected void onPostExecute(String json) { + super.onPostExecute(json); + Timber.d("Read json, %s", json); + MapboxMap mapboxMap = this.mapboxMap.get(); + if (mapboxMap != null) { + mapboxMap.setStyleJson(json); + } + } + } + + /** * Task to write a style file to local disk and load it in the map view */ - private class CreateStyleFileTask extends AsyncTask<Void, Integer, Long> { + private static class CreateStyleFileTask extends AsyncTask<Void, Integer, Long> { private File cacheStyleFile; + private WeakReference<Context> context; + private WeakReference<MapboxMap> mapboxMap; + + CreateStyleFileTask(Context context, MapboxMap mapboxMap) { + this.context = new WeakReference<>(context); + this.mapboxMap = new WeakReference<>(mapboxMap); + } @Override protected Long doInBackground(Void... params) { try { cacheStyleFile = File.createTempFile("my-", ".style.json"); cacheStyleFile.createNewFile(); - Timber.i("Writing style file to: " + cacheStyleFile.getAbsolutePath()); - writeToFile(cacheStyleFile, readRawResource(R.raw.local_style)); + Timber.i("Writing style file to: %s", cacheStyleFile.getAbsolutePath()); + Context context = this.context.get(); + if (context != null) { + writeToFile(cacheStyleFile, ResourceUtils.readRawResource(context, R.raw.local_style)); + } } catch (Exception exception) { - Toast.makeText(StyleFileActivity.this, "Could not create style file in cache dir", Toast.LENGTH_SHORT).show(); + Toast.makeText(context.get(), "Could not create style file in cache dir", Toast.LENGTH_SHORT).show(); } return 1L; } protected void onPostExecute(Long result) { // Actual file:// usage - mapboxMap.setStyleUrl("file://" + cacheStyleFile.getAbsolutePath()); - } - - private String readRawResource(@RawRes int rawResource) throws IOException { - InputStream is = getResources().openRawResource(rawResource); - Writer writer = new StringWriter(); - char[] buffer = new char[1024]; - try { - Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); - int numRead; - while ((numRead = reader.read(buffer)) != -1) { - writer.write(buffer, 0, numRead); - } - } finally { - is.close(); + MapboxMap mapboxMap = this.mapboxMap.get(); + if (mapboxMap != null) { + mapboxMap.setStyleUrl("file://" + cacheStyleFile.getAbsolutePath()); } - - return writer.toString(); } private void writeToFile(File file, String contents) throws IOException { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java new file mode 100644 index 0000000000..0eaccfef0c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java @@ -0,0 +1,216 @@ +package com.mapbox.mapboxsdk.testapp.activity.style; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.support.v7.app.AppCompatActivity; +import android.graphics.Color; +import android.graphics.PointF; +import android.os.Bundle; +import android.support.annotation.NonNull; + +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.google.gson.GsonBuilder; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.style.sources.Source; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; +import com.mapbox.services.commons.geojson.Feature; +import com.mapbox.services.commons.geojson.FeatureCollection; +import com.mapbox.services.commons.geojson.Geometry; +import com.mapbox.services.commons.geojson.custom.GeometryDeserializer; +import com.mapbox.services.commons.geojson.custom.PositionDeserializer; +import com.mapbox.services.commons.models.Position; + +import java.io.IOException; + +import java.util.List; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; + +/** + * Test activity showcasing using a symbol generator that generates Bitmaps from Android SDK Views. + */ +public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final String SOURCE_ID = "com.mapbox.mapboxsdk.style.layers.symbol.source.id"; + private static final String LAYER_ID = "com.mapbox.mapboxsdk.style.layers.symbol.layer.id"; + private static final String FEATURE_ID = "brk_name"; + private static final String FEATURE_VALUE = "name_sort"; + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_symbol_generator); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + try { + // read local geojson from raw folder + String tinyCountriesJson = ResourceUtils.readRawResource(this, R.raw.tiny_countries); + + // convert geojson to a model + FeatureCollection featureCollection = new GsonBuilder() + .registerTypeAdapter(Geometry.class, new GeometryDeserializer()) + .registerTypeAdapter(Position.class, new PositionDeserializer()) + .create().fromJson(tinyCountriesJson, FeatureCollection.class); + + // add a geojson to the map + Source source = new GeoJsonSource(SOURCE_ID, featureCollection); + mapboxMap.addSource(source); + + // for each feature add a symbolLayer + for (Feature feature : featureCollection.getFeatures()) { + String countryName = feature.getStringProperty(FEATURE_ID); + + // create View + TextView textView = new TextView(this); + textView.setBackgroundColor(getResources().getColor(R.color.blueAccent)); + textView.setPadding(10, 5, 10, 5); + textView.setTextColor(Color.WHITE); + textView.setText(countryName); + + // create bitmap from view + mapboxMap.addImage(countryName, SymbolGenerator.generate(textView)); + } + + // create layer use + mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID) + .withProperties( + iconImage("{" + FEATURE_ID + "}"), // { } is a token notation + iconAllowOverlap(false) + ) + ); + + addSymbolClickListener(); + } catch (IOException exception) { + Timber.e(exception); + } + } + + private void addSymbolClickListener() { + mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() { + @Override + public void onMapClick(@NonNull LatLng point) { + PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point); + List<Feature> features = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID); + if (!features.isEmpty()) { + Feature feature = features.get(0); + Timber.v("Feature was clicked with data: %s", feature.toJson()); + Toast.makeText( + SymbolGeneratorActivity.this, + "hello from: " + feature.getStringProperty(FEATURE_VALUE), + Toast.LENGTH_LONG).show(); + } + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_generator_symbol, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.menu_action_icon_overlap) { + SymbolLayer layer = mapboxMap.getLayerAs(LAYER_ID); + layer.setProperties(iconAllowOverlap(!layer.getIconAllowOverlap().getValue())); + } + return super.onOptionsItemSelected(item); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + /** + * Utility class to generate Bitmaps for Symbol. + * <p> + * Bitmaps can be added to the map with {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)} + * </p> + */ + private static class SymbolGenerator { + + /** + * Generate a Bitmap from an Android SDK View. + * + * @param view the View to be drawn to a Bitmap + * @return the generated bitmap + */ + public static Bitmap generate(@NonNull View view) { + int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + view.measure(measureSpec, measureSpec); + + int measuredWidth = view.getMeasuredWidth(); + int measuredHeight = view.getMeasuredHeight(); + + view.layout(0, 0, measuredWidth, measuredHeight); + Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888); + bitmap.eraseColor(Color.TRANSPARENT); + Canvas canvas = new Canvas(bitmap); + view.draw(canvas); + return bitmap; + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java new file mode 100644 index 0000000000..cd741a85b6 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java @@ -0,0 +1,171 @@ +package com.mapbox.mapboxsdk.testapp.activity.style; + +import android.graphics.PointF; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; + +import com.google.gson.JsonObject; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.services.commons.geojson.Feature; +import com.mapbox.services.commons.geojson.FeatureCollection; +import com.mapbox.services.commons.geojson.Point; +import com.mapbox.services.commons.models.Position; + +import java.util.List; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.functions.Function.property; +import static com.mapbox.mapboxsdk.style.functions.Function.zoom; +import static com.mapbox.mapboxsdk.style.functions.stops.Stop.stop; +import static com.mapbox.mapboxsdk.style.functions.stops.Stops.categorical; +import static com.mapbox.mapboxsdk.style.functions.stops.Stops.interval; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; + +/** + * Test activity showcasing changing the icon with a zoom function and adding selection state to a SymbolLayer. + */ +public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity { + + private static final String LAYER_ID = "symbolLayer"; + private static final String SOURCE_ID = "poiSource"; + private static final String BUS_MAKI_ICON_ID = "bus-11"; + private static final String CAFE_MAKI_ICON_ID = "cafe-11"; + private static final String KEY_PROPERTY_SELECTED = "selected"; + private static final float ZOOM_STOP_MIN_VALUE = 7.0f; + private static final float ZOOM_STOP_MAX_VALUE = 12.0f; + + private MapView mapView; + private MapboxMap mapboxMap; + private GeoJsonSource source; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_zoom_symbol_layer); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(new OnMapReadyCallback() { + @Override + public void onMapReady(@NonNull final MapboxMap map) { + mapboxMap = map; + updateSource(false); + addLayer(); + addMapClickListener(); + } + }); + } + + private void updateSource(boolean selected) { + FeatureCollection featureCollection = createFeatureCollection(selected); + if (source != null) { + source.setGeoJson(featureCollection); + } else { + source = new GeoJsonSource(SOURCE_ID, featureCollection); + mapboxMap.addSource(source); + } + } + + private FeatureCollection createFeatureCollection(boolean selected) { + Point point = Point.fromCoordinates(Position.fromCoordinates(-74.016181, 40.701745)); + Feature feature = Feature.fromGeometry(point); + JsonObject properties = new JsonObject(); + properties.addProperty(KEY_PROPERTY_SELECTED, selected); + feature.setProperties(properties); + return FeatureCollection.fromFeatures(new Feature[] {feature}); + } + + private void addLayer() { + SymbolLayer layer = new SymbolLayer(LAYER_ID, SOURCE_ID); + layer.setProperties( + iconImage( + zoom( + interval( + stop(ZOOM_STOP_MIN_VALUE, iconImage(BUS_MAKI_ICON_ID)), + stop(ZOOM_STOP_MAX_VALUE, iconImage(CAFE_MAKI_ICON_ID)) + ) + ) + ), + iconSize( + property( + KEY_PROPERTY_SELECTED, + categorical( + stop(true, iconSize(3.0f)), + stop(false, iconSize(1.0f)) + ) + ) + ), + iconAllowOverlap(true) + ); + mapboxMap.addLayer(layer); + } + + private void addMapClickListener() { + mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() { + @Override + public void onMapClick(@NonNull LatLng point) { + PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point); + List<Feature> featureList = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID); + if (!featureList.isEmpty()) { + Feature feature = featureList.get(0); + boolean isSelected = feature.getBooleanProperty(KEY_PROPERTY_SELECTED); + updateSource(!isSelected); + } else { + Timber.e("No features found"); + } + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java index f41e5e38f0..71b8115d2e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java @@ -14,6 +14,9 @@ import com.mapbox.services.android.telemetry.permissions.PermissionsManager; import java.util.List; +/** + * Base class for location aware activities. + */ public abstract class BaseLocationActivity extends AppCompatActivity implements PermissionsListener { private PermissionsManager permissionsManager; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java index b0ea9c608b..e954b73f8d 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java @@ -12,6 +12,9 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.testapp.R; +/** + * Test activity showcasing using a custom location engine. + */ public class CustomLocationEngineActivity extends BaseLocationActivity { private MapView mapView; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java index 69e6d64325..9f6f2b2fcd 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java @@ -50,7 +50,7 @@ public class MyLocationDrawableActivity extends BaseLocationActivity implements mapView = new MapView(this, mapboxMapOptions); mapView.setId(R.id.mapView); - ViewGroup parent = (ViewGroup) findViewById(R.id.container); + ViewGroup parent = (ViewGroup) findViewById(android.R.id.content); parent.addView(mapView); mapView.onCreate(savedInstanceState); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java index d465d676f7..718c10c7cb 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java @@ -11,6 +11,9 @@ import com.mapbox.mapboxsdk.testapp.R; import timber.log.Timber; +/** + * Test activity showcasing toggling the user location on the map. + */ public class MyLocationToggleActivity extends BaseLocationActivity { private MapView mapView; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java index 8c049d7730..aaad2f04ab 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java @@ -11,5 +11,6 @@ public class ExampleCustomLayer { public static long InitializeFunction; public static long RenderFunction; + public static long ContextLostFunction; public static long DeinitializeFunction; } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java index f717daeada..76f07ba526 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java @@ -32,7 +32,7 @@ public class OfflineListRegionsDialog extends DialogFragment { .setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - Timber.d("Selected item: " + which); + Timber.d("Selected item: %s", which); } }) .setPositiveButton("Accept", new DialogInterface.OnClickListener() { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java index 8c6ab3e211..6220dc7e69 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java @@ -29,7 +29,7 @@ public class OfflineUtils { String json = jsonObject.toString(); metadata = json.getBytes(JSON_CHARSET); } catch (Exception exception) { - Timber.e("Failed to encode metadata: " + exception.getMessage()); + Timber.e(exception, "Failed to encode metadata: "); } return metadata; } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java new file mode 100644 index 0000000000..f0cca57e10 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java @@ -0,0 +1,36 @@ +package com.mapbox.mapboxsdk.testapp.utils; + +import android.content.Context; +import android.support.annotation.RawRes; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; + +public class ResourceUtils { + + public static String readRawResource(Context context, @RawRes int rawResource) throws IOException { + String json = ""; + if (context != null) { + InputStream is = context.getResources().openRawResource(rawResource); + Writer writer = new StringWriter(); + char[] buffer = new char[1024]; + try { + Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + int numRead; + while ((numRead = reader.read(buffer)) != -1) { + writer.write(buffer, 0, numRead); + } + } finally { + is.close(); + } + json = writer.toString(); + } + return json; + } +} + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java index 6f20d6fb0f..235fd8233b 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java @@ -146,15 +146,15 @@ public class TimingLogger { if (disabled) { return; } - Timber.d(label + ": begin"); + Timber.d("%s: begin", label); final long first = splits.get(0); long now = first; for (int i = 1; i < splits.size(); i++) { now = splits.get(i); final String splitLabel = splitLabels.get(i); final long prev = splits.get(i - 1); - Timber.d(label + ": " + (now - prev) + " ms, " + splitLabel); + Timber.d("%s: %s ms, %s", label, (now - prev), splitLabel); } - Timber.d(label + ": end, " + (now - first) + " ms"); + Timber.d("%s: end, %s ms", label, (now - first)); } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TokenUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TokenUtils.java new file mode 100644 index 0000000000..e08fdb9154 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TokenUtils.java @@ -0,0 +1,37 @@ +package com.mapbox.mapboxsdk.testapp.utils; + + +import android.content.Context; +import android.support.annotation.NonNull; + +import com.mapbox.mapboxsdk.Mapbox; + +public class TokenUtils { + + /** + * <p> + * Returns the Mapbox access token set in the app resources. + * </p> + * It will first search for a token in the Mapbox object. If not found it + * will then attempt to load the access token from the + * {@code res/values/dev.xml} development file. + * + * @param context The {@link Context} of the {@link android.app.Activity} or {@link android.app.Fragment}. + * @return The Mapbox access token or null if not found. + */ + public static String getMapboxAccessToken(@NonNull Context context) { + try { + // Read out AndroidManifest + String token = Mapbox.getAccessToken(); + if (token == null || token.isEmpty()) { + throw new IllegalArgumentException(); + } + return token; + } catch (Exception exception) { + // Use fallback on string resource, used for development + int tokenResId = context.getResources() + .getIdentifier("mapbox_access_token", "string", context.getPackageName()); + return tokenResId != 0 ? context.getString(tokenResId) : null; + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java new file mode 100644 index 0000000000..a69fb48ab4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java @@ -0,0 +1,74 @@ +package com.mapbox.mapboxsdk.testapp.view; + +import android.content.Context; +import android.support.design.widget.BottomSheetBehavior; +import android.support.design.widget.CoordinatorLayout; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +public class LockableBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> { + + private boolean locked = false; + + public LockableBottomSheetBehavior(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setLocked(boolean locked) { + this.locked = locked; + } + + @Override + public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { + boolean handled = false; + if (!locked) { + handled = super.onInterceptTouchEvent(parent, child, event); + } + return handled; + } + + @Override + public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { + boolean handled = false; + if (!locked) { + handled = super.onTouchEvent(parent, child, event); + } + return handled; + } + + @Override + public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, + int nestedScrollAxes) { + boolean handled = false; + if (!locked) { + handled = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); + } + return handled; + } + + @Override + public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, + int[] consumed) { + if (!locked) { + super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed); + } + } + + @Override + public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { + if (!locked) { + super.onStopNestedScroll(coordinatorLayout, child, target); + } + } + + @Override + public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, + float velocityY) { + boolean handled = false; + if (!locked) { + handled = super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); + } + return handled; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/MapViewPager.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/MapViewPager.java new file mode 100644 index 0000000000..92c28d7ed4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/MapViewPager.java @@ -0,0 +1,20 @@ +package com.mapbox.mapboxsdk.testapp.view; + +import android.content.Context; +import android.support.v4.view.PagerTabStrip; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.SurfaceView; +import android.view.View; + +public class MapViewPager extends ViewPager { + + public MapViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { + return v instanceof SurfaceView || v instanceof PagerTabStrip || (super.canScroll(v, checkV, dx, x, y)); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_0.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_0.png Binary files differnew file mode 100644 index 0000000000..c304b619c4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_0.png diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_1.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_1.png Binary files differnew file mode 100644 index 0000000000..ed09fffbe1 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_1.png diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_2.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_2.png Binary files differnew file mode 100644 index 0000000000..fee630f863 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_2.png diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_3.png b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_3.png Binary files differnew file mode 100644 index 0000000000..c4c7146afa --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_3.png diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_white.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_white.xml new file mode 100644 index 0000000000..cd5c045783 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_white.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0"> + <path + android:fillColor="#FFFFFF" + android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" /> +</vector> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml new file mode 100644 index 0000000000..ded53fc4f2 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z" + android:fillColor="#F1F1F1"/> +</vector> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml deleted file mode 100644 index 3217661b64..0000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<shape - xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="oval"> - - <solid - android:color="@color/mapbox_blue"/> - - <size - android:width="@dimen/circle_size" - android:height="@dimen/circle_size"/> -</shape> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml new file mode 100644 index 0000000000..bf9b895aca --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M8,5v14l11,-7z"/> +</vector> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml deleted file mode 100644 index 71992ebd7f..0000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/marker.xml +++ /dev/null @@ -1,4 +0,0 @@ -<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - <size android:width="10px" android:height="10px"/> - <solid android:color="@color/redAccent"/> -</shape>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml new file mode 100644 index 0000000000..26b40b9ab6 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <com.mapbox.mapboxsdk.maps.MapView + android:id="@id/mapView" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:mapbox_cameraTargetLat="41.9567" + app:mapbox_cameraTargetLng="-78.6430" + app:mapbox_cameraZoom="5" + app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/> + +</RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_bottom_sheet.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_bottom_sheet.xml new file mode 100644 index 0000000000..cbb440d926 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_bottom_sheet.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.support.design.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/coordinator_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true" + android:orientation="vertical"> + + <FrameLayout + android:id="@+id/fragment_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"/> + + <android.support.v4.widget.NestedScrollView + android:id="@+id/bottom_sheet" + android:layout_width="match_parent" + android:layout_height="250dp" + android:background="@color/primaryDark" + app:behavior_hideable="true" + app:layout_behavior="android.support.design.widget.BottomSheetBehavior"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <TextView + android:layout_width="match_parent" + android:layout_height="64dp" + android:layout_gravity="center" + android:gravity="center" + android:textColor="@color/white" + android:text="^" + android:textSize="22sp" + tools:ignore="HardcodedText"/> + + <FrameLayout + android:id="@+id/fragment_container_bottom" + android:layout_width="match_parent" + android:layout_height="186dp"/> + + </LinearLayout> + + </android.support.v4.widget.NestedScrollView> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/fabBottomSheet" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="@dimen/fab_margin" + app:backgroundTint="@color/primary" + android:src="@drawable/ic_refresh" + app:layout_anchor="@id/bottom_sheet" + app:layout_anchorGravity="top|end"/> + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|end" + android:clipToPadding="false" + android:paddingBottom="8dp" + app:layout_anchor="@id/fabBottomSheet" + app:layout_anchorGravity="top"> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/fabFragment" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_add_white" + app:backgroundTint="@color/accent"/> + </FrameLayout> +</android.support.design.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml index fa37c485d7..c8752df1bf 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml @@ -1,8 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent"> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> <com.mapbox.mapboxsdk.maps.MapView android:id="@id/mapView" @@ -20,8 +18,8 @@ android:layout_height="wrap_content" android:layout_gravity="end|bottom" android:layout_marginBottom="82dp" - android:layout_marginRight="@dimen/fab_margin" android:layout_marginEnd="@dimen/fab_margin" + android:layout_marginRight="@dimen/fab_margin" android:src="@drawable/ic_my_location" app:backgroundTint="@color/accent" app:layout_anchorGravity="top"/> @@ -35,4 +33,4 @@ android:src="@drawable/ic_paint" app:backgroundTint="@color/primary"/> -</FrameLayout> +</merge> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml index b70bb6d7b2..0cb065a676 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml @@ -15,7 +15,9 @@ app:mapbox_cameraZoom="15"/> <LinearLayout + style="?android:attr/buttonBarStyle" android:layout_width="match_parent" + android:background="@color/primary" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:orientation="horizontal" @@ -23,6 +25,7 @@ <Button android:id="@+id/cameraMoveButton" + style="?android:attr/buttonBarButtonStyle" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" @@ -30,6 +33,7 @@ <Button android:id="@+id/cameraEaseButton" + style="?android:attr/buttonBarButtonStyle" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" @@ -37,6 +41,7 @@ <Button android:id="@+id/cameraAnimateButton" + style="?android:attr/buttonBarButtonStyle" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml new file mode 100644 index 0000000000..d4933bfb9a --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.support.design.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/coordinator_layout" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.mapbox.mapboxsdk.maps.MapView + android:id="@id/mapView" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:mapbox_cameraTargetLat="37.774913" + app:mapbox_cameraTargetLng="-122.419368" + app:mapbox_cameraZoom="11" + app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/fab" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end|bottom" + android:layout_margin="@dimen/fab_margin" + android:src="@drawable/ic_play_arrow_black_24dp" + app:backgroundTint="@android:color/white"/> + +</android.support.design.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml index 6e8a4e5eb2..5ac55e75e2 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml @@ -23,6 +23,7 @@ android:layout_alignParentRight="true" android:layout_marginBottom="@dimen/fab_margin" android:layout_marginRight="@dimen/fab_margin" + android:layout_marginEnd="@dimen/fab_margin" android:src="@drawable/ic_directions_bus_black" app:backgroundTint="@android:color/white"/> @@ -36,6 +37,7 @@ android:layout_marginBottom="@dimen/fab_margin" android:layout_marginRight="@dimen/fab_margin" android:src="@drawable/ic_layers" + android:layout_marginEnd="@dimen/fab_margin" app:backgroundTint="@color/primary"/> </RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml index 6db8b073d9..c6f3c0e3f2 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml @@ -1,46 +1,97 @@ <?xml version="1.0" encoding="utf-8"?> -<android.support.design.widget.CoordinatorLayout +<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:id="@+id/coordinator_layout" + android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:clickable="true" + android:focusable="true" + android:focusableInTouchMode="true"> - <com.mapbox.mapboxsdk.maps.MapView - android:id="@+id/mapView" + <android.support.design.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/coordinator_layout" android:layout_width="match_parent" android:layout_height="match_parent" - app:mapbox_uiAttribution="false" - app:mapbox_uiCompass="false" - app:mapbox_uiLogo="false"/> + android:orientation="vertical"> - <TextView - android:id="@+id/textZoom" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="bottom|start" - android:layout_margin="8dp" - android:textSize="14sp"/> + <com.mapbox.mapboxsdk.maps.MapView + android:id="@+id/mapView" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:mapbox_uiAttribution="false" + app:mapbox_uiCompass="false" + app:mapbox_uiLogo="false"/> - <android.support.design.widget.FloatingActionButton - android:id="@+id/fabDebug" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="end|bottom" - android:layout_marginBottom="82dp" - android:layout_marginRight="@dimen/fab_margin" - android:src="@drawable/ic_refresh" - app:backgroundTint="@color/accent" - app:layout_anchorGravity="top"/> - - <android.support.design.widget.FloatingActionButton - android:id="@+id/fabStyles" + <TextView + android:id="@+id/textZoom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|start" + android:layout_margin="8dp" + android:textIsSelectable="true" + android:textSize="14sp" + app:layout_anchor="@id/bottom_sheet" + app:layout_anchorGravity="bottom|start"/> + + <TextView + android:id="@+id/fpsView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|end" + android:layout_margin="8dp" + android:textIsSelectable="true" + android:textSize="14sp" + app:layout_anchor="@id/textZoom" + app:layout_anchorGravity="top"/> + + <android.support.v4.widget.NestedScrollView + android:id="@+id/bottom_sheet" + android:layout_width="match_parent" + android:layout_height="250dp" + android:background="@color/white" + android:visibility="invisible" + app:behavior_hideable="true" + app:layout_behavior="android.support.design.widget.BottomSheetBehavior"/> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/fabDebug" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top|end" + android:layout_marginBottom="82dp" + android:layout_marginEnd="@dimen/fab_margin" + android:layout_marginRight="@dimen/fab_margin" + android:src="@drawable/ic_refresh" + app:backgroundTint="@color/accent" + app:layout_anchor="@+id/fabStyles" + app:layout_anchorGravity="top"/> + + <android.support.design.widget.FloatingActionButton + android:id="@id/fabStyles" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end|bottom" + android:layout_margin="@dimen/fab_margin" + android:src="@drawable/ic_layers" + app:backgroundTint="@color/primary" + app:layout_anchor="@id/bottom_sheet" + app:layout_anchorGravity="bottom|end"/> + + </android.support.design.widget.CoordinatorLayout> + + <android.support.design.widget.NavigationView android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="end|bottom" - android:layout_margin="@dimen/fab_margin" - android:src="@drawable/ic_layers" - app:backgroundTint="@color/primary"/> + android:layout_height="match_parent" + android:layout_gravity="start" + android:background="@android:color/white"> + + <ListView + android:id="@+id/listView" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + + </android.support.design.widget.NavigationView> -</android.support.design.widget.CoordinatorLayout> +</android.support.v4.widget.DrawerLayout>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml new file mode 100644 index 0000000000..e565c3c9d1 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.support.design.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/coordinator_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true" + android:orientation="vertical"> + + <com.mapbox.mapboxsdk.maps.MapView + android:id="@id/mapView" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"/> + + <android.support.v4.widget.NestedScrollView + android:id="@+id/bottom_sheet" + android:layout_width="match_parent" + android:layout_height="375dp" + android:background="@color/primaryDark" + app:behavior_hideable="true" + app:behavior_skipCollapsed="false" + app:layout_behavior="com.mapbox.mapboxsdk.testapp.view.LockableBottomSheetBehavior"/> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/fab" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="@dimen/fab_margin" + android:src="@drawable/ic_arrow_downward" + app:backgroundTint="@color/primary" + app:layout_anchor="@id/bottom_sheet" + app:layout_anchorGravity="top|end"/> + +</android.support.design.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml index 43fa8fb995..419660b36a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml @@ -1,17 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/fragment_container" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - tools:context=".activity.fragment.MapFragmentActivity"> - - <FrameLayout - android:id="@+id/fragment_container" - android:layout_width="match_parent" - android:layout_height="match_parent"/> - -</LinearLayout> + android:layout_height="match_parent"/> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml index 3fd66977e2..0a12fd9e50 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml @@ -1,8 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent"> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> <Button android:id="@+id/button_open_dialog" @@ -11,4 +8,4 @@ android:layout_gravity="center" android:text="@string/button_open_dialog"/> -</FrameLayout> +</merge> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml index a0de31ee48..cd4aa4bdef 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml @@ -1,19 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> <com.mapbox.mapboxsdk.maps.MapView android:id="@id/mapView" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent"/> <View android:layout_width="@dimen/map_padding_left" android:layout_height="match_parent" android:alpha="0.5" - android:background="@color/mapbox_blue" /> + android:background="@color/mapbox_blue"/> <View android:layout_width="match_parent" @@ -24,13 +21,13 @@ android:layout_marginRight="@dimen/map_padding_right" android:layout_marginStart="@dimen/map_padding_left" android:alpha="0.5" - android:background="@color/mapbox_blue" /> + android:background="@color/mapbox_blue"/> <View android:layout_width="@dimen/map_padding_right" android:layout_height="match_parent" android:layout_gravity="end" android:alpha="0.5" - android:background="@color/mapbox_blue" /> + android:background="@color/mapbox_blue"/> -</FrameLayout> +</merge> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml new file mode 100644 index 0000000000..30ad494dca --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <GridLayout + android:id="@+id/snapshot_grid" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:columnCount="3" + android:orientation="horizontal" + android:rowCount="3"/> + +</RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_visibility.xml index 6da5a61ecb..c33034181c 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_remove_marker.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_visibility.xml @@ -1,13 +1,17 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/viewParent" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:orientation="vertical" + tools:context=".activity.maplayout.SimpleMapActivity"> <com.mapbox.mapboxsdk.maps.MapView - android:id="@+id/mapView" + android:id="@id/mapView" android:layout_width="match_parent" - android:layout_height="match_parent"/> + android:layout_height="match_parent" + android:visibility="invisible"/> </LinearLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml index ff28d2edf0..52691a26fe 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml @@ -17,6 +17,7 @@ <TextView android:id="@+id/countView" + android:textIsSelectable="false" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/toolbar" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml index cf4b51bbe0..dae3a1d9b7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_view.xml @@ -10,16 +10,18 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/toolbar" + android:textIsSelectable="false" app:mapbox_cameraTargetLat="38.907192" app:mapbox_cameraTargetLng="-77.036871" - app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets" - app:mapbox_cameraZoom="12" /> + app:mapbox_cameraZoom="12" + app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/> <TextView android:id="@+id/countView" + android:textIsSelectable="false" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="16dp" - android:textSize="20sp" /> + android:textSize="20sp"/> </RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml index 084675fb2c..1eb999caf5 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml @@ -15,7 +15,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" - android:text="No Results" + android:text="@string/no_results" android:textSize="24sp"/> </LinearLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml index 599ae3fa1c..193ae55e59 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml @@ -2,13 +2,13 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:mapbox="http://schemas.android.com/tools" - android:id="@+id/map_container" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:orientation="vertical" + mapbox:ignore="NestedWeights"> <LinearLayout - android:id="@+id/map_container1" + android:baselineAligned="false" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="0.5" @@ -40,7 +40,7 @@ </LinearLayout> <LinearLayout - android:id="@+id/map_container2" + android:baselineAligned="false" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="0.5" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml index d65d5796f1..addfe8427b 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml @@ -1,22 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@id/container" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> - <FrameLayout + <android.support.v4.widget.ContentLoadingProgressBar android:id="@id/progress" - android:layout_width="match_parent" - android:layout_height="match_parent"> + style="?android:attr/progressBarStyleLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center"/> - <android.support.v4.widget.ContentLoadingProgressBar - style="?android:attr/progressBarStyleLarge" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center"/> - - </FrameLayout> - -</LinearLayout> +</merge> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml deleted file mode 100644 index 26a71bc568..0000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_drawer.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:id="@+id/drawer_layout" - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:context="com.mapbox.mapboxsdk.testapp.activity.maplayout.NavigationDrawerActivity"> - - <FrameLayout - android:id="@+id/container" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - - <FrameLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:fitsSystemWindows="true"> - - <android.support.v7.widget.Toolbar - android:id="@+id/toolbar" - android:layout_width="match_parent" - android:layout_height="56dp" /> - - </FrameLayout> - - <FrameLayout - android:id="@+id/navigation_drawer" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="start" - android:fitsSystemWindows="true" /> - -</android.support.v4.widget.DrawerLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml index d4b64b1ea2..3e21015e96 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml @@ -18,27 +18,30 @@ android:layout_height="match_parent" android:layout_below="@+id/progress_bar"/> - <Button - android:id="@+id/button_download_region" - android:layout_width="wrap_content" + <LinearLayout + style="?android:attr/buttonBarStyle" + android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" - android:layout_alignParentLeft="true" - android:layout_alignParentStart="true" - android:layout_margin="@dimen/fab_margin" - android:background="@color/white" - android:padding="10dp" - android:text="@string/button_download_region"/> + android:background="@color/primary" + android:orientation="horizontal" + android:weightSum="2"> - <Button - android:id="@+id/button_list_regions" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignBottom="@+id/button_download_region" - android:layout_alignParentRight="true" - android:layout_marginRight="@dimen/fab_margin" - android:background="@color/white" - android:padding="10dp" - android:text="@string/button_list_regions"/> + <Button + android:id="@+id/button_download_region" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/button_download_region"/> + + <Button + android:id="@+id/button_list_regions" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/button_list_regions"/> + </LinearLayout> </RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml index 084675fb2c..1eb999caf5 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml @@ -15,7 +15,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" - android:text="No Results" + android:text="@string/no_results" android:textSize="24sp"/> </LinearLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml index bc24533960..cfbd07ce39 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.Toolbar @@ -22,7 +23,7 @@ android:layout_height="wrap_content" android:layout_marginTop="12dp" android:paddingBottom="8dp" - android:text="Move the map by x/y pixels" + android:text="@string/action_scroll_by" android:textColor="#FFFFFF" android:textSize="20sp" /> @@ -32,7 +33,8 @@ android:layout_height="wrap_content" android:layout_alignBottom="@+id/seekbar_move_x" android:layout_below="@id/title" - android:text="X: 0000" /> + android:text="X: 0000" + tools:ignore="HardcodedText"/> <SeekBar android:id="@id/seekbar_move_x" @@ -40,6 +42,7 @@ android:layout_height="wrap_content" android:layout_below="@id/title" android:layout_marginLeft="56dp" + android:layout_marginStart="56dp" android:max="50" android:progress="0" /> @@ -48,7 +51,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@+id/seekbar_move_y" - android:text="Y: 0000" /> + android:text="Y: 0000" + tools:ignore="HardcodedText"/> <SeekBar android:id="@id/seekbar_move_y" @@ -58,6 +62,7 @@ android:layout_marginBottom="8dp" android:layout_marginLeft="56dp" android:layout_marginTop="16dp" + android:layout_marginStart="56dp" android:max="50" android:progress="0" /> @@ -66,7 +71,6 @@ </android.support.v7.widget.Toolbar> <FrameLayout - android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/toolbar"> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml index 6b99711e84..53345571b4 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml @@ -16,6 +16,7 @@ android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" + android:contentDescription="@null" android:background="@color/primary"/> <com.mapbox.mapboxsdk.maps.MapView diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml index b133f3d9a5..83150be4bf 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml @@ -15,7 +15,18 @@ app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"/> <android.support.design.widget.FloatingActionButton - android:id="@id/fab" + android:id="@+id/fab_style_json" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_above="@+id/fab_file" + android:layout_margin="@dimen/fab_margin" + android:src="@drawable/ic_add" + app:backgroundTint="@android:color/white"/> + + <android.support.design.widget.FloatingActionButton + android:id="@id/fab_file" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_visible_bounds.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbol_generator.xml index 89a28a7799..ffcdddce57 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_visible_bounds.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbol_generator.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> @@ -8,6 +9,7 @@ <com.mapbox.mapboxsdk.maps.MapView android:id="@id/mapView" android:layout_width="match_parent" - android:layout_height="match_parent"/> + android:layout_height="match_parent" + app:mapbox_styleUrl="@string/mapbox_style_outdoors"/> -</LinearLayout> +</RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml index d931cb3643..3edaff6985 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml @@ -4,19 +4,18 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - <android.support.v4.view.ViewPager + <com.mapbox.mapboxsdk.testapp.view.MapViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.view.PagerTabStrip - android:id="@+id/viewpager_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" android:paddingBottom="4dp" android:paddingTop="4dp"/> - </android.support.v4.view.ViewPager> + </com.mapbox.mapboxsdk.testapp.view.MapViewPager> </RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml new file mode 100644 index 0000000000..0bb59451ab --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <com.mapbox.mapboxsdk.maps.MapView + android:id="@id/mapView" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:mapbox_cameraTargetLat="40.730648" + app:mapbox_cameraTargetLng="-73.993619" + app:mapbox_cameraZoom="11" + app:mapbox_styleUrl="@string/mapbox_style_light"/> + +</RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml index 1c9fbbd482..a7f422f9ce 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml @@ -16,7 +16,7 @@ android:layout_marginEnd="4dp" android:layout_marginRight="4dp" android:gravity="center" - android:text="Latitude" + android:text="@string/latitude" android:textColor="@android:color/white" /> <SeekBar @@ -25,7 +25,9 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_marginRight="4dp" + android:layout_toStartOf="@+id/value_lat" android:layout_toLeftOf="@+id/value_lat" + android:layout_toEndOf="@+id/text_lat" android:layout_toRightOf="@id/text_lat" android:max="360" /> @@ -38,7 +40,7 @@ android:layout_marginLeft="4dp" android:layout_marginStart="4dp" android:gravity="center" - android:text="-180" + android:text="@string/min_value" android:textColor="@android:color/white" /> </RelativeLayout> @@ -56,7 +58,7 @@ android:textColor="@android:color/white" android:layout_marginRight="4dp" android:gravity="center" - android:text="Longitude" /> + android:text="@string/longitude" /> <SeekBar android:id="@+id/seekbar_lon" @@ -64,8 +66,10 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_marginRight="4dp" + android:layout_toStartOf="@+id/value_lon" android:layout_toLeftOf="@+id/value_lon" android:layout_toRightOf="@id/text_lon" + android:layout_toEndOf="@id/text_lon" android:max="360" /> <TextView @@ -78,7 +82,7 @@ android:layout_marginLeft="4dp" android:layout_marginStart="4dp" android:gravity="center" - android:text="-180" /> + android:text="@string/min_value" /> </RelativeLayout> @@ -95,7 +99,7 @@ android:textColor="@android:color/white" android:layout_marginRight="4dp" android:gravity="center" - android:text="Zoom" /> + android:text="@string/zoom" /> <SeekBar android:id="@+id/seekbar_zoom" @@ -103,8 +107,10 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_marginRight="4dp" + android:layout_toStartOf="@+id/value_zoom" android:layout_toLeftOf="@+id/value_zoom" android:layout_toRightOf="@id/text_zoom" + android:layout_toEndOf="@+id/text_zoom" android:max="18" /> <TextView @@ -117,7 +123,7 @@ android:textColor="@android:color/white" android:layout_marginStart="4dp" android:gravity="center" - android:text="18" /> + android:text="@string/default_zoom_value" /> </RelativeLayout> @@ -134,7 +140,7 @@ android:layout_marginEnd="4dp" android:layout_marginRight="4dp" android:gravity="center" - android:text="Bearing" /> + android:text="@string/bearing" /> <SeekBar android:id="@+id/seekbar_bearing" @@ -142,8 +148,10 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_marginRight="4dp" + android:layout_toStartOf="@+id/value_bearing" android:layout_toLeftOf="@+id/value_bearing" android:layout_toRightOf="@id/text_bearing" + android:layout_toEndOf="@id/text_bearing" android:max="360" /> <TextView @@ -156,7 +164,7 @@ android:layout_marginLeft="4dp" android:layout_marginStart="4dp" android:gravity="center" - android:text="0" /> + android:text="@string/default_tilt_value" /> </RelativeLayout> @@ -173,7 +181,7 @@ android:textColor="@android:color/white" android:layout_marginRight="4dp" android:gravity="center" - android:text="Tilt" /> + android:text="@string/tilt" /> <SeekBar android:id="@+id/seekbar_tilt" @@ -181,8 +189,10 @@ android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_marginRight="4dp" + android:layout_toStartOf="@+id/value_tilt" android:layout_toLeftOf="@+id/value_tilt" android:layout_toRightOf="@id/text_tilt" + android:layout_toEndOf="@id/text_tilt" android:max="60" /> <TextView @@ -195,7 +205,7 @@ android:layout_marginLeft="4dp" android:layout_marginStart="4dp" android:gravity="center" - android:text="0" /> + android:text="@string/default_tilt_value" /> </RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml index afebfa1c47..b8ea3d847e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml @@ -12,7 +12,6 @@ mapbox:mapbox_cameraTargetLat="47.6077" mapbox:mapbox_cameraTargetLng="-122.3421" mapbox:mapbox_cameraZoom="11" - mapbox:mapbox_renderTextureMode="true" mapbox:mapbox_styleUrl="mapbox://styles/mapbox/streets-v10" /> </LinearLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml index b976013ead..3cf2fbea55 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> + android:layout_width="fill_parent" + android:layout_height="fill_parent" + xmlns:maps="http://schemas.android.com/apk/res-auto"> <com.mapbox.mapboxsdk.maps.MapView android:id="@id/mapView" @@ -9,7 +10,6 @@ android:layout_height="match_parent" /> <FrameLayout - android:id="@+id/map_card" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginLeft="5dp" @@ -19,6 +19,7 @@ <com.mapbox.mapboxsdk.maps.MapView android:id="@+id/mini_map" android:layout_width="100dp" + maps:mapbox_enableZMediaOverlay="true" android:layout_height="100dp" /> </FrameLayout> </RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml index b290d013f0..49b38f081a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml @@ -20,6 +20,7 @@ android:gravity="center_vertical" android:maxLines="1" android:textColor="@android:color/black" + android:textIsSelectable="false" android:textSize="16sp"/> <TextView @@ -31,6 +32,7 @@ android:alpha="0.56" android:maxLines="1" android:textColor="@android:color/black" + android:textIsSelectable="false" android:textSize="14sp"/> </LinearLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml index 75f6ac9588..afec1f3bea 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml @@ -18,6 +18,7 @@ android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:layout_marginStart="16dp" + android:textIsSelectable="false" android:layout_marginTop="16dp" android:alpha="0.54" android:background="@android:color/transparent" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml index 324a4861c3..cafa7d9746 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml @@ -6,6 +6,7 @@ <ImageView android:id="@id/imageView" android:layout_width="64dp" + android:contentDescription="@null" android:layout_height="64dp"/> <TextView @@ -14,6 +15,7 @@ android:layout_height="wrap_content" android:layout_centerInParent="true" android:padding="2dp" + android:textIsSelectable="false" android:textColor="@android:color/white" android:textStyle="bold"/> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml index 299865be9e..1ef2c69012 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml @@ -11,6 +11,7 @@ android:layout_height="wrap_content" android:textStyle="bold" android:padding="4dp" + android:textIsSelectable="false" android:layout_centerInParent="true" /> </RelativeLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml index 92d1dd5380..ff65f319f9 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml @@ -3,10 +3,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menu_action_intensity" - android:title="Change intensity" + android:title="@string/change_intensity" app:showAsAction="never"/> <item android:id="@+id/menu_action_anchor" - android:title="Change Anchor" + android:title="@string/change_anchor" app:showAsAction="never"/> </menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml index 8b7245c5ca..43a191f7b1 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/spinner" - android:title="Amount of markers" + android:title="@string/amount_of_markers" app:actionViewClass="android.widget.Spinner" app:showAsAction="always"/> </menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml index 4639dd65ba..915afd77fa 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml @@ -1,17 +1,15 @@ <?xml version="1.0" encoding="utf-8"?> -<menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:mapbox="http://schemas.android.com/apk/res-auto"> - +<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_update_layer" - android:title="Update layer (invalidate)" /> + android:title="@string/update_layer_invalidate"/> <item android:id="@+id/action_set_color_red" - android:title="Red" /> + android:title="@string/red"/> <item android:id="@+id/action_set_color_green" - android:title="Green" /> + android:title="@string/green"/> <item android:id="@+id/action_set_color_blue" - android:title="Blue" /> + android:title="@string/blue"/> </menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml index 3eae56a273..a596ff5708 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml @@ -4,47 +4,47 @@ <item android:id="@+id/action_add_exponential_zoom_function" - android:title="Add an exponential zoom function" + android:title="@string/add_an_exponential_zoom_function" mapbox:showAsAction="never"/> <item android:id="@+id/action_add_interval_zoom_function" - android:title="Add an interval zoom function" + android:title="@string/add_an_interval_zoom_function" mapbox:showAsAction="never"/> <item android:id="@+id/action_add_categorical_source_function" - android:title="Add a categorical source function" + android:title="@string/add_a_categorical_source_function" mapbox:showAsAction="never"/> <item android:id="@+id/action_add_exponential_source_function" - android:title="Add an exponential source function" + android:title="@string/add_an_exponential_source_function" mapbox:showAsAction="never"/> <item android:id="@+id/action_add_identity_source_function" - android:title="Add an identity source function" + android:title="@string/add_an_identity_source_function" mapbox:showAsAction="never"/> <item android:id="@+id/action_add_interval_source_function" - android:title="Add an interval source function" + android:title="@string/add_an_interval_source_function" mapbox:showAsAction="never"/> <item android:id="@+id/action_add_composite_exponential_function" - android:title="Add a composite, exponential function" + android:title="@string/add_a_composite_exponential_function" mapbox:showAsAction="never"/> <item android:id="@+id/action_add_composite_categorical_function" - android:title="Add a composite, categorical function" + android:title="@string/add_a_composite_categorical_function" mapbox:showAsAction="never"/> <item android:id="@+id/action_add_composite_interval_function" - android:title="Add a composite, interval function" + android:title="@string/add_a_composite_interval_function" mapbox:showAsAction="never"/> </menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml new file mode 100644 index 0000000000..168361a263 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/menu_action_icon_overlap" + android:title="@string/menuitem_change_icon_overlap" + app:showAsAction="never"/> +</menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml index e0052d4a8c..7132c0c2a9 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml @@ -3,10 +3,10 @@ xmlns:mapbox="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_user_tracking" - android:title="My Location Tracking" + android:title="@string/my_location_tracking" mapbox:showAsAction="never" /> <item android:id="@+id/action_bangalore" - android:title="Bangalore" + android:title="@string/bangalore" mapbox:showAsAction="never" /> </menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml index 0b3a8e797e..7d20442c8c 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml @@ -4,6 +4,5 @@ <item android:id="@+id/menuItemReset" android:title="@string/menuitem_title_reset" - app:showAsAction="always" - /> + app:showAsAction="always"/> </menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml index 86f0b4faee..5c77179272 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml @@ -4,66 +4,66 @@ <item android:id="@+id/action_list_layers" - android:title="List all layers in the style" + android:title="@string/list_all_layers_in_the_style" mapbox:showAsAction="never" /> <item android:id="@+id/action_list_sources" - android:title="List all sources in the style" + android:title="@string/list_all_sources_in_the_style" mapbox:showAsAction="never" /> <item android:id="@+id/action_water_color" - android:title="Color the water" + android:title="@string/color_the_water" mapbox:showAsAction="never" /> <item android:id="@+id/action_background_opacity" - android:title="Set background opacity" + android:title="@string/set_background_opacity" mapbox:showAsAction="never" /> <item android:id="@+id/action_road_avoid_edges" - android:title="Set road symbol placement to Point" + android:title="@string/set_road_symbol_placement_to_point" mapbox:showAsAction="never" /> <item android:id="@+id/action_layer_visibility" - android:title="Set layer visibility to false" + android:title="@string/set_layer_visibility_to_false" mapbox:showAsAction="never" /> <item android:id="@+id/action_add_parks_layer" - android:title="Add a parks layer" + android:title="@string/add_a_parks_layer" mapbox:showAsAction="never" /> <item android:id="@+id/action_add_dynamic_parks_layer" - android:title="Add a dynamic GeoJSON source" + android:title="@string/add_a_dynamic_geojson_source" mapbox:showAsAction="never" /> <item android:id="@+id/action_remove_layer" - android:title="Remove buildings layer" + android:title="@string/remove_buildings_layer" mapbox:showAsAction="never" /> <item android:id="@+id/action_add_terrain_layer" - android:title="Add a terrain layer" + android:title="@string/add_a_terrain_layer" mapbox:showAsAction="never" /> <item android:id="@+id/action_add_satellite_layer" - android:title="Add a satellite layer" + android:title="@string/add_a_satellite_layer" mapbox:showAsAction="never" /> <item android:id="@+id/action_update_water_color_on_zoom" - android:title="Change the water color on zoom" + android:title="@string/change_the_water_color_on_zoom" mapbox:showAsAction="never" /> <item android:id="@+id/action_add_custom_tiles" - android:title="Custom tiles" + android:title="@string/custom_tiles" mapbox:showAsAction="never" /> <item android:id="@+id/action_fill_filter" - android:title="Apply filtered fill" + android:title="@string/apply_filtered_fill" mapbox:showAsAction="never" /> <item android:id="@+id/action_line_filter" - android:title="Apply filtered line" + android:title="@string/apply_filtered_line" mapbox:showAsAction="never" /> <item android:id="@+id/action_numeric_filter" - android:title="Apply numeric fill filter" + android:title="@string/apply_numeric_fill_filter" mapbox:showAsAction="never" /> </menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml index 77468b4861..8f396b07b0 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml @@ -3,14 +3,14 @@ <item android:id="@+id/action_toggle_text_size" - android:title="Toggle text size"/> + android:title="@string/toggle_text_size"/> <item android:id="@+id/action_toggle_text_field" - android:title="Toggle text field contents"/> + android:title="@string/toggle_text_field_contents"/> <item android:id="@+id/action_toggle_text_font" - android:title="Toggle text font"/> + android:title="@string/toggle_text_font"/> </menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml index 0cea519a24..67c0b2df55 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml @@ -3,23 +3,23 @@ xmlns:mapbox="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_zoom_in" - android:title="Zoom in" + android:title="@string/zoom_in" mapbox:showAsAction="never" /> <item android:id="@+id/action_zoom_out" - android:title="Zoom out" + android:title="@string/zoom_out" mapbox:showAsAction="never" /> <item android:id="@+id/action_zoom_by" - android:title="Zoom by 2" + android:title="@string/zoom_by_2" mapbox:showAsAction="never" /> <item android:id="@+id/action_zoom_to_point" - android:title="Zoom to point" + android:title="@string/zoom_to_point" mapbox:showAsAction="never" /> <item android:id="@+id/action_zoom_to" - android:title="Zoom to 4" + android:title="@string/zoom_to_4" mapbox:showAsAction="never" /> </menu> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json new file mode 100644 index 0000000000..f2cd969be4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json @@ -0,0 +1,31 @@ +{ + "version": 8, + "name": "Satellite", + "metadata": { + "mapbox:autocomposite": true + }, + "sources": { + "mapbox": { + "type": "raster", + "url": "mapbox://mapbox.satellite", + "tileSize": 256 + } + }, + "sprite": "mapbox://sprites/mapbox/satellite-v8", + "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "rgb(4,7,14)" + } + }, + { + "id": "satellite", + "type": "raster", + "source": "mapbox", + "source-layer": "mapbox_satellite_full" + } + ] +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson new file mode 100644 index 0000000000..caff2ac81c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson @@ -0,0 +1,2741 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "scalerank": 1, + "sr_label_i": 2, + "sr_label_o": 4, + "sovereignt": "Vanuatu", + "sov_a3": "VUT", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Vanuatu", + "adm0_a3": "VUT", + "geou_dif": 0, + "geounit": "Vanuatu", + "gu_a3": "VUT", + "su_dif": 0, + "subunit": "Vanuatu", + "su_a3": "VUT", + "brk_diff": 0, + "name": "Vanuatu", + "name_long": "Vanuatu", + "brk_a3": "VUT", + "brk_name": "Vanuatu", + "brk_group": null, + "abbrev": "Van.", + "postal": "VU", + "formal_en": "Republic of Vanuatu", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Vanuatu", + "name_alt": null, + "mapcolor7": 6, + "mapcolor8": 3, + "mapcolor9": 7, + "mapcolor13": 3, + "pop_est": 218519, + "gdp_md_est": 988.5, + "pop_year": -99, + "lastcensus": 2009, + "gdp_year": -99, + "economy": "7. Least developed region", + "income_grp": "4. Lower middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "VU", + "iso_a3": "VUT", + "iso_n3": "548", + "un_a3": "548", + "wb_a2": "VU", + "wb_a3": "VUT", + "woe_id": -99, + "adm0_a3_is": "VUT", + "adm0_a3_us": "VUT", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Melanesia", + "region_wb": "East Asia & Pacific", + "name_len": 7, + "long_len": 7, + "abbrev_len": 4, + "tiny": 2, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 166.9270664395989, + -15.367957152169708 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 1, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "France", + "sov_a3": "FR1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "French Southern and Antarctic Lands", + "adm0_a3": "ATF", + "geou_dif": 0, + "geounit": "French Southern and Antarctic Lands", + "gu_a3": "ATF", + "su_dif": 0, + "subunit": "French Southern and Antarctic Lands", + "su_a3": "ATF", + "brk_diff": 0, + "name": "Fr. S. Antarctic Lands", + "name_long": "French Southern and Antarctic Lands", + "brk_a3": "ATF", + "brk_name": "Fr. S. and Antarctic Lands", + "brk_group": null, + "abbrev": "Fr. S.A.L.", + "postal": "TF", + "formal_en": "Territory of the French Southern and Antarctic Lands", + "formal_fr": null, + "note_adm0": "Fr.", + "note_brk": null, + "name_sort": "French Southern and Antarctic Lands", + "name_alt": null, + "mapcolor7": 7, + "mapcolor8": 5, + "mapcolor9": 9, + "mapcolor13": 11, + "pop_est": 140, + "gdp_md_est": 16, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "TF", + "iso_a3": "ATF", + "iso_n3": "260", + "un_a3": "-099", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "ATF", + "adm0_a3_us": "ATF", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Seven seas (open ocean)", + "region_un": "Seven seas (open ocean)", + "subregion": "Seven seas (open ocean)", + "region_wb": "Sub-Saharan Africa", + "name_len": 22, + "long_len": 35, + "abbrev_len": 10, + "tiny": 2, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 69.22513999086925, + -49.33878196163545 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 6, + "sovereignt": "New Zealand", + "sov_a3": "NZ1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "Cook Islands", + "adm0_a3": "COK", + "geou_dif": 0, + "geounit": "Cook Islands", + "gu_a3": "COK", + "su_dif": 0, + "subunit": "Cook Islands", + "su_a3": "COK", + "brk_diff": 0, + "name": "Cook Is.", + "name_long": "Cook Islands", + "brk_a3": "COK", + "brk_name": "Cook Is.", + "brk_group": null, + "abbrev": "Cook Is.", + "postal": "CK", + "formal_en": null, + "formal_fr": null, + "note_adm0": "Assoc. with N.Z.", + "note_brk": null, + "name_sort": "Cook Islands", + "name_alt": null, + "mapcolor7": 3, + "mapcolor8": 3, + "mapcolor9": 4, + "mapcolor13": 4, + "pop_est": 11870, + "gdp_md_est": 183.2, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "3. Upper middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "CK", + "iso_a3": "COK", + "iso_n3": "184", + "un_a3": "184", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "COK", + "adm0_a3_us": "COK", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Polynesia", + "region_wb": "East Asia & Pacific", + "name_len": 8, + "long_len": 12, + "abbrev_len": 8, + "tiny": 3, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -159.78922694470387, + -21.220086945691605 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 4, + "sovereignt": "Samoa", + "sov_a3": "WSM", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Samoa", + "adm0_a3": "WSM", + "geou_dif": 0, + "geounit": "Samoa", + "gu_a3": "WSM", + "su_dif": 0, + "subunit": "Samoa", + "su_a3": "WSM", + "brk_diff": 0, + "name": "Samoa", + "name_long": "Samoa", + "brk_a3": "WSM", + "brk_name": "Samoa", + "brk_group": null, + "abbrev": "Samoa", + "postal": "WS", + "formal_en": "Independent State of Samoa", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Samoa", + "name_alt": null, + "mapcolor7": 3, + "mapcolor8": 3, + "mapcolor9": 4, + "mapcolor13": 6, + "pop_est": 219998, + "gdp_md_est": 1049, + "pop_year": -99, + "lastcensus": 2006, + "gdp_year": -99, + "economy": "7. Least developed region", + "income_grp": "4. Lower middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "WS", + "iso_a3": "WSM", + "iso_n3": "882", + "un_a3": "882", + "wb_a2": "WS", + "wb_a3": "WSM", + "woe_id": -99, + "adm0_a3_is": "WSM", + "adm0_a3_us": "WSM", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Polynesia", + "region_wb": "East Asia & Pacific", + "name_len": 5, + "long_len": 5, + "abbrev_len": 5, + "tiny": -99, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -172.41373026688336, + -13.637369985140253 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 4, + "sovereignt": "Tonga", + "sov_a3": "TON", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Tonga", + "adm0_a3": "TON", + "geou_dif": 0, + "geounit": "Tonga", + "gu_a3": "TON", + "su_dif": 0, + "subunit": "Tonga", + "su_a3": "TON", + "brk_diff": 0, + "name": "Tonga", + "name_long": "Tonga", + "brk_a3": "TON", + "brk_name": "Tonga", + "brk_group": null, + "abbrev": "Tongo", + "postal": "TO", + "formal_en": "Kingdom of Tonga", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Tonga", + "name_alt": null, + "mapcolor7": 2, + "mapcolor8": 1, + "mapcolor9": 1, + "mapcolor13": 8, + "pop_est": 120898, + "gdp_md_est": 549, + "pop_year": -99, + "lastcensus": 2006, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "4. Lower middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "TO", + "iso_a3": "TON", + "iso_n3": "776", + "un_a3": "776", + "wb_a2": "TO", + "wb_a3": "TON", + "woe_id": -99, + "adm0_a3_is": "TON", + "adm0_a3_us": "TON", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Polynesia", + "region_wb": "East Asia & Pacific", + "name_len": 5, + "long_len": 5, + "abbrev_len": 5, + "tiny": 3, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -175.23533295466754, + -21.158187998515473 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 5, + "sovereignt": "France", + "sov_a3": "FR1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "French Polynesia", + "adm0_a3": "PYF", + "geou_dif": 0, + "geounit": "French Polynesia", + "gu_a3": "PYF", + "su_dif": 0, + "subunit": "French Polynesia", + "su_a3": "PYF", + "brk_diff": 0, + "name": "Fr. Polynesia", + "name_long": "French Polynesia", + "brk_a3": "PYF", + "brk_name": "Fr. Polynesia", + "brk_group": null, + "abbrev": "Fr. Poly.", + "postal": "PF", + "formal_en": "French Polynesia", + "formal_fr": null, + "note_adm0": "Fr.", + "note_brk": null, + "name_sort": "French Polynesia", + "name_alt": null, + "mapcolor7": 7, + "mapcolor8": 5, + "mapcolor9": 9, + "mapcolor13": 11, + "pop_est": 287032, + "gdp_md_est": 4718, + "pop_year": -99, + "lastcensus": 2007, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "PF", + "iso_a3": "PYF", + "iso_n3": "258", + "un_a3": "258", + "wb_a2": "PF", + "wb_a3": "PYF", + "woe_id": -99, + "adm0_a3_is": "PYF", + "adm0_a3_us": "PYF", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Polynesia", + "region_wb": "East Asia & Pacific", + "name_len": 13, + "long_len": 16, + "abbrev_len": 9, + "tiny": 2, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -149.47549597877855, + -17.6250049835121 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 6, + "sovereignt": "United Kingdom", + "sov_a3": "GB1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "Pitcairn Islands", + "adm0_a3": "PCN", + "geou_dif": 0, + "geounit": "Pitcairn Islands", + "gu_a3": "PCN", + "su_dif": 0, + "subunit": "Pitcairn Islands", + "su_a3": "PCN", + "brk_diff": 0, + "name": "Pitcairn Is.", + "name_long": "Pitcairn Islands", + "brk_a3": "PCN", + "brk_name": "Pitcairn Is.", + "brk_group": null, + "abbrev": "Pit. Is.", + "postal": "PN", + "formal_en": "Pitcairn, Henderson, Ducie and Oeno Islands", + "formal_fr": null, + "note_adm0": "U.K.", + "note_brk": null, + "name_sort": "Pitcairn Islands", + "name_alt": null, + "mapcolor7": 6, + "mapcolor8": 6, + "mapcolor9": 6, + "mapcolor13": 3, + "pop_est": 48, + "gdp_md_est": 0.72, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "7. Least developed region", + "income_grp": "5. Low income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "PN", + "iso_a3": "PCN", + "iso_n3": "612", + "un_a3": "612", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "PCN", + "adm0_a3_us": "PCN", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Polynesia", + "region_wb": "East Asia & Pacific", + "name_len": 12, + "long_len": 16, + "abbrev_len": 8, + "tiny": -99, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -128.31780012096033, + -24.364139777771015 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 5, + "sovereignt": "Barbados", + "sov_a3": "BRB", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Barbados", + "adm0_a3": "BRB", + "geou_dif": 0, + "geounit": "Barbados", + "gu_a3": "BRB", + "su_dif": 0, + "subunit": "Barbados", + "su_a3": "BRB", + "brk_diff": 0, + "name": "Barbados", + "name_long": "Barbados", + "brk_a3": "BRB", + "brk_name": "Barbados", + "brk_group": null, + "abbrev": "Barb.", + "postal": "BB", + "formal_en": "Barbados", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Barbados", + "name_alt": null, + "mapcolor7": 4, + "mapcolor8": 1, + "mapcolor9": 5, + "mapcolor13": 3, + "pop_est": 284589, + "gdp_md_est": 5425, + "pop_year": -99, + "lastcensus": 2010, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "BB", + "iso_a3": "BRB", + "iso_n3": "052", + "un_a3": "052", + "wb_a2": "BB", + "wb_a3": "BRB", + "woe_id": -99, + "adm0_a3_is": "BRB", + "adm0_a3_us": "BRB", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "North America", + "region_un": "Americas", + "subregion": "Caribbean", + "region_wb": "Latin America & Caribbean", + "name_len": 8, + "long_len": 8, + "abbrev_len": 5, + "tiny": 3, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -59.554305983838844, + 13.174672374462602 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 5, + "sovereignt": "Trinidad and Tobago", + "sov_a3": "TTO", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Trinidad and Tobago", + "adm0_a3": "TTO", + "geou_dif": 0, + "geounit": "Trinidad and Tobago", + "gu_a3": "TTO", + "su_dif": 0, + "subunit": "Trinidad and Tobago", + "su_a3": "TTO", + "brk_diff": 0, + "name": "Trinidad and Tobago", + "name_long": "Trinidad and Tobago", + "brk_a3": "TTO", + "brk_name": "Trinidad and Tobago", + "brk_group": null, + "abbrev": "Tr.T.", + "postal": "TT", + "formal_en": "Republic of Trinidad and Tobago", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Trinidad and Tobago", + "name_alt": null, + "mapcolor7": 5, + "mapcolor8": 6, + "mapcolor9": 2, + "mapcolor13": 5, + "pop_est": 1310000, + "gdp_md_est": 29010, + "pop_year": -99, + "lastcensus": 2011, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "TT", + "iso_a3": "TTO", + "iso_n3": "780", + "un_a3": "780", + "wb_a2": "TT", + "wb_a3": "TTO", + "woe_id": -99, + "adm0_a3_is": "TTO", + "adm0_a3_us": "TTO", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "North America", + "region_un": "Americas", + "subregion": "Caribbean", + "region_wb": "Latin America & Caribbean", + "name_len": 19, + "long_len": 19, + "abbrev_len": 5, + "tiny": 2, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -61.255188941565905, + 10.43680324164859 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 4, + "sovereignt": "Sao Tome and Principe", + "sov_a3": "STP", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Sao Tome and Principe", + "adm0_a3": "STP", + "geou_dif": 0, + "geounit": "Sao Tome and Principe", + "gu_a3": "STP", + "su_dif": 0, + "subunit": "Sao Tome and Principe", + "su_a3": "STP", + "brk_diff": 0, + "name": "São Tomé and Principe", + "name_long": "São Tomé and Principe", + "brk_a3": "STP", + "brk_name": "Sao Tome and Principe", + "brk_group": null, + "abbrev": "S.T.P.", + "postal": "ST", + "formal_en": "Democratic Republic of São Tomé and Principe", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "São Tomé and Principe", + "name_alt": null, + "mapcolor7": 1, + "mapcolor8": 6, + "mapcolor9": 1, + "mapcolor13": 7, + "pop_est": 212679, + "gdp_md_est": 276.5, + "pop_year": -99, + "lastcensus": 2001, + "gdp_year": -99, + "economy": "7. Least developed region", + "income_grp": "4. Lower middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "ST", + "iso_a3": "STP", + "iso_n3": "678", + "un_a3": "678", + "wb_a2": "ST", + "wb_a3": "STP", + "woe_id": -99, + "adm0_a3_is": "STP", + "adm0_a3_us": "STP", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Africa", + "region_un": "Africa", + "subregion": "Middle Africa", + "region_wb": "Sub-Saharan Africa", + "name_len": 21, + "long_len": 21, + "abbrev_len": 6, + "tiny": 3, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 6.617198520543866, + 0.246806952308191 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 6, + "sovereignt": "United Kingdom", + "sov_a3": "GB1", + "adm0_dif": 1, + "level": 4, + "type": "Geo subunit", + "admin": "Saint Helena", + "adm0_a3": "SHN", + "geou_dif": 0, + "geounit": "Saint Helena", + "gu_a3": "SHN", + "su_dif": 1, + "subunit": "Ascension", + "su_a3": "BAC", + "brk_diff": 0, + "name": "Ascension", + "name_long": "Ascension", + "brk_a3": "BAC", + "brk_name": "Ascension", + "brk_group": null, + "abbrev": "Asc.", + "postal": "AS", + "formal_en": null, + "formal_fr": null, + "note_adm0": "U.K.", + "note_brk": null, + "name_sort": "Ascension", + "name_alt": null, + "mapcolor7": 6, + "mapcolor8": 6, + "mapcolor9": 6, + "mapcolor13": 3, + "pop_est": 940, + "gdp_md_est": 2.21553, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "-99", + "income_grp": "-99", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "-99", + "iso_a3": "-99", + "iso_n3": "-99", + "un_a3": "-099", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "SHN", + "adm0_a3_us": "SHN", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Seven seas (open ocean)", + "region_un": "Seven seas (open ocean)", + "subregion": "Seven seas (open ocean)", + "region_wb": "Antarctica", + "name_len": 9, + "long_len": 9, + "abbrev_len": 4, + "tiny": 3, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -14.362068334482444, + -7.939246540570252 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "United Kingdom", + "sov_a3": "GB1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "Saint Helena", + "adm0_a3": "SHN", + "geou_dif": 0, + "geounit": "Saint Helena", + "gu_a3": "SHN", + "su_dif": 0, + "subunit": "Saint Helena", + "su_a3": "SHN", + "brk_diff": 0, + "name": "Saint Helena", + "name_long": "Saint Helena", + "brk_a3": "SHN", + "brk_name": "Saint Helena", + "brk_group": null, + "abbrev": "St.H.", + "postal": "SH", + "formal_en": null, + "formal_fr": null, + "note_adm0": "U.K.", + "note_brk": null, + "name_sort": "St. Helena", + "name_alt": null, + "mapcolor7": 6, + "mapcolor8": 6, + "mapcolor9": 6, + "mapcolor13": 3, + "pop_est": 7637, + "gdp_md_est": 18, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "4. Lower middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "SH", + "iso_a3": "SHN", + "iso_n3": "654", + "un_a3": "654", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "SHN", + "adm0_a3_us": "SHN", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Seven seas (open ocean)", + "region_un": "Africa", + "subregion": "Western Africa", + "region_wb": "Sub-Saharan Africa", + "name_len": 12, + "long_len": 12, + "abbrev_len": 5, + "tiny": -99, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -5.716296101395358, + -15.963221612123107 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 5, + "sovereignt": "Malta", + "sov_a3": "MLT", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Malta", + "adm0_a3": "MLT", + "geou_dif": 0, + "geounit": "Malta", + "gu_a3": "MLT", + "su_dif": 0, + "subunit": "Malta", + "su_a3": "MLT", + "brk_diff": 0, + "name": "Malta", + "name_long": "Malta", + "brk_a3": "MLT", + "brk_name": "Malta", + "brk_group": null, + "abbrev": "Malta", + "postal": "M", + "formal_en": "Republic of Malta", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Malta", + "name_alt": null, + "mapcolor7": 1, + "mapcolor8": 4, + "mapcolor9": 1, + "mapcolor13": 8, + "pop_est": 405165, + "gdp_md_est": 9962, + "pop_year": -99, + "lastcensus": 2005, + "gdp_year": -99, + "economy": "2. Developed region: nonG7", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "MT", + "iso_a3": "MLT", + "iso_n3": "470", + "un_a3": "470", + "wb_a2": "MT", + "wb_a3": "MLT", + "woe_id": -99, + "adm0_a3_is": "MLT", + "adm0_a3_us": "MLT", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Europe", + "region_un": "Europe", + "subregion": "Southern Europe", + "region_wb": "Middle East & North Africa", + "name_len": 5, + "long_len": 5, + "abbrev_len": 5, + "tiny": 3, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 14.438179478988388, + 35.882081031796645 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 2, + "sovereignt": "Bahrain", + "sov_a3": "BHR", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Bahrain", + "adm0_a3": "BHR", + "geou_dif": 0, + "geounit": "Bahrain", + "gu_a3": "BHR", + "su_dif": 0, + "subunit": "Bahrain", + "su_a3": "BHR", + "brk_diff": 0, + "name": "Bahrain", + "name_long": "Bahrain", + "brk_a3": "BHR", + "brk_name": "Bahrain", + "brk_group": null, + "abbrev": "Bahr.", + "postal": "BH", + "formal_en": "Kingdom of Bahrain", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Bahrain", + "name_alt": null, + "mapcolor7": 1, + "mapcolor8": 1, + "mapcolor9": 1, + "mapcolor13": 9, + "pop_est": 727785, + "gdp_md_est": 26820, + "pop_year": -99, + "lastcensus": 2010, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "BH", + "iso_a3": "BHR", + "iso_n3": "048", + "un_a3": "048", + "wb_a2": "BH", + "wb_a3": "BHR", + "woe_id": -99, + "adm0_a3_is": "BHR", + "adm0_a3_us": "BHR", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Asia", + "region_un": "Asia", + "subregion": "Western Asia", + "region_wb": "Middle East & North Africa", + "name_len": 7, + "long_len": 7, + "abbrev_len": 5, + "tiny": 2, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 50.553638136605, + 26.06944265390905 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 4, + "sovereignt": "Maldives", + "sov_a3": "MDV", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Maldives", + "adm0_a3": "MDV", + "geou_dif": 0, + "geounit": "Maldives", + "gu_a3": "MDV", + "su_dif": 0, + "subunit": "Maldives", + "su_a3": "MDV", + "brk_diff": 0, + "name": "Maldives", + "name_long": "Maldives", + "brk_a3": "MDV", + "brk_name": "Maldives", + "brk_group": null, + "abbrev": "Mald.", + "postal": "MV", + "formal_en": "Republic of Maldives", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Maldives", + "name_alt": null, + "mapcolor7": 2, + "mapcolor8": 3, + "mapcolor9": 1, + "mapcolor13": 7, + "pop_est": 396334, + "gdp_md_est": 1716, + "pop_year": -99, + "lastcensus": 2011, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "3. Upper middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "MV", + "iso_a3": "MDV", + "iso_n3": "462", + "un_a3": "462", + "wb_a2": "MV", + "wb_a3": "MDV", + "woe_id": -99, + "adm0_a3_is": "MDV", + "adm0_a3_us": "B13", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Seven seas (open ocean)", + "region_un": "Asia", + "subregion": "Southern Asia", + "region_wb": "South Asia", + "name_len": 8, + "long_len": 8, + "abbrev_len": 5, + "tiny": 2, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 73.50223056083513, + 4.186658727806048 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "United Kingdom", + "sov_a3": "GB1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "British Indian Ocean Territory", + "adm0_a3": "IOT", + "geou_dif": 0, + "geounit": "British Indian Ocean Territory", + "gu_a3": "IOT", + "su_dif": 0, + "subunit": "British Indian Ocean Territory", + "su_a3": "IOT", + "brk_diff": 1, + "name": "Br. Indian Ocean Ter.", + "name_long": "British Indian Ocean Territory", + "brk_a3": "B69", + "brk_name": "Br. Indian Ocean Ter.", + "brk_group": null, + "abbrev": "I.O.T.", + "postal": "IO", + "formal_en": null, + "formal_fr": null, + "note_adm0": "U.K.", + "note_brk": "Admin. by U.K.; Claimed by Mauritius and Seychelles", + "name_sort": "British Indian Ocean Territory", + "name_alt": null, + "mapcolor7": 6, + "mapcolor8": 6, + "mapcolor9": 6, + "mapcolor13": 3, + "pop_est": 4000, + "gdp_md_est": 160, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "2. Developed region: nonG7", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "IO", + "iso_a3": "IOT", + "iso_n3": "086", + "un_a3": "-099", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "IOT", + "adm0_a3_us": "IOT", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Seven seas (open ocean)", + "region_un": "Seven seas (open ocean)", + "subregion": "Seven seas (open ocean)", + "region_wb": "Sub-Saharan Africa", + "name_len": 21, + "long_len": 30, + "abbrev_len": 6, + "tiny": 5, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 72.47872949418257, + -7.340705873210993 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 3, + "sovereignt": "Singapore", + "sov_a3": "SGP", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Singapore", + "adm0_a3": "SGP", + "geou_dif": 0, + "geounit": "Singapore", + "gu_a3": "SGP", + "su_dif": 0, + "subunit": "Singapore", + "su_a3": "SGP", + "brk_diff": 0, + "name": "Singapore", + "name_long": "Singapore", + "brk_a3": "SGP", + "brk_name": "Singapore", + "brk_group": null, + "abbrev": "Sing.", + "postal": "SG", + "formal_en": "Republic of Singapore", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Singapore", + "name_alt": null, + "mapcolor7": 5, + "mapcolor8": 3, + "mapcolor9": 7, + "mapcolor13": 3, + "pop_est": 4657542, + "gdp_md_est": 237300, + "pop_year": -99, + "lastcensus": 2010, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "SG", + "iso_a3": "SGP", + "iso_n3": "702", + "un_a3": "702", + "wb_a2": "SG", + "wb_a3": "SGP", + "woe_id": -99, + "adm0_a3_is": "SGP", + "adm0_a3_us": "SGP", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Asia", + "region_un": "Asia", + "subregion": "South-Eastern Asia", + "region_wb": "East Asia & Pacific", + "name_len": 9, + "long_len": 9, + "abbrev_len": 5, + "tiny": 3, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 103.81481982900323, + 1.359363931813562 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 2, + "sovereignt": "Brunei", + "sov_a3": "BRN", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Brunei", + "adm0_a3": "BRN", + "geou_dif": 0, + "geounit": "Brunei", + "gu_a3": "BRN", + "su_dif": 0, + "subunit": "Brunei", + "su_a3": "BRN", + "brk_diff": 0, + "name": "Brunei", + "name_long": "Brunei Darussalam", + "brk_a3": "BRN", + "brk_name": "Brunei", + "brk_group": null, + "abbrev": "Brunei", + "postal": "BN", + "formal_en": "Negara Brunei Darussalam", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Brunei", + "name_alt": null, + "mapcolor7": 4, + "mapcolor8": 6, + "mapcolor9": 6, + "mapcolor13": 12, + "pop_est": 388190, + "gdp_md_est": 20250, + "pop_year": -99, + "lastcensus": 2001, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "BN", + "iso_a3": "BRN", + "iso_n3": "096", + "un_a3": "096", + "wb_a2": "BN", + "wb_a3": "BRN", + "woe_id": -99, + "adm0_a3_is": "BRN", + "adm0_a3_us": "BRN", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Asia", + "region_un": "Asia", + "subregion": "South-Eastern Asia", + "region_wb": "East Asia & Pacific", + "name_len": 6, + "long_len": 17, + "abbrev_len": 6, + "tiny": 2, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 114.56745460338925, + 4.434669496170784 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "Palau", + "sov_a3": "PLW", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Palau", + "adm0_a3": "PLW", + "geou_dif": 0, + "geounit": "Palau", + "gu_a3": "PLW", + "su_dif": 0, + "subunit": "Palau", + "su_a3": "PLW", + "brk_diff": 0, + "name": "Palau", + "name_long": "Palau", + "brk_a3": "PLW", + "brk_name": "Palau", + "brk_group": null, + "abbrev": "Palau", + "postal": "PW", + "formal_en": "Republic of Palau", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Palau", + "name_alt": null, + "mapcolor7": 2, + "mapcolor8": 5, + "mapcolor9": 1, + "mapcolor13": 12, + "pop_est": 20796, + "gdp_md_est": 164, + "pop_year": -99, + "lastcensus": 2010, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "3. Upper middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "PW", + "iso_a3": "PLW", + "iso_n3": "585", + "un_a3": "585", + "wb_a2": "PW", + "wb_a3": "PLW", + "woe_id": -99, + "adm0_a3_is": "PLW", + "adm0_a3_us": "PLW", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Micronesia", + "region_wb": "East Asia & Pacific", + "name_len": 5, + "long_len": 5, + "abbrev_len": 5, + "tiny": 2, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 134.57924133620793, + 7.507494163314107 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 6, + "sovereignt": "United States of America", + "sov_a3": "US1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "Northern Mariana Islands", + "adm0_a3": "MNP", + "geou_dif": 0, + "geounit": "Northern Mariana Islands", + "gu_a3": "MNP", + "su_dif": 0, + "subunit": "Northern Mariana Islands", + "su_a3": "MNP", + "brk_diff": 0, + "name": "N. Mariana Is.", + "name_long": "Northern Mariana Islands", + "brk_a3": "MNP", + "brk_name": "N. Mariana Is.", + "brk_group": null, + "abbrev": "N.M.I.", + "postal": "MP", + "formal_en": "Commonwealth of the Northern Mariana Islands", + "formal_fr": null, + "note_adm0": "Commonwealth of U.S.A.", + "note_brk": null, + "name_sort": "Northern Mariana Islands", + "name_alt": null, + "mapcolor7": 4, + "mapcolor8": 5, + "mapcolor9": 1, + "mapcolor13": 1, + "pop_est": 88662, + "gdp_md_est": 900, + "pop_year": -99, + "lastcensus": 2010, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "MP", + "iso_a3": "MNP", + "iso_n3": "580", + "un_a3": "580", + "wb_a2": "MP", + "wb_a3": "MNP", + "woe_id": -99, + "adm0_a3_is": "MNP", + "adm0_a3_us": "MNP", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Micronesia", + "region_wb": "East Asia & Pacific", + "name_len": 14, + "long_len": 24, + "abbrev_len": 6, + "tiny": 3, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 145.73926332724704, + 15.17463695328189 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "United States of America", + "sov_a3": "US1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "Guam", + "adm0_a3": "GUM", + "geou_dif": 0, + "geounit": "Guam", + "gu_a3": "GUM", + "su_dif": 0, + "subunit": "Guam", + "su_a3": "GUM", + "brk_diff": 0, + "name": "Guam", + "name_long": "Guam", + "brk_a3": "GUM", + "brk_name": "Guam", + "brk_group": null, + "abbrev": "Guam", + "postal": "GU", + "formal_en": "Territory of Guam", + "formal_fr": null, + "note_adm0": "U.S.A.", + "note_brk": null, + "name_sort": "Guam", + "name_alt": null, + "mapcolor7": 4, + "mapcolor8": 5, + "mapcolor9": 1, + "mapcolor13": 1, + "pop_est": 178430, + "gdp_md_est": 2500, + "pop_year": -99, + "lastcensus": 2010, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "GU", + "iso_a3": "GUM", + "iso_n3": "316", + "un_a3": "316", + "wb_a2": "GU", + "wb_a3": "GUM", + "woe_id": -99, + "adm0_a3_is": "GUM", + "adm0_a3_us": "GUM", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Micronesia", + "region_wb": "East Asia & Pacific", + "name_len": 4, + "long_len": 4, + "abbrev_len": 4, + "tiny": 2, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 144.77003842181864, + 13.459684857600507 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 6, + "sovereignt": "Federated States of Micronesia", + "sov_a3": "FSM", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Federated States of Micronesia", + "adm0_a3": "FSM", + "geou_dif": 0, + "geounit": "Federated States of Micronesia", + "gu_a3": "FSM", + "su_dif": 0, + "subunit": "Federated States of Micronesia", + "su_a3": "FSM", + "brk_diff": 0, + "name": "Micronesia", + "name_long": "Federated States of Micronesia", + "brk_a3": "FSM", + "brk_name": "Micronesia", + "brk_group": null, + "abbrev": "F.S.M.", + "postal": "FSM", + "formal_en": "Federated States of Micronesia", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Micronesia, Federated States of", + "name_alt": null, + "mapcolor7": 5, + "mapcolor8": 2, + "mapcolor9": 4, + "mapcolor13": 13, + "pop_est": 107434, + "gdp_md_est": 238.1, + "pop_year": -99, + "lastcensus": 2000, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "4. Lower middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "FM", + "iso_a3": "FSM", + "iso_n3": "583", + "un_a3": "583", + "wb_a2": "FM", + "wb_a3": "FSM", + "woe_id": -99, + "adm0_a3_is": "FSM", + "adm0_a3_us": "FSM", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Micronesia", + "region_wb": "East Asia & Pacific", + "name_len": 10, + "long_len": 30, + "abbrev_len": 6, + "tiny": -99, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 158.2420151934607, + 6.885941535379288 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "Marshall Islands", + "sov_a3": "MHL", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Marshall Islands", + "adm0_a3": "MHL", + "geou_dif": 0, + "geounit": "Marshall Islands", + "gu_a3": "MHL", + "su_dif": 0, + "subunit": "Marshall Islands", + "su_a3": "MHL", + "brk_diff": 0, + "name": "Marshall Is.", + "name_long": "Marshall Islands", + "brk_a3": "MHL", + "brk_name": "Marshall Is.", + "brk_group": null, + "abbrev": "M. Is.", + "postal": "MH", + "formal_en": "Republic of the Marshall Islands", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Marshall Islands", + "name_alt": null, + "mapcolor7": 2, + "mapcolor8": 5, + "mapcolor9": 5, + "mapcolor13": 3, + "pop_est": 64522, + "gdp_md_est": 133.5, + "pop_year": -99, + "lastcensus": 2011, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "4. Lower middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "MH", + "iso_a3": "MHL", + "iso_n3": "584", + "un_a3": "584", + "wb_a2": "MH", + "wb_a3": "MHL", + "woe_id": -99, + "adm0_a3_is": "MHL", + "adm0_a3_us": "MHL", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Micronesia", + "region_wb": "East Asia & Pacific", + "name_len": 12, + "long_len": 16, + "abbrev_len": 6, + "tiny": 2, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 168.72896600641184, + 7.313460144816133 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "Kiribati", + "sov_a3": "KIR", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Kiribati", + "adm0_a3": "KIR", + "geou_dif": 0, + "geounit": "Kiribati", + "gu_a3": "KIR", + "su_dif": 0, + "subunit": "Kiribati", + "su_a3": "KIR", + "brk_diff": 0, + "name": "Kiribati", + "name_long": "Kiribati", + "brk_a3": "KIR", + "brk_name": "Kiribati", + "brk_group": null, + "abbrev": "Kir.", + "postal": "KI", + "formal_en": "Republic of Kiribati", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Kiribati", + "name_alt": null, + "mapcolor7": 5, + "mapcolor8": 7, + "mapcolor9": 6, + "mapcolor13": 12, + "pop_est": 112850, + "gdp_md_est": 579.5, + "pop_year": -99, + "lastcensus": 2005, + "gdp_year": -99, + "economy": "7. Least developed region", + "income_grp": "4. Lower middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "KI", + "iso_a3": "KIR", + "iso_n3": "296", + "un_a3": "296", + "wb_a2": "KI", + "wb_a3": "KIR", + "woe_id": -99, + "adm0_a3_is": "KIR", + "adm0_a3_us": "KIR", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Micronesia", + "region_wb": "East Asia & Pacific", + "name_len": 8, + "long_len": 8, + "abbrev_len": 4, + "tiny": 2, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 173.13515838316619, + 1.364258124187756 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 6, + "sovereignt": "Nauru", + "sov_a3": "NRU", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Nauru", + "adm0_a3": "NRU", + "geou_dif": 0, + "geounit": "Nauru", + "gu_a3": "NRU", + "su_dif": 0, + "subunit": "Nauru", + "su_a3": "NRU", + "brk_diff": 0, + "name": "Nauru", + "name_long": "Nauru", + "brk_a3": "NRU", + "brk_name": "Nauru", + "brk_group": null, + "abbrev": "Nauru", + "postal": "NR", + "formal_en": "Republic of Nauru", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Nauru", + "name_alt": null, + "mapcolor7": 3, + "mapcolor8": 7, + "mapcolor9": 6, + "mapcolor13": 9, + "pop_est": 14019, + "gdp_md_est": 60, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "4. Lower middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "NR", + "iso_a3": "NRU", + "iso_n3": "520", + "un_a3": "520", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "NRU", + "adm0_a3_us": "NRU", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Micronesia", + "region_wb": "East Asia & Pacific", + "name_len": 5, + "long_len": 5, + "abbrev_len": 5, + "tiny": 3, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 166.93748256244703, + -0.523068535976108 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 5, + "sr_label_o": 6, + "sovereignt": "Tuvalu", + "sov_a3": "TUV", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Tuvalu", + "adm0_a3": "TUV", + "geou_dif": 0, + "geounit": "Tuvalu", + "gu_a3": "TUV", + "su_dif": 0, + "subunit": "Tuvalu", + "su_a3": "TUV", + "brk_diff": 0, + "name": "Tuvalu", + "name_long": "Tuvalu", + "brk_a3": "TUV", + "brk_name": "Tuvalu", + "brk_group": null, + "abbrev": "Tuv.", + "postal": "TV", + "formal_en": "Tuvalu", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Tuvalu", + "name_alt": null, + "mapcolor7": 1, + "mapcolor8": 3, + "mapcolor9": 8, + "mapcolor13": 5, + "pop_est": 12373, + "gdp_md_est": 14.94, + "pop_year": -99, + "lastcensus": 2002, + "gdp_year": -99, + "economy": "7. Least developed region", + "income_grp": "3. Upper middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "TV", + "iso_a3": "TUV", + "iso_n3": "798", + "un_a3": "798", + "wb_a2": "TV", + "wb_a3": "TUV", + "woe_id": -99, + "adm0_a3_is": "TUV", + "adm0_a3_us": "TUV", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Oceania", + "region_un": "Oceania", + "subregion": "Polynesia", + "region_wb": "East Asia & Pacific", + "name_len": 6, + "long_len": 6, + "abbrev_len": 4, + "tiny": 5, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries Pacific" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 179.20397422623353, + -8.49972371316585 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 4, + "sovereignt": "Mauritius", + "sov_a3": "MUS", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Mauritius", + "adm0_a3": "MUS", + "geou_dif": 0, + "geounit": "Mauritius", + "gu_a3": "MUS", + "su_dif": 0, + "subunit": "Mauritius", + "su_a3": "MUS", + "brk_diff": 0, + "name": "Mauritius", + "name_long": "Mauritius", + "brk_a3": "MUS", + "brk_name": "Mauritius", + "brk_group": null, + "abbrev": "Mus.", + "postal": "MU", + "formal_en": "Republic of Mauritius", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Mauritius", + "name_alt": null, + "mapcolor7": 1, + "mapcolor8": 3, + "mapcolor9": 5, + "mapcolor13": 7, + "pop_est": 1284264, + "gdp_md_est": 15270, + "pop_year": -99, + "lastcensus": 2011, + "gdp_year": -99, + "economy": "6. Developing region", + "income_grp": "3. Upper middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "MU", + "iso_a3": "MUS", + "iso_n3": "480", + "un_a3": "480", + "wb_a2": "MU", + "wb_a3": "MUS", + "woe_id": -99, + "adm0_a3_is": "MUS", + "adm0_a3_us": "MUS", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Seven seas (open ocean)", + "region_un": "Africa", + "subregion": "Eastern Africa", + "region_wb": "Sub-Saharan Africa", + "name_len": 9, + "long_len": 9, + "abbrev_len": 4, + "tiny": 2, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 57.58565995816849, + -20.302274672122962 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "Comoros", + "sov_a3": "COM", + "adm0_dif": 0, + "level": 2, + "type": "Sovereign country", + "admin": "Comoros", + "adm0_a3": "COM", + "geou_dif": 0, + "geounit": "Comoros", + "gu_a3": "COM", + "su_dif": 0, + "subunit": "Comoros", + "su_a3": "COM", + "brk_diff": 0, + "name": "Comoros", + "name_long": "Comoros", + "brk_a3": "COM", + "brk_name": "Comoros", + "brk_group": null, + "abbrev": "Com.", + "postal": "KM", + "formal_en": "Union of the Comoros", + "formal_fr": null, + "note_adm0": null, + "note_brk": null, + "name_sort": "Comoros", + "name_alt": null, + "mapcolor7": 2, + "mapcolor8": 1, + "mapcolor9": 4, + "mapcolor13": 10, + "pop_est": 752438, + "gdp_md_est": 751.2, + "pop_year": -99, + "lastcensus": 2003, + "gdp_year": -99, + "economy": "7. Least developed region", + "income_grp": "5. Low income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "KM", + "iso_a3": "COM", + "iso_n3": "174", + "un_a3": "174", + "wb_a2": "KM", + "wb_a3": "COM", + "woe_id": -99, + "adm0_a3_is": "COM", + "adm0_a3_us": "COM", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Africa", + "region_un": "Africa", + "subregion": "Eastern Africa", + "region_wb": "Sub-Saharan Africa", + "name_len": 7, + "long_len": 7, + "abbrev_len": 4, + "tiny": 2, + "homepart": 1, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + 43.337943198143535, + -11.715555516231973 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 6, + "sovereignt": "Denmark", + "sov_a3": "DN1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "Faroe Islands", + "adm0_a3": "FRO", + "geou_dif": 0, + "geounit": "Faroe Islands", + "gu_a3": "FRO", + "su_dif": 0, + "subunit": "Faroe Islands", + "su_a3": "FRO", + "brk_diff": 0, + "name": "Faeroe Is.", + "name_long": "Faeroe Islands", + "brk_a3": "FRO", + "brk_name": "Faeroe Islands", + "brk_group": null, + "abbrev": "Faeroe Is.", + "postal": "FO", + "formal_en": "Føroyar Is. (Faeroe Is.)", + "formal_fr": null, + "note_adm0": "Den.", + "note_brk": null, + "name_sort": "Faeroe Islands", + "name_alt": null, + "mapcolor7": 4, + "mapcolor8": 1, + "mapcolor9": 3, + "mapcolor13": 12, + "pop_est": 48856, + "gdp_md_est": 1000, + "pop_year": -99, + "lastcensus": 2011, + "gdp_year": -99, + "economy": "2. Developed region: nonG7", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "FO", + "iso_a3": "FRO", + "iso_n3": "234", + "un_a3": "234", + "wb_a2": "FO", + "wb_a3": "FRO", + "woe_id": -99, + "adm0_a3_is": "FRO", + "adm0_a3_us": "FRO", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Europe", + "region_un": "Europe", + "subregion": "Northern Europe", + "region_wb": "Europe & Central Asia", + "name_len": 10, + "long_len": 14, + "abbrev_len": 10, + "tiny": 3, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -6.942567803221323, + 62.19161776035833 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "Norway", + "sov_a3": "NOR", + "adm0_dif": 0, + "level": 3, + "type": "Geo unit", + "admin": "Norway", + "adm0_a3": "NOR", + "geou_dif": 1, + "geounit": "Jan Mayen", + "gu_a3": "NJM", + "su_dif": 0, + "subunit": "Jan Mayen", + "su_a3": "NJM", + "brk_diff": 0, + "name": "Jan Mayen I.", + "name_long": "Jan Mayen Island", + "brk_a3": "NJM", + "brk_name": "Jan Mayen", + "brk_group": null, + "abbrev": "J.M.", + "postal": "JM", + "formal_en": null, + "formal_fr": null, + "note_adm0": "Nor.", + "note_brk": null, + "name_sort": "Jan Mayen I.", + "name_alt": null, + "mapcolor7": 5, + "mapcolor8": 3, + "mapcolor9": 8, + "mapcolor13": 12, + "pop_est": 20, + "gdp_md_est": -99, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "7. Least developed region", + "income_grp": "5. Low income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "-99", + "iso_a3": "-99", + "iso_n3": "-99", + "un_a3": "-099", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "SJM", + "adm0_a3_us": "NOR", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Europe", + "region_un": "Europe", + "subregion": "Northern Europe", + "region_wb": "Europe & Central Asia", + "name_len": 12, + "long_len": 16, + "abbrev_len": 4, + "tiny": -99, + "homepart": -99, + "featureclass": "Admin-0 Tiny GeoUnit" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -8.420617438175157, + 71.02824880643254 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 6, + "sovereignt": "France", + "sov_a3": "FR1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "Saint Pierre and Miquelon", + "adm0_a3": "SPM", + "geou_dif": 0, + "geounit": "Saint Pierre and Miquelon", + "gu_a3": "SPM", + "su_dif": 0, + "subunit": "Saint Pierre and Miquelon", + "su_a3": "SPM", + "brk_diff": 0, + "name": "St. Pierre and Miquelon", + "name_long": "Saint Pierre and Miquelon", + "brk_a3": "SPM", + "brk_name": "St. Pierre and Miquelon", + "brk_group": null, + "abbrev": "St. P.M.", + "postal": "PM", + "formal_en": "Saint Pierre and Miquelon", + "formal_fr": null, + "note_adm0": "Fr.", + "note_brk": null, + "name_sort": "St. Pierre and Miquelon", + "name_alt": null, + "mapcolor7": 7, + "mapcolor8": 5, + "mapcolor9": 9, + "mapcolor13": 11, + "pop_est": 7051, + "gdp_md_est": 48.3, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "2. Developed region: nonG7", + "income_grp": "3. Upper middle income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "PM", + "iso_a3": "SPM", + "iso_n3": "666", + "un_a3": "666", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "SPM", + "adm0_a3_us": "SPM", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "North America", + "region_un": "Americas", + "subregion": "Northern America", + "region_wb": "North America", + "name_len": 23, + "long_len": 25, + "abbrev_len": 8, + "tiny": 3, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -56.31570304234327, + 46.85746558614022 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 4, + "sovereignt": "United Kingdom", + "sov_a3": "GB1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "Bermuda", + "adm0_a3": "BMU", + "geou_dif": 0, + "geounit": "Bermuda", + "gu_a3": "BMU", + "su_dif": 0, + "subunit": "Bermuda", + "su_a3": "BMU", + "brk_diff": 0, + "name": "Bermuda", + "name_long": "Bermuda", + "brk_a3": "BMU", + "brk_name": "Bermuda", + "brk_group": null, + "abbrev": "Berm.", + "postal": "BM", + "formal_en": "The Bermudas or Somers Isles", + "formal_fr": null, + "note_adm0": "U.K.", + "note_brk": null, + "name_sort": "Bermuda", + "name_alt": null, + "mapcolor7": 6, + "mapcolor8": 6, + "mapcolor9": 6, + "mapcolor13": 3, + "pop_est": 67837, + "gdp_md_est": 4500, + "pop_year": -99, + "lastcensus": 2010, + "gdp_year": -99, + "economy": "2. Developed region: nonG7", + "income_grp": "2. High income: nonOECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "BM", + "iso_a3": "BMU", + "iso_n3": "060", + "un_a3": "060", + "wb_a2": "BM", + "wb_a3": "BMU", + "woe_id": -99, + "adm0_a3_is": "BMU", + "adm0_a3_us": "BMU", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "North America", + "region_un": "Americas", + "subregion": "Northern America", + "region_wb": "North America", + "name_len": 7, + "long_len": 7, + "abbrev_len": 5, + "tiny": 4, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -64.74797798630703, + 32.307221641280876 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "Portugal", + "sov_a3": "PRT", + "adm0_dif": 0, + "level": 3, + "type": "Geo unit", + "admin": "Portugal", + "adm0_a3": "PRT", + "geou_dif": 1, + "geounit": "Azores", + "gu_a3": "PAZ", + "su_dif": 0, + "subunit": "Azores", + "su_a3": "PAZ", + "brk_diff": 0, + "name": "Azores", + "name_long": "Azores", + "brk_a3": "PAZ", + "brk_name": "Azores", + "brk_group": null, + "abbrev": "Az.", + "postal": "AZ", + "formal_en": null, + "formal_fr": null, + "note_adm0": "Port.", + "note_brk": null, + "name_sort": "Azores", + "name_alt": null, + "mapcolor7": 1, + "mapcolor8": 7, + "mapcolor9": 1, + "mapcolor13": 4, + "pop_est": 235374, + "gdp_md_est": 4492, + "pop_year": 0, + "lastcensus": -99, + "gdp_year": 0, + "economy": "2. Developed region: nonG7", + "income_grp": "1. High income: OECD", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "-99", + "iso_a3": "-99", + "iso_n3": "-99", + "un_a3": "-099", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "-99", + "adm0_a3_us": "PRT", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Seven seas (open ocean)", + "region_un": "Europe", + "subregion": "Southern Europe", + "region_wb": "Europe & Central Asia", + "name_len": 6, + "long_len": 6, + "abbrev_len": 3, + "tiny": -99, + "homepart": -99, + "featureclass": "Admin-0 Tiny GeoUnit" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -28.423474244011175, + 38.48233011770992 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 2, + "sr_label_o": 6, + "sovereignt": "Spain", + "sov_a3": "ESP", + "adm0_dif": 0, + "level": 4, + "type": "Geo subunit", + "admin": "Spain", + "adm0_a3": "ESP", + "geou_dif": 0, + "geounit": "Spain", + "gu_a3": "ESP", + "su_dif": 1, + "subunit": "Canary Islands", + "su_a3": "ESC", + "brk_diff": 0, + "name": "Canary Is.", + "name_long": "Canary Islands", + "brk_a3": "ESC", + "brk_name": "Canary Is.", + "brk_group": null, + "abbrev": "Can. Is.", + "postal": "CI", + "formal_en": null, + "formal_fr": null, + "note_adm0": "Sp.", + "note_brk": null, + "name_sort": "Canary Islands", + "name_alt": null, + "mapcolor7": 4, + "mapcolor8": 5, + "mapcolor9": 5, + "mapcolor13": 5, + "pop_est": 2098593, + "gdp_md_est": 72654.55481, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "-99", + "income_grp": "-99", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "-99", + "iso_a3": "-99", + "iso_n3": "-99", + "un_a3": "-099", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "ESP", + "adm0_a3_us": "ESP", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Africa", + "region_un": "Europe", + "subregion": "Southern Europe", + "region_wb": "Europe & Central Asia", + "name_len": 10, + "long_len": 14, + "abbrev_len": 8, + "tiny": -99, + "homepart": -99, + "featureclass": "Admin-0 Tiny GeoSubunit" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -16.592772263568634, + 28.228989968662177 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 6, + "sovereignt": "Portugal", + "sov_a3": "PRT", + "adm0_dif": 0, + "level": 3, + "type": "Geo unit", + "admin": "Portugal", + "adm0_a3": "PRT", + "geou_dif": 1, + "geounit": "Madeira", + "gu_a3": "PMD", + "su_dif": 0, + "subunit": "Madeira", + "su_a3": "PMD", + "brk_diff": 0, + "name": "Madeira", + "name_long": "Madeira", + "brk_a3": "PMD", + "brk_name": "Madeira", + "brk_group": null, + "abbrev": "Mad.", + "postal": "MD", + "formal_en": null, + "formal_fr": null, + "note_adm0": "Port.", + "note_brk": null, + "name_sort": "Madeira", + "name_alt": null, + "mapcolor7": 1, + "mapcolor8": 7, + "mapcolor9": 1, + "mapcolor13": 4, + "pop_est": 267785, + "gdp_md_est": 6414, + "pop_year": 0, + "lastcensus": -99, + "gdp_year": 0, + "economy": "2. Developed region: nonG7", + "income_grp": "1. High income: OECD", + "wikipedia": 0, + "fips_10": null, + "iso_a2": "-99", + "iso_a3": "-99", + "iso_n3": "-99", + "un_a3": "-099", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "-99", + "adm0_a3_us": "PRT", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Africa", + "region_un": "Europe", + "subregion": "Southern Europe", + "region_wb": "Europe & Central Asia", + "name_len": 7, + "long_len": 7, + "abbrev_len": 4, + "tiny": -99, + "homepart": -99, + "featureclass": "Admin-0 Tiny GeoUnit" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -16.959751345358598, + 32.74536514049669 + ] + } + }, + { + "type": "Feature", + "properties": { + "scalerank": 2, + "sr_label_i": 3, + "sr_label_o": 6, + "sovereignt": "United Kingdom", + "sov_a3": "GB1", + "adm0_dif": 1, + "level": 2, + "type": "Dependency", + "admin": "South Georgia and South Sandwich Islands", + "adm0_a3": "SGS", + "geou_dif": 0, + "geounit": "South Georgia and South Sandwich Islands", + "gu_a3": "SGS", + "su_dif": 0, + "subunit": "South Georgia and South Sandwich Islands", + "su_a3": "SGS", + "brk_diff": 0, + "name": "S. Geo. and S. Sandw. Is.", + "name_long": "South Georgia and South Sandwich Islands", + "brk_a3": "SGS", + "brk_name": "S. Geo. and S. Sandw. Is.", + "brk_group": null, + "abbrev": "S.G. S.S. Is.", + "postal": "GS", + "formal_en": "South Georgia and South Sandwich Islands", + "formal_fr": null, + "note_adm0": "U.K.", + "note_brk": null, + "name_sort": "South Georgia and the Islands", + "name_alt": null, + "mapcolor7": 6, + "mapcolor8": 6, + "mapcolor9": 6, + "mapcolor13": 3, + "pop_est": 30, + "gdp_md_est": 0.3, + "pop_year": -99, + "lastcensus": -99, + "gdp_year": -99, + "economy": "7. Least developed region", + "income_grp": "5. Low income", + "wikipedia": -99, + "fips_10": null, + "iso_a2": "GS", + "iso_a3": "SGS", + "iso_n3": "239", + "un_a3": "-099", + "wb_a2": "-99", + "wb_a3": "-99", + "woe_id": -99, + "adm0_a3_is": "SGS", + "adm0_a3_us": "SGS", + "adm0_a3_un": -99, + "adm0_a3_wb": -99, + "continent": "Seven seas (open ocean)", + "region_un": "Seven seas (open ocean)", + "subregion": "Seven seas (open ocean)", + "region_wb": "Antarctica", + "name_len": 25, + "long_len": 40, + "abbrev_len": 13, + "tiny": 3, + "homepart": -99, + "featureclass": "Admin-0 Tiny Countries" + }, + "geometry": { + "type": "Point", + "coordinates": [ + -36.792143407672654, + -54.274478863695265 + ] + } + } + ] +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml new file mode 100644 index 0000000000..865baede0e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="menuitem_title_concurrent_infowindow">Concurrent Open InfoWindows</string> + <string name="menuitem_title_deselect_markers_on_tap">Deselect Markers On Tap</string> + <string name="menuitem_title_tracking_mode_dismiss_on_gesture">Dismiss location tracking on gesture</string> + <string name="menuitem_title_bearing_mode_dismiss_on_gesture">Dismiss bearing tracking on gesture</string> + <string name="menuitem_title_reset">Reset</string> + <string name="menuitem_title_rotate_gesture_enabled">Enable rotate gestures</string> + <string name="menuitem_title_scroll_gesture_enabled">Enable scroll gestures</string> + <string name="menuitem_title_change_location_source_lost">Change to LOST location source</string> + <string name="menuitem_title_change_location_source_mock">Change to mock location source</string> + <string name="menuitem_title_change_location_source_null">Reset location source to null</string> + <string name="menuitem_change_icon_overlap">Toggle icon overlap</string> + <string name="button_camera_move">Move</string> + <string name="button_camera_ease">Ease</string> + <string name="button_camera_animate">Animate</string> + <string name="button_user_dot_default">Default</string> + <string name="button_user_dot_tint">Tint dot</string> + <string name="button_user_accuracy_ring_tint">Tint ring</string> + <string name="button_user_transparent_tint">tran</string> + <string name="button_open_dialog">Open dialog</string> + <string name="button_download_region">Download region</string> + <string name="button_list_regions">List regions</string> + <string name="action_remove_polylines">Remove polylines</string> + <string name="action_visibility_polygon">Change visibility</string> + <string name="action_alpha_polygon">Change alpha</string> + <string name="action_points_polygon">Change points</string> + <string name="action_color_polygon">Change color</string> + <string name="action_holes_polygon">Change holes</string> + <string name="action_width_polyline">Change width</string> + <string name="action_calculate_distance">"Click the map to calculate the distance"</string> + <string name="action_scroll_by">Move the map by x/y pixels</string> + <string name="navigation_drawer_open">Open navigation drawer</string> + <string name="navigation_drawer_close">Close navigation drawer</string> + <string name="scrollby_x_value">X: %1$d</string> + <string name="scrollby_y_value">Y: %1$d</string> + <string name="dialog_camera_position">Animate to new position</string> + <string name="dynamic_marker_chelsea_title">Chelsea</string> + <string name="dynamic_marker_chelsea_snippet">Stamford Bridge</string> + <string name="dynamic_marker_arsenal_title">Arsenal</string> + <string name="dynamic_marker_arsenal_snippet">Emirates Stadium</string> + <string name="debug_zoom">Zoom: %.2f</string> + <string name="viewcache_size">ViewCache size %.2f</string> + <string name="latitude">Latitude</string> + <string name="min_value">-180</string> + <string name="longitude">Longitude</string> + <string name="zoom">Zoom</string> + <string name="default_zoom_value">18</string> + <string name="bearing">Bearing</string> + <string name="default_tilt_value">0</string> + <string name="tilt">Tilt</string> + <string name="no_results">No Results</string> + <string name="change_intensity">Change intensity</string> + <string name="change_anchor">Change Anchor</string> + <string name="amount_of_markers">Amount of markers</string> + <string name="update_layer_invalidate">Update layer (invalidate)</string> + <string name="red">Red</string> + <string name="green">Green</string> + <string name="blue">Blue</string> + <string name="add_an_exponential_zoom_function">Add an exponential zoom function</string> + <string name="add_an_interval_zoom_function">Add an interval zoom function</string> + <string name="add_a_categorical_source_function">Add a categorical source function</string> + <string name="add_an_exponential_source_function">Add an exponential source function</string> + <string name="add_an_identity_source_function">Add an identity source function</string> + <string name="add_an_interval_source_function">Add an interval source function</string> + <string name="add_a_composite_categorical_function">Add a composite, categorical function</string> + <string name="add_a_composite_exponential_function">Add a composite, exponential function</string> + <string name="add_a_composite_interval_function">Add a composite, interval function</string> + <string name="my_location_tracking">My Location Tracking</string> + <string name="bangalore">Bangalore</string> + <string name="list_all_layers_in_the_style">List all layers in the style</string> + <string name="list_all_sources_in_the_style">List all sources in the style</string> + <string name="color_the_water">Color the water</string> + <string name="set_background_opacity">Set background opacity</string> + <string name="set_road_symbol_placement_to_point">Set road symbol placement to Point</string> + <string name="set_layer_visibility_to_false">Set layer visibility to false</string> + <string name="add_a_parks_layer">Add a parks layer</string> + <string name="add_a_dynamic_geojson_source">Add a dynamic GeoJSON source</string> + <string name="remove_buildings_layer">Remove buildings layer</string> + <string name="add_a_terrain_layer">Add a terrain layer</string> + <string name="add_a_satellite_layer">Add a satellite layer</string> + <string name="change_the_water_color_on_zoom">Change the water color on zoom</string> + <string name="custom_tiles">Custom tiles</string> + <string name="apply_filtered_fill">Apply filtered fill</string> + <string name="apply_filtered_line">Apply filtered line</string> + <string name="apply_numeric_fill_filter">Apply numeric fill filter</string> + <string name="toggle_text_size">Toggle text size</string> + <string name="toggle_text_field_contents">Toggle text field contents</string> + <string name="toggle_text_font">Toggle text font</string> + <string name="zoom_in">Zoom in</string> + <string name="zoom_out">Zoom out</string> + <string name="zoom_by_2">Zoom by 2</string> + <string name="zoom_to_point">Zoom to point</string> + <string name="zoom_to_4">Zoom to 4</string> +</resources>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml new file mode 100644 index 0000000000..9ade28ae8d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="category">category</string> + <string name="category_basic">_Basic</string> + <string name="category_annotation">Annotation</string> + <string name="category_camera">Camera</string> + <string name="category_custom_layer">Custom Layer</string> + <string name="category_fragment">Fragment</string> + <string name="category_imagegenerator">Image Generator</string> + <string name="category_infowindow">Info Window</string> + <string name="category_maplayout">Map Layout</string> + <string name="category_offline">Offline</string> + <string name="category_userlocation">User Location</string> + <string name="category_style">Styling</string> + <string name="category_features">Features</string> + <string name="category_storage">Storage</string> +</resources>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml new file mode 100644 index 0000000000..4d1f7eac38 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="description_user_location_tracking">Tracks the location of the user</string> + <string name="description_user_location_customization">Customize the location of the user</string> + <string name="description_user_location_dot_color">Customize the user location color</string> + <string name="description_user_location_toggle">Toggle location of the user on and off</string> + <string name="description_custom_location_engine">Customize location engine</string> + <string name="description_custom_layer">Overlay a custom native layer on the map</string> + <string name="description_info_window_adapter">Learn how to create a custom InfoWindow</string> + <string name="description_cameraposition">CameraPosition capabilities</string> + <string name="description_map_fragment">Showcase MapFragment</string> + <string name="description_map_fragment_support">Showcase SupportMapFragment</string> + <string name="description_multimap">Activity with multiple maps on screen</string> + <string name="description_press_for_marker">Add marker to map on long press</string> + <string name="description_camera_zoom">Different types of zoom methods</string> + <string name="description_minmax_zoom">Configure a max and min zoomlevel</string> + <string name="description_info_window">Learn how to handle the InfoWindow</string> + <string name="description_add_bulk_markers">Add Markers In Bulk to a Map</string> + <string name="description_camera_animation_types">Showcase the different animation types</string> + <string name="description_visible_bounds">Center the camera around a bounds</string> + <string name="description_dynamic_marker">Update position and icon</string> + <string name="description_map_padding">Map Padding example</string> + <string name="description_debug_mode">Debug Mode</string> + <string name="description_offline">Offline Map example</string> + <string name="description_update_metadata">Update metadata example</string> + <string name="description_offline_region_delete">Delete region example</string> + <string name="description_animated_marker">Animate the position change of a marker</string> + <string name="description_polyline">Add a polyline to a map</string> + <string name="description_polygon">Add a polygon to a map</string> + <string name="description_scroll_by">Scroll with pixels in x,y direction</string> + <string name="description_snapshot">Example to make a snapshot of the map</string> + <string name="description_doublemap">2 maps in a view hierarchy</string> + <string name="description_view_marker">Use an Android SDK View as marker</string> + <string name="description_dynamic_info_window_adapter">Learn how to create a dynamic custom InfoWindow</string> + <string name="description_viewpager">Use SupportMapFragments in a ViewPager</string> + <string name="description_runtime_style">Adopt the map style on the fly</string> + <string name="description_data_driven_style">Use functions to change the map appearance</string> + <string name="description_symbol_layer">Manipulate symbols at runtime</string> + <string name="description_custom_sprite">Use a custom sprite in a Symbol Layer</string> + <string name="description_geojson_clustering">Use GeoJson sources and dynamic layers to cluster information</string> + <string name="description_geojson_realtime">Use realtime GeoJSON data streams to move a symbol on your map</string> + <string name="description_print">Shows how to print a map</string> + <string name="description_query_rendered_feature_properties_point">Query rendered feature properties on click</string> + <string name="description_query_rendered_features_box_count">Count all rendered features in box</string> + <string name="description_query_rendered_features_box_symbol_count">Count all rendered symbols in box</string> + <string name="description_query_rendered_features_box_highlight">Hightligh buildings in box</string> + <string name="description_query_source_features">Query source for features</string> + <string name="description_simple_map">Shows a simple map</string> + <string name="description_map_change">Logs map change events to Logcat</string> + <string name="description_visibility_map">Changes visibility of map and view parent</string> + <string name="description_add_remove_markers">Change Symbol icon when zoom levels changes</string> + <string name="description_style_file">Use a local file as the style</string> + <string name="description_map_in_dialog">Display a map inside a dialog fragment</string> + <string name="description_marker_view_rectangle">Marker Views within a rectangle</string> + <string name="description_circle_layer">Show bus stops and route in Singapore</string> + <string name="description_url_transform">Transform urls on the fly</string> + <string name="description_restricted_bounds">Limit viewport to Iceland</string> + <string name="description_fill_extrusion_layer">Shows how to add 3D extruded shapes</string> + <string name="description_building_fill_extrusion_layer">Shows how to show 3D extruded buildings</string> + <string name="description_animated_image_source">Shows how to animate georeferenced images</string> + <string name="description_bottom_sheet">Show 2 MapView on screen with a bottom sheet</string> + <string name="description_map_snapshotter">Show a static bitmap taken with the MapSnapshotter</string> + <string name="description_camera_animator">Use Android SDK Animators to animate camera position changes</string> + <string name="description_symbol_generator">Use Android SDK Views as symbols</string> +</resources>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml index 402d42d485..0a43af09de 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml @@ -1,9 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <dimen name="circle_size">24dp</dimen> <dimen name="fab_margin">16dp</dimen> <dimen name="attr_margin">10dp</dimen> - <dimen name="coordinatebounds_margin">32dp</dimen> <dimen name="map_padding_left">96dp</dimen> <dimen name="map_padding_bottom">256dp</dimen> <dimen name="map_padding_right">32dp</dimen> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml index 74833105a4..15a916fac9 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml @@ -1,181 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Mapbox Android SDK TestApp</string> - - <!--Activity--> - <string name="activity_map_fragment_suport">Support Map Fragment</string> - <string name="activity_map_fragment">Map Fragment</string> - <string name="activity_multimap">Multiple Maps on Screen</string> - <string name="activity_add_bulk_markers">Add Markers In Bulk</string> - <string name="activity_animated_marker">Animated Markers</string> - <string name="activity_dynamic_marker">Dynamic Marker</string> - <string name="activity_polyline">Polyline</string> - <string name="activity_polygon">Polygon</string> - <string name="activity_press_for_marker">Press Map For Marker</string> - <string name="activity_view_marker">View Marker API</string> - <string name="activity_add_remove_markers">Add/Remove marker</string> - <string name="activity_info_window">Standard InfoWindow</string> - <string name="activity_infowindow_adapter">Custom InfoWindow</string> - <string name="activity_dynamic_infowindow_adapter">Custom Dynamic InfoWindow</string> - <string name="activity_camera_animation_types">Animation Types</string> - <string name="activity_camera_zoom">Zoom Methods</string> - <string name="activity_visible_coordinate_bounds">LatLngBounds Method</string> - <string name="activity_camera_position">CameraPosition Method</string> - <string name="activity_scroll_by">Scroll By Method</string> - <string name="activity_double_map">Double Map Activity</string> - <string name="activity_snapshot">Snapshot Activity</string> - <string name="activity_user_tracking_mode">User tracking mode</string> - <string name="activity_user_tracking_customization">User location drawable</string> - <string name="activity_user_dot_color">User location tint color</string> - <string name="activity_user_location_toggle">User location toggle</string> - <string name="activity_custom_location_engine">Custom location engine</string> - <string name="activity_custom_layer">Custom Layer</string> - <string name="activity_map_padding">Map Padding</string> - <string name="activity_debug_mode">Debug Mode</string> - <string name="activity_offline">Offline Map</string> - <string name="activity_update_metadata">Update metadata Map</string> - <string name="activity_offline_region_delete">Delete region</string> - <string name="activity_minmax_zoom">Min/Max Zoom</string> - <string name="activity_viewpager">ViewPager</string> - <string name="activity_runtime_style">Runtime Style</string> - <string name="activity_data_driven_style">Data Driven Style</string> - <string name="activity_circle_layer">Circle layer</string> - <string name="activity_style_file">Local Style file</string> - <string name="activity_geojson_clustering">GeoJson Clustering</string> - <string name="activity_geojson_realtime">Add live realtime data</string> - <string name="activity_print">Print a map</string> - <string name="activity_query_rendered_feature_properties">Query feature properties</string> - <string name="activity_query_rendered_features_box_count">Count features in box</string> - <string name="activity_query_rendered_features_box_symbol_count">Count symbols in box</string> - <string name="activity_query_rendered_features_box_highlight">Highlight features in box</string> - <string name="activity_query_source_features">Query source features</string> - <string name="activity_symbol_layer">Symbols</string> - <string name="activity_add_sprite">Add Custom Sprite</string> - <string name="activity_navigation_drawer">Android SDK View integration</string> - <string name="activity_simple_map">Simple Map</string> - <string name="activity_map_in_dialog">Dialog with map</string> - <string name="activity_marker_view_rectangle">Marker views in rectangle</string> - <string name="activity_url_transform">Url transform</string> - <string name="activity_restricted_bounds">Restrict camera to a bounds</string> - <string name="activity_fill_extrusion_layer">Fill extrusions</string> - <string name="activity_building_fill_extrusion_layer">Building layer</string> - - <!--Description--> - <string name="description_user_location_tracking">Tracks the location of the user</string> - <string name="description_user_location_customization">Customize the location of the user</string> - <string name="description_user_location_dot_color">Customize the user location color</string> - <string name="description_user_location_toggle">Toggle location of the user on and off</string> - <string name="description_custom_location_engine">Customize location engine</string> - <string name="description_custom_layer">Overlay a custom native layer on the map</string> - <string name="description_info_window_adapter">Learn how to create a custom InfoWindow</string> - <string name="description_cameraposition">CameraPosition capabilities</string> - <string name="description_map_fragment">Showcase MapFragment</string> - <string name="description_map_fragment_support">Showcase SupportMapFragment</string> - <string name="description_multimap">Activity with multiple maps on screen</string> - <string name="description_press_for_marker">Add marker to map on long press</string> - <string name="description_camera_zoom">Different types of zoom methods</string> - <string name="description_minmax_zoom">Configure a max and min zoomlevel</string> - <string name="description_info_window">Learn how to handle the InfoWindow</string> - <string name="description_add_bulk_markers">Add Markers In Bulk to a Map</string> - <string name="description_camera_animation_types">Showcase the different animation types</string> - <string name="description_visible_bounds">Center the camera around a bounds</string> - <string name="description_dynamic_marker">Update position and icon</string> - <string name="description_map_padding">Map Padding example</string> - <string name="description_debug_mode">Debug Mode</string> - <string name="description_offline">Offline Map example</string> - <string name="description_update_metadata">Update metadata example</string> - <string name="description_offline_region_delete">Delete region example</string> - <string name="description_animated_marker">Animate the position change of a marker</string> - <string name="description_polyline">Add a polyline to a map</string> - <string name="description_polygon">Add a polygon to a map</string> - <string name="description_scroll_by">Scroll with pixels in x,y direction</string> - <string name="description_snapshot">Example to make a snapshot of the map</string> - <string name="description_doublemap">2 maps in a view hierarchy</string> - <string name="description_view_marker">Use an Android SDK View as marker</string> - <string name="description_dynamic_info_window_adapter">Learn how to create a dynamic custom InfoWindow</string> - <string name="description_viewpager">Use SupportMapFragments in a ViewPager</string> - <string name="description_runtime_style">Adopt the map style on the fly</string> - <string name="description_data_driven_style">Use functions to change the map appearance</string> - <string name="description_symbol_layer">Manipulate symbols at runtime</string> - <string name="description_custom_sprite">Use a custom sprite in a Symbol Layer</string> - <string name="description_geojson_clustering">Use GeoJson sources and dynamic layers to cluster information</string> - <string name="description_geojson_realtime">Use realtime GeoJSON data streams to move a symbol on your map</string> - <string name="description_print">Shows how to print a map</string> - <string name="description_navigation_drawer">Test animation of Android SDK View components</string> - <string name="description_query_rendered_feature_properties_point">Query rendered feature properties on click</string> - <string name="description_query_rendered_features_box_count">Count all rendered features in box</string> - <string name="description_query_rendered_features_box_symbol_count">Count all rendered symbols in box</string> - <string name="description_query_rendered_features_box_highlight">Hightligh buildings in box</string> - <string name="description_query_source_features">Query source for features</string> - <string name="description_simple_map">Shows a simple map</string> - <string name="description_add_remove_markers">Based on zoom level</string> - <string name="description_style_file">Use a local file as the style</string> - <string name="description_map_in_dialog">Display a map inside a dialog fragment</string> - <string name="description_marker_view_rectangle">Marker Views within a rectangle</string> - <string name="description_circle_layer">Show bus stops and route in Singapore</string> - <string name="description_url_transform">Transform urls on the fly</string> - <string name="description_restricted_bounds">Limit viewport to Iceland</string> - <string name="description_fill_extrusion_layer">Shows how to add 3D extruded shapes</string> - <string name="description_building_fill_extrusion_layer">Shows how to show 3D extruded buildings</string> - - <!--Categories--> - <string name="category">category</string> - <string name="category_basic">_Basic</string> - <string name="category_annotation">Annotation</string> - <string name="category_camera">Camera</string> - <string name="category_custom_layer">Custom Layer</string> - <string name="category_fragment">Fragment</string> - <string name="category_imagegenerator">Image Generator</string> - <string name="category_infowindow">Info Window</string> - <string name="category_maplayout">Map Layout</string> - <string name="category_offline">Offline</string> - <string name="category_userlocation">User Location</string> - <string name="category_style">Styling</string> - <string name="category_features">Features</string> - <string name="category_storage">Storage</string> - - <!--Actions--> - <string name="action_remove_polylines">Remove polylines</string> - <string name="action_visibility_polygon">Change visibility</string> - <string name="action_alpha_polygon">Change alpha</string> - <string name="action_points_polygon">Change points</string> - <string name="action_color_polygon">Change color</string> - <string name="action_holes_polygon">Change holes</string> - <string name="action_width_polyline">Change width</string> - - <!--Menu--> - <string name="menuitem_title_concurrent_infowindow">Concurrent Open InfoWindows</string> - <string name="menuitem_title_deselect_markers_on_tap">Deselect Markers On Tap</string> - <string name="menuitem_title_tracking_mode_dismiss_on_gesture">Dismiss location tracking on gesture</string> - <string name="menuitem_title_bearing_mode_dismiss_on_gesture">Dismiss bearing tracking on gesture</string> - <string name="menuitem_title_reset">Reset</string> - <string name="menuitem_title_rotate_gesture_enabled">Enable rotate gestures</string> - <string name="menuitem_title_scroll_gesture_enabled">Enable scroll gestures</string> - <string name="menuitem_title_change_location_source_lost">Change to LOST location source</string> - <string name="menuitem_title_change_location_source_mock">Change to mock location source</string> - <string name="menuitem_title_change_location_source_null">Reset location source to null</string> - - <!--Button--> - <string name="button_camera_move">Move</string> - <string name="button_camera_ease">Ease</string> - <string name="button_camera_animate">Animate</string> - <string name="button_user_dot_default">Default</string> - <string name="button_user_dot_tint">Tint dot</string> - <string name="button_user_accuracy_ring_tint">Tint ring</string> - <string name="button_user_transparent_tint">tran</string> - <string name="button_open_dialog">Open dialog</string> - <string name="button_download_region">Download region</string> - <string name="button_list_regions">List regions</string> - - <!--Other--> - <string name="navigation_drawer_open">Open navigation drawer</string> - <string name="navigation_drawer_close">Close navigation drawer</string> - <string name="scrollby_x_value">X: %1$d</string> - <string name="scrollby_y_value">Y: %1$d</string> - <string name="dialog_camera_position">Animate to new position</string> - <string name="dynamic_marker_chelsea_title">Chelsea</string> - <string name="dynamic_marker_chelsea_snippet">Stamford Bridge</string> - <string name="dynamic_marker_arsenal_title">Arsenal</string> - <string name="dynamic_marker_arsenal_snippet">Emirates Stadium</string> - <string name="debug_zoom">Zoom: %s</string> </resources> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml new file mode 100644 index 0000000000..8f394d0eb4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="activity_map_fragment_suport">Support Map Fragment</string> + <string name="activity_map_fragment">Map Fragment</string> + <string name="activity_multimap">Multiple Maps on Screen</string> + <string name="activity_add_bulk_markers">Add Markers In Bulk</string> + <string name="activity_animated_marker">Animated Markers</string> + <string name="activity_dynamic_marker">Dynamic Marker</string> + <string name="activity_polyline">Polyline</string> + <string name="activity_polygon">Polygon</string> + <string name="activity_press_for_marker">Press Map For Marker</string> + <string name="activity_view_marker">View Marker API</string> + <string name="activity_add_remove_markers">Zoom function with SymbolLayer</string> + <string name="activity_info_window">Standard InfoWindow</string> + <string name="activity_infowindow_adapter">Custom InfoWindow</string> + <string name="activity_dynamic_infowindow_adapter">Custom Dynamic InfoWindow</string> + <string name="activity_camera_animation_types">Animation Types</string> + <string name="activity_camera_zoom">Zoom Methods</string> + <string name="activity_visible_coordinate_bounds">LatLngBounds Method</string> + <string name="activity_camera_position">CameraPosition Method</string> + <string name="activity_scroll_by">Scroll By Method</string> + <string name="activity_double_map">Double Map Activity</string> + <string name="activity_snapshot">Snapshot Activity</string> + <string name="activity_user_tracking_mode">User tracking mode</string> + <string name="activity_user_tracking_customization">User location drawable</string> + <string name="activity_user_dot_color">User location tint color</string> + <string name="activity_user_location_toggle">User location toggle</string> + <string name="activity_custom_location_engine">Custom location engine</string> + <string name="activity_custom_layer">Custom Layer</string> + <string name="activity_map_padding">Map Padding</string> + <string name="activity_debug_mode">Debug Mode</string> + <string name="activity_offline">Offline Map</string> + <string name="activity_update_metadata">Update metadata Map</string> + <string name="activity_offline_region_delete">Delete region</string> + <string name="activity_minmax_zoom">Min/Max Zoom</string> + <string name="activity_viewpager">ViewPager</string> + <string name="activity_runtime_style">Runtime Style</string> + <string name="activity_data_driven_style">Data Driven Style</string> + <string name="activity_circle_layer">Circle layer</string> + <string name="activity_style_file">Local Style file</string> + <string name="activity_geojson_clustering">GeoJson Clustering</string> + <string name="activity_geojson_realtime">Add live realtime data</string> + <string name="activity_print">Print a map</string> + <string name="activity_query_rendered_feature_properties">Query feature properties</string> + <string name="activity_query_rendered_features_box_count">Count features in box</string> + <string name="activity_query_rendered_features_box_symbol_count">Count symbols in box</string> + <string name="activity_query_rendered_features_box_highlight">Highlight features in box</string> + <string name="activity_query_source_features">Query source features</string> + <string name="activity_symbol_layer">Symbols</string> + <string name="activity_add_sprite">Add Custom Sprite</string> + <string name="activity_simple_map">Simple Map</string> + <string name="activity_map_change">Map Change Events</string> + <string name="activity_map_visibility">Visibility Map</string> + <string name="activity_map_in_dialog">Dialog with map</string> + <string name="activity_marker_view_rectangle">Marker views in rectangle</string> + <string name="activity_url_transform">Url transform</string> + <string name="activity_restricted_bounds">Restrict camera to a bounds</string> + <string name="activity_fill_extrusion_layer">Fill extrusions</string> + <string name="activity_building_fill_extrusion_layer">Building layer</string> + <string name="activity_animated_image_source">Animated Image Source</string> + <string name="activity_bottom_sheet">Bottom sheet</string> + <string name="activity_map_snapshotter">Map Snapshotter</string> + <string name="activity_camera_animator">Animator animation</string> + <string name="activity_symbol_generator">SymbolGenerator</string> +</resources>
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKWearTestApp/build.gradle deleted file mode 100644 index 6ac8961421..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/build.gradle +++ /dev/null @@ -1,50 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - - defaultConfig { - applicationId "com.mapbox.mapboxsdk.testapp" - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode rootProject.ext.versionCode - versionName rootProject.ext.versionName - } - - lintOptions { - disable 'MissingTranslation' - } - - buildTypes { - debug { - testCoverageEnabled = true - } - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} - -dependencies { - compile(project(':MapboxGLAndroidSDK')) { - transitive = true - } - - // Wear - compile rootProject.ext.dep.wearCompile - provided rootProject.ext.dep.wearProvided - - // Leak Canary - debugCompile rootProject.ext.dep.leakCanaryDebug - releaseCompile rootProject.ext.dep.leakCanaryRelease - testCompile rootProject.ext.dep.leakCanaryTest - - // Testing dependencies - testCompile rootProject.ext.dep.junit - testCompile rootProject.ext.dep.mockito -} - -apply from: 'gradle-config.gradle' -apply from: 'gradle-checkstyle.gradle' diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-checkstyle.gradle b/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-checkstyle.gradle deleted file mode 100644 index bfb8341dbc..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-checkstyle.gradle +++ /dev/null @@ -1,17 +0,0 @@ -apply plugin: 'checkstyle' - -checkstyle { - toolVersion = "7.1.1" // 7.3 - configFile = "../checkstyle.xml" as File -} - -task checkstyle(type: Checkstyle) { - description 'Checks if the code adheres to coding standards' - group 'verification' - configFile file("../checkstyle.xml") - source 'src' - include '**/*.java' - exclude '**/gen/**' - classpath = files() - ignoreFailures = false -} diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-config.gradle b/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-config.gradle deleted file mode 100644 index 27c13b935b..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/gradle-config.gradle +++ /dev/null @@ -1,22 +0,0 @@ -// -// Configuration file for gradle build execution. -// - -task accessToken { - def tokenFile = new File("MapboxGLAndroidSDKWearTestApp/src/main/res/values/developer-config.xml") - if (!tokenFile.exists()) { - String tokenFileContents = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + - "<resources>\n" + - " <string name=\"mapbox_access_token\">" + "$System.env.MAPBOX_ACCESS_TOKEN" + "</string>\n" + - "</resources>" - - if (tokenFileContents == null) { - throw new InvalidUserDataException("You must set the MAPBOX_ACCESS_TOKEN environment variable.") - } - tokenFile.write(tokenFileContents) - } -} - -gradle.projectsEvaluated { - preBuild.dependsOn('accessToken') -} diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/proguard-rules.pro b/platform/android/MapboxGLAndroidSDKWearTestApp/proguard-rules.pro deleted file mode 100644 index 362685b172..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/proguard-rules.pro +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /Users/cameron/Library/Android/sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/AndroidManifest.xml deleted file mode 100644 index 36588a89f5..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/AndroidManifest.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.mapbox.weartestapp"> - - <uses-feature android:name="android.hardware.type.watch"/> - - <uses-permission android:name="android.permission.WAKE_LOCK"/> - <uses-permission android:name="android.permission.INTERNET" /> - <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> - <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> - <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - - <application - android:name=".MapboxApplication" - android:allowBackup="true" - android:icon="@mipmap/ic_launcher" - android:label="@string/app_name" - android:supportsRtl="true" - android:theme="@android:style/Theme.DeviceDefault"> - <uses-library - android:name="com.google.android.wearable" - android:required="false"/> - - <activity - android:name="com.mapbox.weartestapp.activity.FeatureOverviewActivity" - android:label="@string/app_name"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - - <category android:name="android.intent.category.LAUNCHER"/> - </intent-filter> - </activity> - - <activity - android:name=".activity.SimpleWearMapActivity" - android:label="@string/activity_simple_mapview_title"> - <meta-data - android:name="android.support.PARENT_ACTIVITY" - android:value=".activity.FeatureOverviewActivity"/> - </activity> - - <service android:name="com.mapbox.services.android.telemetry.service.TelemetryService"/> - - </application> - -</manifest> diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java deleted file mode 100644 index cbbdcb8493..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/MapboxApplication.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.mapbox.weartestapp; - -import android.app.Application; -import android.os.StrictMode; - -import com.mapbox.mapboxsdk.Mapbox; -import com.squareup.leakcanary.LeakCanary; - -public class MapboxApplication extends Application { - - @Override - public void onCreate() { - super.onCreate(); - Mapbox.getInstance(getApplicationContext(), getString(R.string.mapbox_access_token)); - LeakCanary.install(this); - StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() - .detectDiskReads() - .detectDiskWrites() - .detectNetwork() // or .detectAll() for all detectable problems - .penaltyLog() - .build()); - StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() - .detectLeakedSqlLiteObjects() - .penaltyLog() - .penaltyDeath() - .build()); - } -} diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/FeatureOverviewActivity.java deleted file mode 100644 index 1fe8a6cf10..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/FeatureOverviewActivity.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.mapbox.weartestapp.activity; - -import android.content.Intent; -import android.os.Bundle; -import android.support.wearable.activity.WearableActivity; -import android.support.wearable.view.WearableRecyclerView; - -import com.mapbox.weartestapp.R; -import com.mapbox.weartestapp.adapter.FeatureAdapter; -import com.mapbox.weartestapp.model.Feature; -import com.mapbox.weartestapp.utils.OffsettingHelper; - -import java.util.ArrayList; -import java.util.List; - -public class FeatureOverviewActivity extends WearableActivity implements FeatureAdapter.ItemSelectedListener { - - private WearableRecyclerView wearableRecyclerView; - private List<Feature> exampleItemModels; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_feature_overview); - - wearableRecyclerView = (WearableRecyclerView) findViewById(R.id.recycler_launcher_view); - wearableRecyclerView.setHasFixedSize(true); - - OffsettingHelper offsettingHelper = new OffsettingHelper(); - - wearableRecyclerView.setOffsettingHelper(offsettingHelper); - - exampleItemModels = new ArrayList<>(); - exampleItemModels.add(new Feature(R.string.activity_simple_mapview_title, new Intent(FeatureOverviewActivity.this, - SimpleWearMapActivity.class))); - - FeatureAdapter exampleAdapter = new FeatureAdapter(FeatureOverviewActivity.this, exampleItemModels); - wearableRecyclerView.setAdapter(exampleAdapter); - - exampleAdapter.setListener(this); - } - - @Override - public void onItemSelected(int position) { - startActivity(exampleItemModels.get(position).getActivity()); - } -} diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/SimpleWearMapActivity.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/SimpleWearMapActivity.java deleted file mode 100644 index f5bca0e051..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/activity/SimpleWearMapActivity.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.mapbox.weartestapp.activity; - -import android.os.Bundle; -import android.support.wearable.activity.WearableActivity; - -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; -import com.mapbox.weartestapp.R; - -public class SimpleWearMapActivity extends WearableActivity { - - private MapView mapView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_simple_mapview); - - mapView = (MapView) findViewById(R.id.mapView); - mapView.onCreate(savedInstanceState); - mapView.getMapAsync(new OnMapReadyCallback() { - @Override - public void onMapReady(MapboxMap mapboxMap) { - - // Customize map with markers, polylines, etc. - } - }); - } - - @Override - protected void onStart() { - super.onStart(); - mapView.onStart(); - } - - @Override - protected void onResume() { - super.onResume(); - mapView.onResume(); - } - - @Override - protected void onPause() { - super.onPause(); - mapView.onPause(); - } - - @Override - protected void onStop() { - super.onStop(); - mapView.onStop(); - } - - @Override - public void onLowMemory() { - super.onLowMemory(); - mapView.onLowMemory(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mapView.onDestroy(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - mapView.onSaveInstanceState(outState); - } -} diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/adapter/FeatureAdapter.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/adapter/FeatureAdapter.java deleted file mode 100644 index 1ef17e2d7a..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/adapter/FeatureAdapter.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.mapbox.weartestapp.adapter; - -import android.content.Context; -import android.support.v7.widget.RecyclerView; -import android.support.wearable.view.WearableRecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.mapbox.weartestapp.R; -import com.mapbox.weartestapp.model.Feature; - -import java.util.List; - -public class FeatureAdapter extends WearableRecyclerView.Adapter<FeatureAdapter.ViewHolder> { - - private List<Feature> data; - private Context context; - private ItemSelectedListener itemSelectedListener; - - public FeatureAdapter(Context context, List<Feature> data) { - this.context = context; - this.data = data; - } - - static class ViewHolder extends RecyclerView.ViewHolder { - - private TextView textView; - - ViewHolder(View view) { - super(view); - textView = (TextView) view.findViewById(R.id.text_item); - } - - void bind(final int position, final ItemSelectedListener listener) { - - itemView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (listener != null) { - listener.onItemSelected(position); - } - } - }); - } - } - - public void setListener(ItemSelectedListener itemSelectedListener) { - this.itemSelectedListener = itemSelectedListener; - } - - @Override - public FeatureAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new ViewHolder(LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_curved_layout, parent, false)); - } - - @Override - public void onBindViewHolder(FeatureAdapter.ViewHolder holder, final int position) { - if (data != null && !data.isEmpty()) { - holder.textView.setText(data.get(position).getTitle()); - holder.bind(position, itemSelectedListener); - } - } - - @Override - public int getItemCount() { - if (data != null && !data.isEmpty()) { - return data.size(); - } - return 0; - } - - public interface ItemSelectedListener { - void onItemSelected(int position); - } -} diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/model/Feature.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/model/Feature.java deleted file mode 100644 index 65954ec27e..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/model/Feature.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.mapbox.weartestapp.model; - -import android.content.Intent; - -public class Feature { - - public int title; - public Intent activity; - - public int getTitle() { - return title; - } - - public void setTitle(int title) { - this.title = title; - } - - public Intent getActivity() { - return activity; - } - - public void setActivity(Intent activity) { - this.activity = activity; - } - - public Feature(int title, Intent activity) { - this.title = title; - this.activity = activity; - } -} diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/utils/OffsettingHelper.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/utils/OffsettingHelper.java deleted file mode 100644 index 8550d0d016..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/java/com/mapbox/weartestapp/utils/OffsettingHelper.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.mapbox.weartestapp.utils; - -import android.support.wearable.view.DefaultOffsettingHelper; -import android.support.wearable.view.WearableRecyclerView; -import android.view.View; - -public class OffsettingHelper extends DefaultOffsettingHelper { - - /** - * How much should we scale the icon at most. - */ - private static final float MAX_ICON_PROGRESS = 0.65f; - - private float progressToCenter; - - public OffsettingHelper() { - } - - @Override - public void updateChild(View child, WearableRecyclerView parent) { - super.updateChild(child, parent); - - // Figure out % progress from top to bottom - float centerOffset = ((float) child.getHeight() / 2.0f) / (float) parent.getHeight(); - float yRelativeToCenterOffset = (child.getY() / parent.getHeight()) + centerOffset; - - // Normalize for center - progressToCenter = Math.abs(0.5f - yRelativeToCenterOffset); - // Adjust to the maximum scale - progressToCenter = Math.min(progressToCenter, MAX_ICON_PROGRESS); - - child.setScaleX(1 - progressToCenter); - child.setScaleY(1 - progressToCenter); - } - - @Override - public void adjustAnchorOffsetXY(View child, float[] anchorOffsetXY) { - anchorOffsetXY[0] = child.getHeight() / 2.0f; - } -} diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_feature_overview.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_feature_overview.xml deleted file mode 100644 index d1a314cfe2..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_feature_overview.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<android.support.wearable.view.BoxInsetLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:id="@+id/container" - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:context=".activity.FeatureOverviewActivity" - tools:deviceIds="wear"> - - <android.support.wearable.view.WearableRecyclerView - android:id="@+id/recycler_launcher_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scrollbars="vertical" /> - -</android.support.wearable.view.BoxInsetLayout> diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml deleted file mode 100644 index 44374f2c6c..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/activity_simple_mapview.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:mapbox="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:id="@+id/container" - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:context=".activity.SimpleWearMapActivity" - tools:deviceIds="wear"> - - <com.mapbox.mapboxsdk.maps.MapView - android:id="@+id/mapView" - android:layout_width="match_parent" - android:layout_height="match_parent" - mapbox:mapbox_cameraTargetLat="40.73581" - mapbox:mapbox_cameraTargetLng="-73.99155" - mapbox:mapbox_styleUrl="mapbox://styles/mapbox/streets-v10" - mapbox:mapbox_cameraZoom="11" - mapbox:mapbox_uiZoomControls="false"/> - -</FrameLayout> diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/item_curved_layout.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/item_curved_layout.xml deleted file mode 100644 index 3d81ba3ad5..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/layout/item_curved_layout.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/layout" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_margin="10dp" - android:clickable="true" - android:orientation="horizontal"> - - <TextView - android:id="@+id/text_item" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:textColor="@color/mapboxWhite" - android:textSize="14sp"/> - -</LinearLayout> diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-hdpi/ic_launcher.png b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index ac2ea61c73..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-hdpi/ic_launcher.png +++ /dev/null diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-mdpi/ic_launcher.png b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 99eed7146c..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-mdpi/ic_launcher.png +++ /dev/null diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xhdpi/ic_launcher.png b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 9b084daf91..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xhdpi/ic_launcher.png +++ /dev/null diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xxhdpi/ic_launcher.png b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 6fa714b47d..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/mipmap-xxhdpi/ic_launcher.png +++ /dev/null diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/colors.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/colors.xml deleted file mode 100644 index 5bcdbe93bf..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/colors.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - - <!-- Mapbox colors --> - <color name="colorPrimary">@color/mapboxBlue</color> - <color name="colorPrimaryDark">@color/mapboxBlueDark</color> - <color name="colorAccent">@color/mapboxRed</color> - - <color name="materialGrey">#F5F5F5</color> - <color name="materialDarkGrey">#DFDFDF</color> - - <color name="mapboxWhite">#ffffff</color> - <color name="mapboxCyan">#3BB2D0</color> - <color name="mapboxGreen">#56B881</color> - <color name="mapboxBlue">#3887BE</color> - <color name="mapboxBlueDark">#1F6EA5</color> - <color name="mapboxPurple">#8A8ACB</color> - <color name="mapboxPurpleDark">#7171b2</color> - <color name="mapboxPurpleLight">#A4A4E5</color> - - <color name="mapboxDenim">#50667F</color> - <color name="mapboxTeal">#41AFA5</color> - <color name="mapboxOrange">#F9886C</color> - <color name="mapboxRed">#E55E5E</color> - <color name="mapboxPink">#ED6498</color> - <color name="mapboxYellow">#f1f075</color> - <color name="mapboxMustard">#FBB03B</color> - <color name="mapboxNavy">#28353D</color> - <color name="mapboxNavyDark">#222B30</color> - -</resources> diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/strings.xml deleted file mode 100644 index e6a10ad308..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/main/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ -<resources> - <string name="app_name">MapboxGLAndroidSDKWearTestApp</string> - - <!-- Feature Titles --> - <string name="activity_simple_mapview_title">A simple map view</string> -</resources> diff --git a/platform/android/MapboxGLAndroidSDKWearTestApp/src/test/java/com/mapbox/weartestapp/utils/OffsettingHelperTest.java b/platform/android/MapboxGLAndroidSDKWearTestApp/src/test/java/com/mapbox/weartestapp/utils/OffsettingHelperTest.java deleted file mode 100644 index aab7714947..0000000000 --- a/platform/android/MapboxGLAndroidSDKWearTestApp/src/test/java/com/mapbox/weartestapp/utils/OffsettingHelperTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.mapbox.weartestapp.utils; - -import android.view.View; - -import org.junit.Test; -import org.mockito.InjectMocks; - -import static junit.framework.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class OffsettingHelperTest { - - private static final double DELTA = 1e-15; - - @InjectMocks - View view = mock(View.class); - - @Test - public void testAnchorOffset() { - float[] offset = new float[2]; - int viewHeight = 50; - when(view.getHeight()).thenReturn(viewHeight); - OffsettingHelper offsettingHelper = new OffsettingHelper(); - offsettingHelper.adjustAnchorOffsetXY(view, offset); - assertEquals("Offset of " + viewHeight + " should be divided by 2: ", viewHeight / 2, offset[0], DELTA); - } -} diff --git a/platform/android/README.md b/platform/android/README.md index 7c28433d96..bd95bdf7fa 100644 --- a/platform/android/README.md +++ b/platform/android/README.md @@ -1,6 +1,6 @@ # [Mapbox Android SDK](https://www.mapbox.com/android-sdk/) -[![Bitrise](https://www.bitrise.io/app/79cdcbdc42de4303.svg?token=_InPF8bII6W7J6kFr-L8QQ&branch=master)](https://www.bitrise.io/app/79cdcbdc42de4303) +[![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) A library based on [Mapbox GL Native](../../README.md) for embedding interactive map views with scalable, customizable vector maps into Java applications on Android devices. @@ -40,13 +40,16 @@ These dependencies are required for all operating systems and all platform targe - NDK - LLDB -- Modern C++ compiler that supports -std=c++14 +- Modern C++ compiler that supports `-std=c++14`\* - clang++ 3.5 or later or - - g++-5 or later + - g++-4.9 or later - [cURL](https://curl.haxx.se) (for build only) - [Node.js](https://nodejs.org/) or later (for build only) - [pkg-config](https://wiki.freedesktop.org/www/Software/pkg-config/) (for build only) +**Note**: We partially support C++14 because GCC 4.9 does not fully implement the +final draft of the C++14 standard. More information in [DEVELOPING.md](DEVELOPING.md). + ##### Additional Dependencies for Linux _These instructions were tested on Ubuntu 16.04 LTS (aka Xenial Xerus)._ @@ -75,6 +78,10 @@ make aproj Open Android Studio project in `/platform/android`, run `make android-configuration` in the root folder of the project. +##### Setup Checkstyle + +Mapbox uses specific IDE settings related to code and check style. +See [checkstyle guide](https://github.com/mapbox/mapbox-gl-native/wiki/Setting-up-Mapbox-checkstyle) for configuration details. ##### Setting Mapbox Access Token @@ -87,3 +94,8 @@ With the first gradle invocation, gradle will take the value of the `MAPBOX_ACCE Run the configuration for the `MapboxGLAndroidSDKTestApp` module and select a device or emulator to deploy on. Based on the selected device, the c++ code will be compiled for the related processor architecture. You can see the project compiling in the `View > Tool Windows > Gradle Console`. More information about building and distributing this project in [DISTRIBUTE.md][https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/DISTRIBUTE.md]. + +#### Symbolicating native crashes + +When hitting native crashes you can use ndk-stack to symbolicate crashes. +More information in [this](https://github.com/mapbox/mapbox-gl-native/wiki/Getting-line-numbers-from-an-Android-crash-with-ndk-stack) guide.
\ No newline at end of file diff --git a/platform/android/bitrise.yml b/platform/android/bitrise.yml deleted file mode 100644 index dcd4d4fb50..0000000000 --- a/platform/android/bitrise.yml +++ /dev/null @@ -1,235 +0,0 @@ ---- -format_version: 1.0.0 -default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git -trigger_map: -- pattern: devicefarmUpload - workflow: devicefarmUpload -- pattern: scheduled - workflow: scheduled -- pattern: nightly-release - workflow: nightly-release -- pattern: "*" - workflow: primary -workflows: - primary: - steps: - - script: - title: Build libmapbox-gl.so for armeabi-v7a - inputs: - - content: |- - #!/bin/bash - echo "Compile libmapbox-gl.so for armeabi-v7a abi:" - ccache -z - BUILDTYPE=Debug make android-lib-arm-v7 - ccache -s - - script: - title: Compile Core tests - inputs: - - content: |- - #!/bin/bash - echo "Compiling core tests:" - ccache -z - BUILDTYPE=Debug make android-test-lib-arm-v7 - ccache -s - - script: - title: Run local JVM Unit tests on phone module - inputs: - - content: |- - #!/bin/bash - echo "Running unit tests from MapboxGLAndroidSDKTestApp/src/test:" - make run-android-unit-test - - script: - title: Run local JVM Unit tests on wear module - inputs: - - content: |- - #!/bin/bash - echo "Running unit tests from MapboxGLAndroidSDKWearTestApp/src/test:" - make run-android-wear-unit-test - - script: - title: Generate Espresso sanity tests - inputs: - - content: |- - #!/bin/bash - echo "Generate these test locally by executing:" - make test-code-android - - script: - title: Run Checkstyle - inputs: - - content: |- - #!/bin/bash - # Run checkstyle gradle task on all modules - make android-checkstyle - - script: - title: Run Firebase instrumentation tests - run_if: '{{getenv "BITRISEIO_GCLOUD_SERVICE_ACCOUNT_JSON_URL" | ne ""}}' - inputs: - - content: |- - #!/bin/bash - set -euo pipefail - echo "Downloading Google Cloud authentication:" - wget -O secret.json "$BITRISEIO_GCLOUD_SERVICE_ACCOUNT_JSON_URL" - - echo "Downloading Mapbox accesstoken for running tests:" - wget -O platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml "$BITRISEIO_TEST_ACCESS_TOKEN_UI_TEST_URL" - - echo "Build seperate test apk:" - ccache -z - make android-ui-test-arm-v7 - ccache -s - - echo "Run tests on firebase:" - gcloud auth activate-service-account --key-file secret.json --project android-gl-native - gcloud beta test android devices list - gcloud beta test android run --type instrumentation --app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug.apk --test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug-androidTest.apk --device-ids shamu --os-version-ids 22 --locales en --orientations portrait --timeout 15m --test-targets "class com.mapbox.mapboxsdk.testapp.maps.widgets.AttributionTest" - - script: - title: Download Firebase results - is_always_run: true - run_if: '{{getenv "BITRISEIO_GCLOUD_SERVICE_ACCOUNT_JSON_URL" | ne ""}}' - inputs: - - content: |- - #!/bin/bash - set -euo pipefail - mkdir -p platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk - - echo "The details from Firebase will be downloaded, zipped and attached as a build artefact." - testUri=$(gsutil ls "gs://test-lab-wrrntqk05p31w-h3y1qk44vuunw/" | tail -n1) - echo "Downloading from : "$testUri - gsutil -m cp -R -Z $testUri platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk - - echo "Try running ndk-stack on downloaded logcat to symbolicate the stacktraces:" - find platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk -type f -name "logcat" -print0 | xargs -0 -Imylogcat mv -i mylogcat ./ - cat logcat | ndk-stack -sym build/android-arm-v7/Debug - - deploy-to-bitrise-io: - inputs: - - deploy_path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk - - is_compress: 'true' - - notify_user_groups: none - - slack: - title: Post to Slack - inputs: - - webhook_url: "$SLACK_HOOK_URL" - - channel: "#gl-bots" - - from_username: 'Bitrise Android' - - from_username_on_error: 'Bitrise Android' - - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> - for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}> - by ${GIT_CLONE_COMMIT_COMMITER_NAME} - passed' - - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> - for <https://github.com/mapbox/mapbox-gl-native/compare/${BITRISE_GIT_BRANCH}|mapbox/mapbox-gl-native@${BITRISE_GIT_BRANCH}> - by ${GIT_CLONE_COMMIT_COMMITER_NAME} - failed' - - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png - - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png - scheduled: - steps: - - script: - title: Download maven credentials - inputs: - - content: |- - #!/bin/bash - aws s3 cp s3://mapbox/android/signing-credentials/secring.gpg platform/android/MapboxGLAndroidSDK/secring.gpg - - # Add maven credentals for publishing - echo "NEXUS_USERNAME=$PUBLISH_NEXUS_USERNAME - NEXUS_PASSWORD=$PUBLISH_NEXUS_PASSWORD - signing.keyId=$SIGNING_KEYID - signing.password=$SIGNING_PASSWORD - signing.secretKeyRingFile=secring.gpg" >> platform/android/MapboxGLAndroidSDK/gradle.properties - - script: - title: Build release - inputs: - - content: |- - #!/bin/bash - echo "Compile libmapbox-gl.so for all supportd abi's:" - BUILDTYPE=Release make android-lib-arm-v5 - BUILDTYPE=Release make android-lib-arm-v7 - BUILDTYPE=Release make android-lib-arm-v8 - BUILDTYPE=Release make android-lib-x86 - BUILDTYPE=Release make android-lib-mips - BUILDTYPE=Release make android-lib-mips-64 - cd platform/android && ./gradlew -Pmapbox.abis=armeabi-v7a MapboxGLAndroidSDK:assembleRelease - - script: - title: Publish to maven - inputs: - - content: |- - #!/bin/bash - echo "Upload aar file to maven:" - make run-android-upload-archives - - slack: - title: Post to Slack - inputs: - - webhook_url: "$SLACK_HOOK_URL" - - channel: "#gl-bots" - - from_username: 'Bitrise Android' - - from_username_on_error: 'Bitrise Android' - - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> Publish of nightly Android SDK SNAPSHOT to maven succeeded.' - - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> Publish of nightly Android SDK SNAPSHOT to maven failed. @android_team.' - - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png - - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png - devicefarmUpload: - steps: - - script: - title: Build release - inputs: - - content: |- - #!/bin/bash - echo "Compile libmapbox-gl.so for all supportd abi's:" - export BUILDTYPE=Release - make apackage - - script: - title: Add AWS credentials - inputs: - - content: |- - #!/bin/bash - echo "AWS_ACCESS_KEY_ID_DEVICE_FARM=$AWS_ACCESS_KEY_ID_DEVICE_FARM" >> platform/android/MapboxGLAndroidSDKTestApp/gradle.properties - echo "AWS_SECRET_ACCESS_KEY_DEVICE_FARM=$AWS_SECRET_ACCESS_KEY_DEVICE_FARM" >> platform/android/MapboxGLAndroidSDKTestApp/gradle.properties - - script: - title: Generate sanity tests - inputs: - - content: |- - #!/bin/bash - echo "Generate these test locally by executing:" - make test-code-android - - script: - title: Run AWS Device Farm instrumentation tests - inputs: - - content: |- - #!/bin/bash - echo "Run tests on device farm:" - make run-android-ui-test-aws - - slack: - title: Post to Slack - inputs: - - webhook_url: "$SLACK_HOOK_URL" - - channel: "#gl-bots" - - from_username: 'Bitrise Android' - - from_username_on_error: 'Bitrise Android' - - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> for devicefarmUpload passed' - - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> for devicefarmUpload failed' - - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png - - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png - nightly-release: - steps: - - script: - title: Configure AWS-CLI - inputs: - - content: |- - #!/bin/bash - apt-get install -y python-pip python-dev build-essential - pip install awscli - - script: - title: Build release - inputs: - - content: |- - #!/bin/bash - echo "Compile libmapbox-gl.so for all supportd abi's:" - export BUILDTYPE=Release - make apackage - - script: - title: Log metrics - inputs: - - content: |- - #!/bin/bash - echo "Log binary size metrics to AWS CloudWatch:" - CLOUDWATCH=true platform/android/scripts/metrics.sh diff --git a/platform/android/build.gradle b/platform/android/build.gradle index bc90896812..e298b84da8 100644 --- a/platform/android/build.gradle +++ b/platform/android/build.gradle @@ -12,6 +12,7 @@ buildscript { allprojects { repositories { jcenter() + maven { url 'https://maven.google.com' } maven { url "http://oss.sonatype.org/content/repositories/snapshots/" } } } diff --git a/platform/android/config.cmake b/platform/android/config.cmake index 84591d644b..227334e0b5 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -55,6 +55,7 @@ macro(mbgl_platform_core) PRIVATE platform/android/src/thread.cpp PRIVATE platform/default/string_stdlib.cpp PRIVATE platform/default/bidi.cpp + PRIVATE platform/default/thread_local.cpp PRIVATE platform/default/utf.cpp # Image handling @@ -70,6 +71,24 @@ macro(mbgl_platform_core) PRIVATE platform/default/mbgl/util/shared_thread_pool.hpp PRIVATE platform/default/mbgl/util/default_thread_pool.cpp PRIVATE platform/default/mbgl/util/default_thread_pool.hpp + + # Rendering + PRIVATE platform/android/src/android_renderer_backend.cpp + PRIVATE platform/android/src/android_renderer_backend.hpp + PRIVATE platform/android/src/android_renderer_frontend.cpp + PRIVATE platform/android/src/android_renderer_frontend.hpp + + # Snapshots + PRIVATE platform/default/mbgl/gl/headless_backend.cpp + PRIVATE platform/default/mbgl/gl/headless_backend.hpp + PRIVATE platform/default/mbgl/gl/headless_frontend.cpp + PRIVATE platform/default/mbgl/gl/headless_frontend.hpp + PRIVATE platform/default/mbgl/map/map_snapshotter.cpp + PRIVATE platform/default/mbgl/map/map_snapshotter.hpp + PRIVATE platform/linux/src/headless_backend_egl.cpp + PRIVATE platform/linux/src/headless_display_egl.cpp + PRIVATE platform/android/src/snapshotter/map_snapshotter.cpp + PRIVATE platform/android/src/snapshotter/map_snapshotter.hpp ) target_include_directories(mbgl-core @@ -158,6 +177,8 @@ add_library(mbgl-android STATIC platform/android/src/style/sources/unknown_source.hpp platform/android/src/style/sources/vector_source.cpp platform/android/src/style/sources/vector_source.hpp + platform/android/src/style/sources/image_source.hpp + platform/android/src/style/sources/image_source.cpp platform/android/src/style/functions/stop.cpp platform/android/src/style/functions/stop.hpp platform/android/src/style/functions/categorical_stops.cpp @@ -184,6 +205,10 @@ add_library(mbgl-android STATIC # Native map platform/android/src/native_map_view.cpp platform/android/src/native_map_view.hpp + platform/android/src/map_renderer.cpp + platform/android/src/map_renderer.hpp + platform/android/src/map_renderer_runnable.cpp + platform/android/src/map_renderer_runnable.hpp # Java core classes platform/android/src/java/util.cpp @@ -222,6 +247,8 @@ add_library(mbgl-android STATIC platform/android/src/geometry/lat_lng.hpp platform/android/src/geometry/lat_lng_bounds.cpp platform/android/src/geometry/lat_lng_bounds.hpp + platform/android/src/geometry/lat_lng_quad.cpp + platform/android/src/geometry/lat_lng_quad.hpp platform/android/src/geometry/projected_meters.cpp platform/android/src/geometry/projected_meters.hpp @@ -299,10 +326,10 @@ macro(mbgl_platform_test) platform/android/src/test/main.jni.cpp # Headless view + platform/default/mbgl/gl/headless_frontend.cpp + platform/default/mbgl/gl/headless_frontend.hpp platform/default/mbgl/gl/headless_backend.cpp platform/default/mbgl/gl/headless_backend.hpp - platform/default/mbgl/gl/offscreen_view.cpp - platform/default/mbgl/gl/offscreen_view.hpp platform/linux/src/headless_backend_egl.cpp platform/linux/src/headless_display_egl.cpp diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle index eb74acac51..521084559f 100644 --- a/platform/android/dependencies.gradle +++ b/platform/android/dependencies.gradle @@ -4,15 +4,14 @@ ext { compileSdkVersion = 25 buildToolsVersion = "25.0.2" - versionCode = 12 - versionName = "5.1.4" + versionCode = 11 + versionName = "5.0.0" mapboxServicesVersion = "2.2.3" - supportLibVersion = "25.3.1" - wearableVersion = '2.0.0' - espressoVersion = '2.2.2' - testRunnerVersion = '0.5' - leakCanaryVersion = '1.5' + supportLibVersion = "25.4.0" + espressoVersion = '3.0.1' + testRunnerVersion = '1.0.1' + leakCanaryVersion = '1.5.1' dep = [ // mapbox @@ -25,10 +24,10 @@ ext { // unit test junit : 'junit:junit:4.12', - mockito : 'org.mockito:mockito-core:2.2.27', + mockito : 'org.mockito:mockito-core:2.10.0', // instrumentation test - testSpoonRunner : 'com.squareup.spoon:spoon-client:1.6.2', + testSpoonRunner : 'com.squareup.spoon:spoon-client:1.7.1', testRunner : "com.android.support.test:runner:${testRunnerVersion}", testRules : "com.android.support.test:rules:${testRunnerVersion}", testEspressoCore : "com.android.support.test.espresso:espresso-core:${espressoVersion}", @@ -41,15 +40,10 @@ ext { supportDesign : "com.android.support:design:${supportLibVersion}", supportRecyclerView : "com.android.support:recyclerview-v7:${supportLibVersion}", - // wear - wearCompile : "com.google.android.support:wearable:${wearableVersion}", - wearProvided : "com.google.android.wearable:wearable:${wearableVersion}", - // square crew timber : 'com.jakewharton.timber:timber:4.5.1', - okhttp3 : 'com.squareup.okhttp3:okhttp:3.8.0', + okhttp3 : 'com.squareup.okhttp3:okhttp:3.9.0', leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${leakCanaryVersion}", - leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}", - leakCanaryTest : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}" + leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${leakCanaryVersion}" ] }
\ No newline at end of file diff --git a/platform/android/gradle-lint.gradle b/platform/android/gradle-lint.gradle new file mode 100644 index 0000000000..cbebeaa74a --- /dev/null +++ b/platform/android/gradle-lint.gradle @@ -0,0 +1,30 @@ +task ciLint(type: Copy) { + if (isLocalBuild()) { + from "${projectDir}/lint/lint-baseline-local.xml" + into "${projectDir}" + rename { String fileName -> + fileName.replace("lint-baseline-local.xml", "lint-baseline.xml") + } + } else { + from "${projectDir}/lint/lint-baseline-ci.xml" + into "${projectDir}" + rename { String fileName -> + fileName.replace("lint-baseline-ci.xml", "lint-baseline.xml") + } + } +} + +def isLocalBuild() { + if (System.getenv('IS_LOCAL_DEVELOPMENT') != null) { + return System.getenv('IS_LOCAL_DEVELOPMENT').toBoolean() + } + return true +} + +lint.dependsOn ciLint + +tasks.whenTaskAdded { task -> + if (task.name == 'lintVitalRelease') { + task.dependsOn ciLint + } +}
\ No newline at end of file diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index a8b2c98c86..abc0796bc1 100644 --- a/platform/android/scripts/generate-style-code.js +++ b/platform/android/scripts/generate-style-code.js @@ -111,6 +111,9 @@ global.propertyNativeType = function (property) { if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) { return 'AlignmentType'; } + if (/^(text|icon)-anchor$/.test(property.name)) { + return 'SymbolAnchorType'; + } switch (property.type) { case 'boolean': return 'bool'; @@ -267,6 +270,9 @@ global.evaluatedType = function (property) { if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) { return 'AlignmentType'; } + if (/^(text|icon)-anchor$/.test(property.name)) { + return 'SymbolAnchorType'; + } if (/position/.test(property.name)) { return 'Position'; } diff --git a/platform/android/settings.gradle b/platform/android/settings.gradle index 9be29f4bd4..b5ab80b5ec 100644 --- a/platform/android/settings.gradle +++ b/platform/android/settings.gradle @@ -1,3 +1 @@ -include ':MapboxGLAndroidSDK' -include ':MapboxGLAndroidSDKTestApp' -include ':MapboxGLAndroidSDKWearTestApp' +include ':MapboxGLAndroidSDK', ':MapboxGLAndroidSDKTestApp'
\ No newline at end of file diff --git a/platform/android/src/android_renderer_backend.cpp b/platform/android/src/android_renderer_backend.cpp new file mode 100755 index 0000000000..9e49915650 --- /dev/null +++ b/platform/android/src/android_renderer_backend.cpp @@ -0,0 +1,63 @@ +#include "android_renderer_backend.hpp" + +#include <mbgl/gl/context.hpp> + +#include <EGL/egl.h> + +#include <cassert> + +namespace mbgl { +namespace android { + +/** + * From mbgl::View + */ +void AndroidRendererBackend::bind() { + assert(BackendScope::exists()); + setFramebufferBinding(0); + setViewport(0, 0, getFramebufferSize()); +} + +/** + * From mbgl::RendererBackend. + */ +gl::ProcAddress AndroidRendererBackend::initializeExtension(const char* name) { + assert(BackendScope::exists()); + return eglGetProcAddress(name); +} + +void AndroidRendererBackend::updateViewPort() { + assert(BackendScope::exists()); + setViewport(0, 0, getFramebufferSize()); +} + +void AndroidRendererBackend::resizeFramebuffer(int width, int height) { + fbWidth = width; + fbHeight = height; +} + +PremultipliedImage AndroidRendererBackend::readFramebuffer() const { + assert(BackendScope::exists()); + return RendererBackend::readFramebuffer(getFramebufferSize()); +} + +mbgl::Size AndroidRendererBackend::getFramebufferSize() const { + return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) }; +} + +/** + * From mbgl::RendererBackend. + */ +void AndroidRendererBackend::updateAssumedState() { + assumeFramebufferBinding(0); + assumeViewport(0, 0, getFramebufferSize()); +} + +void AndroidRendererBackend::markContextLost() { + if (context) { + context->setCleanupOnDestruction(false); + } +} + +} // namespace android +} // namspace mbgl diff --git a/platform/android/src/android_renderer_backend.hpp b/platform/android/src/android_renderer_backend.hpp new file mode 100755 index 0000000000..c5c552459f --- /dev/null +++ b/platform/android/src/android_renderer_backend.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <mbgl/renderer/renderer_backend.hpp> + +namespace mbgl { +namespace android { + +class AndroidRendererBackend : public RendererBackend { +public: + + // mbgl::RendererBackend // + void bind() override; + void updateAssumedState() override; + mbgl::Size getFramebufferSize() const override; + + // Ensures the current context is not + // cleaned up when destroyed + void markContextLost(); + + void updateViewPort(); + + void resizeFramebuffer(int width, int height); + PremultipliedImage readFramebuffer() const; + +protected: + // mbgl::RendererBackend // + gl::ProcAddress initializeExtension(const char*) override; + void activate() override {}; + void deactivate() override {}; + + +private: + + // Minimum texture size according to OpenGL ES 2.0 specification. + int fbWidth = 64; + int fbHeight = 64; + +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/android_renderer_frontend.cpp b/platform/android/src/android_renderer_frontend.cpp new file mode 100644 index 0000000000..b80e23e21f --- /dev/null +++ b/platform/android/src/android_renderer_frontend.cpp @@ -0,0 +1,117 @@ +#include "android_renderer_frontend.hpp" + +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/renderer/renderer_observer.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mbgl/util/thread.hpp> +#include <mbgl/util/run_loop.hpp> + +#include "android_renderer_backend.hpp" + +namespace mbgl { +namespace android { + +// Forwards RendererObserver signals to the given +// Delegate RendererObserver on the given RunLoop +class ForwardingRendererObserver : public RendererObserver { +public: + ForwardingRendererObserver(util::RunLoop& mapRunLoop, RendererObserver& delegate_) + : mailbox(std::make_shared<Mailbox>(mapRunLoop)) + , delegate(delegate_, mailbox) { + } + + ~ForwardingRendererObserver() { + mailbox->close(); + } + + void onInvalidate() override { + delegate.invoke(&RendererObserver::onInvalidate); + } + + void onResourceError(std::exception_ptr err) override { + delegate.invoke(&RendererObserver::onResourceError, err); + } + + void onWillStartRenderingMap() override { + delegate.invoke(&RendererObserver::onWillStartRenderingMap); + } + + void onWillStartRenderingFrame() override { + delegate.invoke(&RendererObserver::onWillStartRenderingFrame); + } + + void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded) override { + delegate.invoke(&RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded); + } + + void onDidFinishRenderingMap() override { + delegate.invoke(&RendererObserver::onDidFinishRenderingMap); + } + +private: + std::shared_ptr<Mailbox> mailbox; + ActorRef<RendererObserver> delegate; +}; + +AndroidRendererFrontend::AndroidRendererFrontend(MapRenderer& mapRenderer_) + : mapRenderer(mapRenderer_) + , mapRunLoop(util::RunLoop::Get()) { +} + +AndroidRendererFrontend::~AndroidRendererFrontend() = default; + +void AndroidRendererFrontend::reset() { + mapRenderer.reset(); +} + +void AndroidRendererFrontend::setObserver(RendererObserver& observer) { + assert (util::RunLoop::Get()); + // Don't call the Renderer directly, but use MapRenderer#setObserver to make sure + // the Renderer may be re-initialised without losing the RendererObserver reference. + mapRenderer.setObserver(std::make_unique<ForwardingRendererObserver>(*mapRunLoop, observer)); +} + +void AndroidRendererFrontend::update(std::shared_ptr<UpdateParameters> params) { + mapRenderer.update(std::move(params)); + mapRenderer.requestRender(); +} + +void AndroidRendererFrontend::onLowMemory() { + mapRenderer.actor().invoke(&Renderer::onLowMemory); +} + +std::vector<Feature> AndroidRendererFrontend::querySourceFeatures(const std::string& sourceID, + const SourceQueryOptions& options) const { + // Waits for the result from the orchestration thread and returns + return mapRenderer.actor().ask(&Renderer::querySourceFeatures, sourceID, options).get(); +} + +std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenBox& box, + const RenderedQueryOptions& options) const { + + // Select the right overloaded method + std::vector<Feature> (Renderer::*fn)(const ScreenBox&, const RenderedQueryOptions&) const = &Renderer::queryRenderedFeatures; + + // Waits for the result from the orchestration thread and returns + return mapRenderer.actor().ask(fn, box, options).get(); +} + +std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenCoordinate& point, + const RenderedQueryOptions& options) const { + + // Select the right overloaded method + std::vector<Feature> (Renderer::*fn)(const ScreenCoordinate&, const RenderedQueryOptions&) const = &Renderer::queryRenderedFeatures; + + // Waits for the result from the orchestration thread and returns + return mapRenderer.actor().ask(fn, point, options).get(); +} + +AnnotationIDs AndroidRendererFrontend::queryPointAnnotations(const ScreenBox& box) const { + // Waits for the result from the orchestration thread and returns + return mapRenderer.actor().ask(&Renderer::queryPointAnnotations, box).get(); +} + +} // namespace android +} // namespace mbgl + diff --git a/platform/android/src/android_renderer_frontend.hpp b/platform/android/src/android_renderer_frontend.hpp new file mode 100644 index 0000000000..94508fd816 --- /dev/null +++ b/platform/android/src/android_renderer_frontend.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include <mbgl/actor/actor.hpp> +#include <mbgl/annotation/annotation.hpp> +#include <mbgl/renderer/renderer_frontend.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/run_loop.hpp> + +#include <functional> +#include <memory> +#include <vector> +#include <string> + +#include "map_renderer.hpp" + +namespace mbgl { + +class RenderedQueryOptions; +class SourceQueryOptions; + +namespace android { + +class AndroidRendererFrontend : public RendererFrontend { +public: + + AndroidRendererFrontend(MapRenderer&); + ~AndroidRendererFrontend() override; + + void reset() override; + void setObserver(RendererObserver&) override; + + void update(std::shared_ptr<UpdateParameters>) override; + + // Feature querying + std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate&, const RenderedQueryOptions&) const; + std::vector<Feature> queryRenderedFeatures(const ScreenBox&, const RenderedQueryOptions&) const; + std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const; + AnnotationIDs queryPointAnnotations(const ScreenBox& box) const; + + // Memory + void onLowMemory(); + +private: + MapRenderer& mapRenderer; + util::RunLoop* mapRunLoop; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/asset_manager_file_source.cpp b/platform/android/src/asset_manager_file_source.cpp index 6a3113d696..aa65e3ff48 100644 --- a/platform/android/src/asset_manager_file_source.cpp +++ b/platform/android/src/asset_manager_file_source.cpp @@ -1,5 +1,6 @@ #include "asset_manager_file_source.hpp" +#include <mbgl/storage/file_source_request.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/util/util.hpp> #include <mbgl/util/thread.hpp> @@ -12,10 +13,10 @@ namespace mbgl { class AssetManagerFileSource::Impl { public: - Impl(AAssetManager* assetManager_) : assetManager(assetManager_) { + Impl(ActorRef<Impl>, AAssetManager* assetManager_) : assetManager(assetManager_) { } - void request(const std::string& url, FileSource::Callback callback) { + void request(const std::string& url, ActorRef<FileSourceRequest> req) { // Note: AssetManager already prepends "assets" to the filename. const std::string path = mbgl::util::percentDecode(url.substr(8)); @@ -30,7 +31,7 @@ public: "Could not read asset"); } - callback(response); + req.invoke(&FileSourceRequest::setResponse, response); } private: @@ -39,15 +40,18 @@ private: AssetManagerFileSource::AssetManagerFileSource(jni::JNIEnv& env, jni::Object<android::AssetManager> assetManager_) : assetManager(assetManager_.NewGlobalRef(env)), - thread(std::make_unique<util::Thread<Impl>>( - util::ThreadContext{"AssetManagerFileSource", util::ThreadPriority::Low}, - AAssetManager_fromJava(&env, jni::Unwrap(**assetManager)))) { + impl(std::make_unique<util::Thread<Impl>>("AssetManagerFileSource", + AAssetManager_fromJava(&env, jni::Unwrap(**assetManager)))) { } AssetManagerFileSource::~AssetManagerFileSource() = default; std::unique_ptr<AsyncRequest> AssetManagerFileSource::request(const Resource& resource, Callback callback) { - return thread->invokeWithCallback(&Impl::request, resource.url, callback); + auto req = std::make_unique<FileSourceRequest>(std::move(callback)); + + impl->actor().invoke(&Impl::request, resource.url, req->actor()); + + return std::move(req); } } // namespace mbgl diff --git a/platform/android/src/asset_manager_file_source.hpp b/platform/android/src/asset_manager_file_source.hpp index 7a447a2c61..dcad95664a 100644 --- a/platform/android/src/asset_manager_file_source.hpp +++ b/platform/android/src/asset_manager_file_source.hpp @@ -20,9 +20,10 @@ public: std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override; private: - jni::UniqueObject<android::AssetManager> assetManager; class Impl; - std::unique_ptr<util::Thread<Impl>> thread; + + jni::UniqueObject<android::AssetManager> assetManager; + std::unique_ptr<util::Thread<Impl>> impl; }; } // namespace mbgl diff --git a/platform/android/src/conversion/constant.hpp b/platform/android/src/conversion/constant.hpp index 2a0b710f73..f1c72eb5dd 100644 --- a/platform/android/src/conversion/constant.hpp +++ b/platform/android/src/conversion/constant.hpp @@ -3,6 +3,7 @@ #include "conversion.hpp" #include <mbgl/util/optional.hpp> +#include <mbgl/util/color.hpp> #include <jni/jni.hpp> #include <string> diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index c55c9c3527..1ed68d0835 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -92,6 +92,10 @@ void nativeRender(void *context, const mbgl::style::CustomLayerRenderParameters& reinterpret_cast<ExampleCustomLayer*>(context)->render(); } +void nativeContextLost(void */*context*/) { + mbgl::Log::Info(mbgl::Event::General, "nativeContextLost"); +} + void nativeDenitialize(void *context) { mbgl::Log::Info(mbgl::Event::General, "nativeDeinitialize"); delete reinterpret_cast<ExampleCustomLayer*>(context); @@ -123,6 +127,10 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { reinterpret_cast<jlong>(nativeRender)); env->SetStaticLongField(customLayerClass, + env->GetStaticFieldID(customLayerClass, "ContextLostFunction", "J"), + reinterpret_cast<jlong>(nativeContextLost)); + + env->SetStaticLongField(customLayerClass, env->GetStaticFieldID(customLayerClass, "DeinitializeFunction", "J"), reinterpret_cast<jlong>(nativeDenitialize)); diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp index 16c09b7b52..262e3d3c6a 100644 --- a/platform/android/src/file_source.cpp +++ b/platform/android/src/file_source.cpp @@ -1,5 +1,8 @@ #include "file_source.hpp" +#include <mbgl/actor/actor.hpp> +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/storage/resource_transform.hpp> #include <mbgl/util/logging.hpp> #include "asset_manager_file_source.hpp" @@ -42,21 +45,22 @@ void FileSource::setAPIBaseUrl(jni::JNIEnv& env, jni::String url) { void FileSource::setResourceTransform(jni::JNIEnv& env, jni::Object<FileSource::ResourceTransformCallback> transformCallback) { if (transformCallback) { - // Launch transformCallback - fileSource->setResourceTransform([ + resourceTransform = std::make_unique<Actor<ResourceTransform>>(*Scheduler::GetCurrent(), // Capture the ResourceTransformCallback object as a managed global into // the lambda. It is released automatically when we're setting a new ResourceTransform in // a subsequent call. // Note: we're converting it to shared_ptr because this lambda is converted to a std::function, // which requires copyability of its captured variables. - callback = std::shared_ptr<jni::jobject>(transformCallback.NewGlobalRef(env).release()->Get(), GenericGlobalRefDeleter()) - ](mbgl::Resource::Kind kind, std::string&& url_) { - android::UniqueEnv _env = android::AttachEnv(); - return FileSource::ResourceTransformCallback::onURL(*_env, jni::Object<FileSource::ResourceTransformCallback>(*callback), int(kind), url_); - }); + [callback = std::shared_ptr<jni::jobject>(transformCallback.NewGlobalRef(env).release()->Get(), GenericGlobalRefDeleter())] + (mbgl::Resource::Kind kind, const std::string&& url_) { + android::UniqueEnv _env = android::AttachEnv(); + return FileSource::ResourceTransformCallback::onURL(*_env, jni::Object<FileSource::ResourceTransformCallback>(*callback), int(kind), url_); + }); + fileSource->setResourceTransform(resourceTransform->self()); } else { // Reset the callback - fileSource->setResourceTransform(nullptr); + resourceTransform.reset(); + fileSource->setResourceTransform({}); } } @@ -106,4 +110,4 @@ std::string FileSource::ResourceTransformCallback::onURL(jni::JNIEnv& env, jni:: } } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/file_source.hpp b/platform/android/src/file_source.hpp index 55e70f34d9..4abe352bff 100644 --- a/platform/android/src/file_source.hpp +++ b/platform/android/src/file_source.hpp @@ -7,6 +7,10 @@ #include <jni/jni.hpp> namespace mbgl { + +template <typename T> class Actor; +class ResourceTransform; + namespace android { /** @@ -46,10 +50,10 @@ public: static void registerNative(jni::JNIEnv&); private: - + std::unique_ptr<Actor<ResourceTransform>> resourceTransform; std::unique_ptr<mbgl::DefaultFileSource> fileSource; }; } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/geometry/lat_lng_quad.cpp b/platform/android/src/geometry/lat_lng_quad.cpp new file mode 100644 index 0000000000..2b36139e18 --- /dev/null +++ b/platform/android/src/geometry/lat_lng_quad.cpp @@ -0,0 +1,39 @@ +#include "lat_lng_quad.hpp" +#include "lat_lng.hpp" + +namespace mbgl { +namespace android { + +jni::Object<LatLngQuad> LatLngQuad::New(jni::JNIEnv& env, std::array<mbgl::LatLng, 4> coordinates) { + static auto quadConstructor = LatLngQuad::javaClass.GetConstructor<jni::Object<LatLng>, jni::Object<LatLng>, jni::Object<LatLng>, jni::Object<LatLng>>(env); + return LatLngQuad::javaClass.New(env, quadConstructor, + LatLng::New(env, coordinates[0]), + LatLng::New(env, coordinates[1]), + LatLng::New(env, coordinates[2]), + LatLng::New(env, coordinates[3])); +} + +std::array<mbgl::LatLng, 4> LatLngQuad::getLatLngArray(jni::JNIEnv& env, jni::Object<LatLngQuad> quad) { + static auto topLeftField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "topLeft"); + static auto topRightField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "topRight"); + static auto bottomRightField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "bottomRight"); + static auto bottomLeftField = LatLngQuad::javaClass.GetField <jni::Object<LatLng>>(env, "bottomLeft"); + + return std::array < mbgl::LatLng, 4 > {{ + LatLng::getLatLng(env, quad.Get(env, topLeftField)), + LatLng::getLatLng(env, quad.Get(env, topRightField)), + LatLng::getLatLng(env, quad.Get(env, bottomRightField)), + LatLng::getLatLng(env, quad.Get(env, bottomLeftField)) + }}; +} + +void LatLngQuad::registerNative(jni::JNIEnv& env) { + // Lookup the class + LatLngQuad::javaClass = *jni::Class<LatLngQuad>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<LatLngQuad> LatLngQuad::javaClass; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/lat_lng_quad.hpp b/platform/android/src/geometry/lat_lng_quad.hpp new file mode 100644 index 0000000000..8f8c9abeef --- /dev/null +++ b/platform/android/src/geometry/lat_lng_quad.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/geometry.hpp> + +#include <jni/jni.hpp> +#include <array> + +namespace mbgl { +namespace android { + +class LatLngQuad : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/LatLngQuad"; }; + + static jni::Object<LatLngQuad> New(jni::JNIEnv&, std::array<mbgl::LatLng, 4>); + + static std::array<mbgl::LatLng, 4> getLatLngArray(jni::JNIEnv&, jni::Object<LatLngQuad>); + + static jni::Class<LatLngQuad> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/http_file_source.cpp b/platform/android/src/http_file_source.cpp index ee1429bd74..8eb9416e9d 100644 --- a/platform/android/src/http_file_source.cpp +++ b/platform/android/src/http_file_source.cpp @@ -117,7 +117,9 @@ void HTTPRequest::onResponse(jni::JNIEnv& env, int code, } if (cacheControl) { - response.expires = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str()).toTimePoint(); + const auto cc = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str()); + response.expires = cc.toTimePoint(); + response.mustRevalidate = cc.mustRevalidate; } if (expires) { diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 6c490fad5c..6acb6a3664 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -23,6 +23,7 @@ #include "geojson/position.hpp" #include "geometry/lat_lng.hpp" #include "geometry/lat_lng_bounds.hpp" +#include "geometry/lat_lng_quad.hpp" #include "geometry/projected_meters.hpp" #include "graphics/pointf.hpp" #include "graphics/rectf.hpp" @@ -31,6 +32,8 @@ #include "gson/json_object.hpp" #include "gson/json_primitive.hpp" #include "java_types.hpp" +#include "map_renderer.hpp" +#include "map_renderer_runnable.hpp" #include "native_map_view.hpp" #include "offline/offline_manager.hpp" #include "offline/offline_region.hpp" @@ -46,6 +49,7 @@ #include "style/layers/layers.hpp" #include "style/sources/sources.hpp" #include "style/light.hpp" +#include "snapshotter/map_snapshotter.hpp" namespace mbgl { namespace android { @@ -127,6 +131,7 @@ void registerNatives(JavaVM *vm) { // Geometry LatLng::registerNative(env); LatLngBounds::registerNative(env); + LatLngQuad::registerNative(env); ProjectedMeters::registerNative(env); // GSon @@ -141,6 +146,8 @@ void registerNatives(JavaVM *vm) { Polyline::registerNative(env); // Map + MapRenderer::registerNative(env); + MapRendererRunnable::registerNative(env); NativeMapView::registerNative(env); // Http @@ -175,6 +182,9 @@ void registerNatives(JavaVM *vm) { OfflineTilePyramidRegionDefinition::registerNative(env); OfflineRegionError::registerNative(env); OfflineRegionStatus::registerNative(env); + + // Snapshotter + MapSnapshotter::registerNative(env); } } // namespace android diff --git a/platform/android/src/jni/generic_global_ref_deleter.hpp b/platform/android/src/jni/generic_global_ref_deleter.hpp index 4e53e0a0ce..7239e361a7 100644 --- a/platform/android/src/jni/generic_global_ref_deleter.hpp +++ b/platform/android/src/jni/generic_global_ref_deleter.hpp @@ -18,5 +18,34 @@ struct GenericGlobalRefDeleter { } }; + +template < class TagType > +class GenericWeakObjectRefDeleter; + +template < class TagType = jni::ObjectTag > +using GenericUniqueWeakObject = std::unique_ptr< const jni::Object<TagType>, GenericWeakObjectRefDeleter<TagType> >; + +template < class TagType > +class GenericWeakObjectRefDeleter +{ +public: + using pointer = jni::PointerToValue< jni::Object<TagType> >; + + void operator()(pointer p) const + { + if (p) + { + auto env = AttachEnv(); + env->DeleteWeakGlobalRef(jni::Unwrap(p->Get())); + } + } +}; + +template < class TagType > +GenericUniqueWeakObject<TagType> SeizeGenericWeakRef(JNIEnv&, jni::Object<TagType>&& object) +{ + return GenericUniqueWeakObject<TagType>(jni::PointerToValue<jni::Object<TagType>>(std::move(object)), GenericWeakObjectRefDeleter<TagType>()); +}; + } // namespace android } // namespace mbgl diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp index d6f2cb83e8..1fc5f9789f 100644 --- a/platform/android/src/map/camera_position.cpp +++ b/platform/android/src/map/camera_position.cpp @@ -27,6 +27,24 @@ jni::Object<CameraPosition> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOp return CameraPosition::javaClass.New(env, constructor, LatLng::New(env, center), options.zoom.value_or(0), tilt_degrees, bearing_degrees); } +mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, jni::Object<CameraPosition> position) { + static auto bearing = CameraPosition::javaClass.GetField<jni::jdouble>(env, "bearing"); + static auto target = CameraPosition::javaClass.GetField<jni::Object<LatLng>>(env, "target"); + static auto tilt = CameraPosition::javaClass.GetField<jni::jdouble>(env, "tilt"); + static auto zoom = CameraPosition::javaClass.GetField<jni::jdouble>(env, "zoom"); + + auto center = LatLng::getLatLng(env, position.Get(env, target)); + + return mbgl::CameraOptions { + center, + {}, + {}, + position.Get(env, zoom), + position.Get(env, bearing) * util::DEG2RAD, + position.Get(env, tilt) + }; +} + void CameraPosition::registerNative(jni::JNIEnv &env) { // Lookup the class CameraPosition::javaClass = *jni::Class<CameraPosition>::Find(env).NewGlobalRef(env).release(); diff --git a/platform/android/src/map/camera_position.hpp b/platform/android/src/map/camera_position.hpp index b9f1646cc9..4eee8be758 100644 --- a/platform/android/src/map/camera_position.hpp +++ b/platform/android/src/map/camera_position.hpp @@ -15,6 +15,8 @@ public: static jni::Object<CameraPosition> New(jni::JNIEnv&, mbgl::CameraOptions); + static mbgl::CameraOptions getCameraOptions(jni::JNIEnv&, jni::Object<CameraPosition>); + static jni::Class<CameraPosition> javaClass; static void registerNative(jni::JNIEnv&); diff --git a/platform/android/src/map_renderer.cpp b/platform/android/src/map_renderer.cpp new file mode 100644 index 0000000000..7655455210 --- /dev/null +++ b/platform/android/src/map_renderer.cpp @@ -0,0 +1,200 @@ +#include "map_renderer.hpp" + +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/util/shared_thread_pool.hpp> +#include <mbgl/util/run_loop.hpp> + +#include <string> + +#include "attach_env.hpp" +#include "android_renderer_backend.hpp" +#include "map_renderer_runnable.hpp" +#include "file_source.hpp" + +namespace mbgl { +namespace android { + +MapRenderer::MapRenderer(jni::JNIEnv& _env, jni::Object<MapRenderer> obj, + jni::Object<FileSource> _fileSource, jni::jfloat pixelRatio_, + jni::String programCacheDir_) + : javaPeer(SeizeGenericWeakRef(_env, jni::Object<MapRenderer>(jni::NewWeakGlobalRef(_env, obj.Get()).release()))), pixelRatio(pixelRatio_) + , fileSource(FileSource::getDefaultFileSource(_env, _fileSource)) + , programCacheDir(jni::Make<std::string>(_env, programCacheDir_)) + , threadPool(sharedThreadPool()) + , mailbox(std::make_shared<Mailbox>(*this)) { +} + +MapRenderer::~MapRenderer() = default; + +void MapRenderer::reset() { + // Make sure to destroy the renderer on the GL Thread + auto self = ActorRef<MapRenderer>(*this, mailbox); + self.ask(&MapRenderer::resetRenderer).wait(); + + // Lock to make sure there is no concurrent initialisation on the gl thread + std::lock_guard<std::mutex> lock(initialisationMutex); + rendererObserver.reset(); +} + +ActorRef<Renderer> MapRenderer::actor() const { + return *rendererRef; +} + +void MapRenderer::schedule(std::weak_ptr<Mailbox> scheduled) { + // Create a runnable and schedule it on the gl thread + android::UniqueEnv _env = android::AttachEnv(); + auto runnable = std::make_unique<MapRendererRunnable>(*_env, std::move(scheduled)); + + static auto queueEvent = javaClass.GetMethod<void( + jni::Object<MapRendererRunnable>)>(*_env, "queueEvent"); + javaPeer->Call(*_env, queueEvent, runnable->getPeer()); + + // Release the object as it will be destroyed on GC of the Java Peer + runnable.release(); +} + +void MapRenderer::requestRender() { + android::UniqueEnv _env = android::AttachEnv(); + static auto onInvalidate = javaClass.GetMethod<void()>(*_env, "requestRender"); + javaPeer->Call(*_env, onInvalidate); +} + +void MapRenderer::update(std::shared_ptr<UpdateParameters> params) { + // Lock on the parameters + std::lock_guard<std::mutex> lock(updateMutex); + updateParameters = std::move(params); +} + +void MapRenderer::setObserver(std::shared_ptr<RendererObserver> _rendererObserver) { + // Lock as the initialization can come from the main thread or the GL thread first + std::lock_guard<std::mutex> lock(initialisationMutex); + + rendererObserver = std::move(_rendererObserver); + + // Set the new observer on the Renderer implementation + if (renderer) { + renderer->setObserver(rendererObserver.get()); + } +} + +void MapRenderer::requestSnapshot(SnapshotCallback callback) { + auto self = ActorRef<MapRenderer>(*this, mailbox); + self.invoke( + &MapRenderer::scheduleSnapshot, + std::make_unique<SnapshotCallback>([&, callback=std::move(callback), runloop=util::RunLoop::Get()](PremultipliedImage image) { + runloop->invoke([callback=std::move(callback), image=std::move(image)]() mutable { + callback(std::move(image)); + }); + snapshotCallback.reset(); + }) + ); +} + +// Called on OpenGL thread // + +void MapRenderer::resetRenderer() { + assert (renderer); + renderer.reset(); +} + +void MapRenderer::scheduleSnapshot(std::unique_ptr<SnapshotCallback> callback) { + snapshotCallback = std::move(callback); + requestRender(); +} + +void MapRenderer::render(JNIEnv&) { + assert (renderer); + + std::shared_ptr<UpdateParameters> params; + { + // Lock on the parameters + std::unique_lock<std::mutex> lock(updateMutex); + if (!updateParameters) return; + + // Hold on to the update parameters during render + params = updateParameters; + } + + // Activate the backend + BackendScope backendGuard { *backend }; + + // Ensure that the "current" scheduler on the render thread is + // this scheduler. + Scheduler::SetCurrent(this); + + if (framebufferSizeChanged) { + backend->updateViewPort(); + framebufferSizeChanged = false; + } + + renderer->render(*params); + + // Deliver the snapshot if requested + if (snapshotCallback) { + snapshotCallback->operator()(backend->readFramebuffer()); + snapshotCallback.reset(); + } +} + +void MapRenderer::onSurfaceCreated(JNIEnv&) { + // Lock as the initialization can come from the main thread or the GL thread first + std::lock_guard<std::mutex> lock(initialisationMutex); + + // The android system will have already destroyed the underlying + // GL resources if this is not the first intialization and an + // attempt to clean them up will fail + if (backend) backend->markContextLost(); + if (renderer) renderer->markContextLost(); + + // Reset in opposite order + renderer.reset(); + backend.reset(); + + // Create the new backend and renderer + backend = std::make_unique<AndroidRendererBackend>(); + renderer = std::make_unique<Renderer>(*backend, pixelRatio, fileSource, *threadPool, + GLContextMode::Unique, programCacheDir); + rendererRef = std::make_unique<ActorRef<Renderer>>(*renderer, mailbox); + + // Set the observer on the new Renderer implementation + if (rendererObserver) { + renderer->setObserver(rendererObserver.get()); + } +} + +void MapRenderer::onSurfaceChanged(JNIEnv&, jint width, jint height) { + backend->resizeFramebuffer(width, height); + framebufferSizeChanged = true; + requestRender(); +} + +// Static methods // + +jni::Class<MapRenderer> MapRenderer::javaClass; + +void MapRenderer::registerNative(jni::JNIEnv& env) { + // Lookup the class + MapRenderer::javaClass = *jni::Class<MapRenderer>::Find(env).NewGlobalRef(env).release(); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<MapRenderer>(env, MapRenderer::javaClass, "nativePtr", + std::make_unique<MapRenderer, JNIEnv&, jni::Object<MapRenderer>, jni::Object<FileSource>, jni::jfloat, jni::String>, + "nativeInitialize", "finalize", + METHOD(&MapRenderer::render, "nativeRender"), + METHOD(&MapRenderer::onSurfaceCreated, + "nativeOnSurfaceCreated"), + METHOD(&MapRenderer::onSurfaceChanged, + "nativeOnSurfaceChanged")); +} + +MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, jni::Object<MapRenderer> jObject) { + static auto field = MapRenderer::javaClass.GetField<jlong>(env, "nativePtr"); + MapRenderer* mapRenderer = reinterpret_cast<MapRenderer*>(jObject.Get(env, field)); + assert(mapRenderer != nullptr); + return *mapRenderer; +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/map_renderer.hpp b/platform/android/src/map_renderer.hpp new file mode 100644 index 0000000000..0d614912a9 --- /dev/null +++ b/platform/android/src/map_renderer.hpp @@ -0,0 +1,126 @@ +#pragma once + +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/util/image.hpp> + +#include <memory> +#include <utility> + +#include <jni/jni.hpp> +#include <mbgl/storage/default_file_source.hpp> + +#include "jni/generic_global_ref_deleter.hpp" + +namespace mbgl { + +template <class> +class ActorRef; +class Mailbox; +class Renderer; +class RendererBackend; +class RendererObserver; +class ThreadPool; +class UpdateParameters; + +namespace android { + +class AndroidRendererBackend; +class FileSource; + +/** + * The MapRenderer is a peer class that encapsulates the actions + * performed on the GL Thread. + * + * The public methods are safe to call from the main thread, others are not. + */ +class MapRenderer : public Scheduler { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/renderer/MapRenderer"; }; + + static jni::Class<MapRenderer> javaClass; + + static void registerNative(jni::JNIEnv&); + + static MapRenderer& getNativePeer(JNIEnv&, jni::Object<MapRenderer>); + + MapRenderer(jni::JNIEnv& _env, + jni::Object<MapRenderer>, + jni::Object<FileSource>, + jni::jfloat pixelRatio, + jni::String programCacheDir); + + ~MapRenderer() override; + + // Resets the renderer to clean up on the calling thread + void reset(); + + // Takes the RendererObserver by shared_ptr so we + // don't have to make the header public. Use + // this instead of Renderer#setObserver directly + void setObserver(std::shared_ptr<RendererObserver>); + + // Sets the new update parameters to use on subsequent + // renders. Be sure to trigger a render with + // requestRender(). + void update(std::shared_ptr<UpdateParameters>); + + // Gives a handle to the Renderer to enable actions on + // any thread. + ActorRef<Renderer> actor() const; + + // From Scheduler. Schedules by using callbacks to the + // JVM to process the mailbox on the right thread. + void schedule(std::weak_ptr<Mailbox> scheduled) override; + + void requestRender(); + + // Snapshot - requires a RunLoop on the calling thread + using SnapshotCallback = std::function<void (PremultipliedImage)>; + void requestSnapshot(SnapshotCallback); + +protected: + // Called from the GL Thread // + + void scheduleSnapshot(std::unique_ptr<SnapshotCallback>); + +private: + // Called from the GL Thread // + + // Resets the renderer + void resetRenderer(); + + // Renders a frame. + void render(JNIEnv&); + + void onSurfaceCreated(JNIEnv&); + + void onSurfaceChanged(JNIEnv&, jint width, jint height); + +private: + GenericUniqueWeakObject<MapRenderer> javaPeer; + + float pixelRatio; + DefaultFileSource& fileSource; + std::string programCacheDir; + + std::shared_ptr<ThreadPool> threadPool; + std::shared_ptr<Mailbox> mailbox; + + std::mutex initialisationMutex; + std::shared_ptr<RendererObserver> rendererObserver; + + std::unique_ptr<AndroidRendererBackend> backend; + std::unique_ptr<Renderer> renderer; + std::unique_ptr<ActorRef<Renderer>> rendererRef; + + std::shared_ptr<UpdateParameters> updateParameters; + std::mutex updateMutex; + + bool framebufferSizeChanged = false; + + std::unique_ptr<SnapshotCallback> snapshotCallback; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/map_renderer_runnable.cpp b/platform/android/src/map_renderer_runnable.cpp new file mode 100644 index 0000000000..df8cba5e55 --- /dev/null +++ b/platform/android/src/map_renderer_runnable.cpp @@ -0,0 +1,49 @@ +#include "map_renderer_runnable.hpp" + +#include <mbgl/util/logging.hpp> + +namespace mbgl { +namespace android { + +MapRendererRunnable::MapRendererRunnable(jni::JNIEnv& env, std::weak_ptr<Mailbox> mailbox_) + : mailbox(std::move(mailbox_)) { + + // Create the Java peer + jni::UniqueLocalFrame frame = jni::PushLocalFrame(env, 5); + static auto constructor = javaClass.GetConstructor<jlong>(env); + auto instance = javaClass.New(env, constructor, reinterpret_cast<jlong>(this)); + javaPeer = SeizeGenericWeakRef(env, jni::Object<MapRendererRunnable>(jni::NewWeakGlobalRef(env, instance.Get()).release())); +} + +MapRendererRunnable::~MapRendererRunnable() = default; + +void MapRendererRunnable::run(jni::JNIEnv&) { + Mailbox::maybeReceive(mailbox); +} + +jni::Object<MapRendererRunnable> MapRendererRunnable::getPeer() { + return *javaPeer; +} + +// Static methods // + +jni::Class<MapRendererRunnable> MapRendererRunnable::javaClass; + +void MapRendererRunnable::registerNative(jni::JNIEnv& env) { + // Lookup the class + MapRendererRunnable::javaClass = *jni::Class<MapRendererRunnable>::Find(env).NewGlobalRef(env).release(); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + jni::RegisterNativePeer<MapRendererRunnable>( + env, + MapRendererRunnable::javaClass, + "nativePtr", + std::make_unique<MapRendererRunnable, JNIEnv&>, + "nativeInitialize", + "finalize", + METHOD(&MapRendererRunnable::run, "run")); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/map_renderer_runnable.hpp b/platform/android/src/map_renderer_runnable.hpp new file mode 100644 index 0000000000..75646a442d --- /dev/null +++ b/platform/android/src/map_renderer_runnable.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include <mbgl/actor/mailbox.hpp> +#include <mbgl/actor/scheduler.hpp> + +#include <memory> +#include <utility> + +#include <jni/jni.hpp> + +#include "jni/generic_global_ref_deleter.hpp" + +namespace mbgl { +namespace android { + +/** + * The MapRendererRunnable is a peer class that encapsulates + * a scheduled mailbox in a Java Runnable so it can be + * scheduled on the map renderer thread. + * + */ +class MapRendererRunnable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable"; }; + + static jni::Class<MapRendererRunnable> javaClass; + + static void registerNative(jni::JNIEnv&); + + MapRendererRunnable(jni::JNIEnv&, std::weak_ptr<Mailbox>); + + // Only for jni registration, unused + MapRendererRunnable(jni::JNIEnv&) { + assert(false); + } + + ~MapRendererRunnable(); + + void run(jni::JNIEnv&); + + jni::Object<MapRendererRunnable> getPeer(); + +private: + GenericUniqueWeakObject<MapRendererRunnable> javaPeer; + std::weak_ptr<Mailbox> mailbox; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 159ba70508..a9ed6d5ead 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -9,12 +9,10 @@ #include <sys/system_properties.h> -#include <EGL/egl.h> #include <android/native_window_jni.h> #include <jni/jni.hpp> -#include <mbgl/map/backend_scope.hpp> #include <mbgl/math/minmax.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/event.hpp> @@ -25,8 +23,10 @@ #include <mbgl/util/logging.hpp> #include <mbgl/util/platform.hpp> #include <mbgl/util/projection.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/image.hpp> #include <mbgl/style/filter.hpp> +#include <mbgl/renderer/query.hpp> // Java -> C++ conversion #include "style/android_conversion.hpp" @@ -41,12 +41,16 @@ #include "jni.hpp" #include "attach_env.hpp" +#include "map_renderer.hpp" +#include "android_renderer_frontend.hpp" +#include "file_source.hpp" #include "bitmap.hpp" #include "run_loop_impl.hpp" #include "java/util.hpp" #include "geometry/lat_lng_bounds.hpp" #include "map/camera_position.hpp" #include "style/light.hpp" +#include "bitmap_factory.hpp" namespace mbgl { namespace android { @@ -54,15 +58,12 @@ namespace android { NativeMapView::NativeMapView(jni::JNIEnv& _env, jni::Object<NativeMapView> _obj, jni::Object<FileSource> jFileSource, - jni::jfloat _pixelRatio, - jni::String _programCacheDir, - jni::jint _availableProcessors, - jni::jlong _totalMemory) - : javaPeer(_obj.NewWeakGlobalRef(_env)), - pixelRatio(_pixelRatio), - availableProcessors(_availableProcessors), - totalMemory(_totalMemory), - threadPool(sharedThreadPool()) { + jni::Object<MapRenderer> jMapRenderer, + jni::jfloat _pixelRatio) + : javaPeer(_obj.NewWeakGlobalRef(_env)) + , mapRenderer(MapRenderer::getNativePeer(_env, jMapRenderer)) + , pixelRatio(_pixelRatio) + , threadPool(sharedThreadPool()) { // Get a reference to the JavaVM for callbacks if (_env.GetJavaVM(&vm) < 0) { @@ -70,116 +71,30 @@ NativeMapView::NativeMapView(jni::JNIEnv& _env, return; } - // Create the core map - map = std::make_unique<mbgl::Map>( - *this, mbgl::Size{ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }, - pixelRatio, mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource), *threadPool, - MapMode::Continuous, GLContextMode::Unique, ConstrainMode::HeightOnly, - ViewportMode::Default, jni::Make<std::string>(_env, _programCacheDir)); - - recalculateSourceTileCacheSize(); -} + // Get native peer for file source + mbgl::FileSource& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource); -void NativeMapView::recalculateSourceTileCacheSize() { - //Calculate a fitting cache size based on device parameters - float zoomFactor = map->getMaxZoom() - map->getMinZoom() + 1; - float cpuFactor = availableProcessors; - float memoryFactor = static_cast<float>(totalMemory) / 1000.0f / 1000.0f / 1000.0f; - float sizeFactor = (static_cast<float>(map->getSize().width) / mbgl::util::tileSize) * - (static_cast<float>(map->getSize().height) / mbgl::util::tileSize); + // Create a renderer frontend + rendererFrontend = std::make_unique<AndroidRendererFrontend>(mapRenderer); - map->setSourceTileCacheSize(zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f); + // Create the core map + map = std::make_unique<mbgl::Map>(*rendererFrontend, *this, + mbgl::Size{ static_cast<uint32_t>(width), + static_cast<uint32_t>(height) }, pixelRatio, + fileSource, *threadPool, MapMode::Continuous, + ConstrainMode::HeightOnly, ViewportMode::Default); } /** * Called through NativeMapView#destroy() */ NativeMapView::~NativeMapView() { - _terminateContext(); - _destroySurface(); - _terminateDisplay(); - map.reset(); - vm = nullptr; } /** - * From mbgl::View - */ -void NativeMapView::bind() { - setFramebufferBinding(0); - setViewportSize(getFramebufferSize()); -} - -/** - * From mbgl::Backend. - */ -gl::ProcAddress NativeMapView::initializeExtension(const char* name) { - return eglGetProcAddress(name); -} - -void NativeMapView::activate() { - - oldDisplay = eglGetCurrentDisplay(); - oldReadSurface = eglGetCurrentSurface(EGL_READ); - oldDrawSurface = eglGetCurrentSurface(EGL_DRAW); - oldContext = eglGetCurrentContext(); - - assert(vm != nullptr); - - if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) { - if (!eglMakeCurrent(display, surface, surface, context)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - if (!eglSwapInterval(display, 0)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapInterval() returned error %d", eglGetError()); - throw std::runtime_error("eglSwapInterval() failed"); - } - } else { - mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready"); - } -} - -/** - * From mbgl::Backend. - */ -void NativeMapView::deactivate() { - assert(vm != nullptr); - - if (oldContext != context && oldContext != EGL_NO_CONTEXT) { - if (!eglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldContext)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - } else if (display != EGL_NO_DISPLAY) { - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - } else { - mbgl::Log::Info(mbgl::Event::Android, "Not deactivating as we are not ready"); - } -} - -/** - * From mbgl::Backend. Callback to java NativeMapView#onInvalidate(). - * - * May be called from any thread - */ -void NativeMapView::invalidate() { - android::UniqueEnv _env = android::AttachEnv(); - static auto onInvalidate = javaClass.GetMethod<void ()>(*_env, "onInvalidate"); - javaPeer->Call(*_env, onInvalidate); -} - -/** - * From mbgl::Backend. Callback to java NativeMapView#onMapChanged(int). + * From mbgl::RendererBackend. Callback to java NativeMapView#onMapChanged(int). * * May be called from any thread */ @@ -257,98 +172,26 @@ void NativeMapView::onSourceChanged(mbgl::style::Source&) { // JNI Methods // -void NativeMapView::initializeDisplay(jni::JNIEnv&) { - _initializeDisplay(); -} - -void NativeMapView::terminateDisplay(jni::JNIEnv&) { - _terminateDisplay(); -} - -void NativeMapView::initializeContext(jni::JNIEnv&) { - _initializeContext(); -} - -void NativeMapView::terminateContext(jni::JNIEnv&) { - _terminateContext(); -} - -void NativeMapView::createSurface(jni::JNIEnv& env, jni::Object<> _surface) { - _createSurface(ANativeWindow_fromSurface(&env, jni::Unwrap(*_surface))); -} - -void NativeMapView::destroySurface(jni::JNIEnv&) { - _destroySurface(); -} - -void NativeMapView::render(jni::JNIEnv& env) { - BackendScope guard(*this); - - if (framebufferSizeChanged) { - setViewportSize(getFramebufferSize()); - framebufferSizeChanged = false; - } - - map->render(*this); - - if(snapshot){ - snapshot = false; - - // take snapshot - auto image = readFramebuffer(getFramebufferSize()); - auto bitmap = Bitmap::CreateBitmap(env, std::move(image)); - - // invoke Mapview#OnSnapshotReady - android::UniqueEnv _env = android::AttachEnv(); - static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady"); - javaPeer->Call(*_env, onSnapshotReady, bitmap); - } - - if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) { - if (!eglSwapBuffers(display, surface)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d", - eglGetError()); - throw std::runtime_error("eglSwapBuffers() failed"); - } - - updateFps(); - } else { - mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready"); - } -} - -void NativeMapView::update(jni::JNIEnv&) { - invalidate(); -} - void NativeMapView::resizeView(jni::JNIEnv&, int w, int h) { width = util::max(64, w); height = util::max(64, h); map->setSize({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); - recalculateSourceTileCacheSize(); -} - -void NativeMapView::resizeFramebuffer(jni::JNIEnv&, int w, int h) { - fbWidth = w; - fbHeight = h; - framebufferSizeChanged = true; - invalidate(); } jni::String NativeMapView::getStyleUrl(jni::JNIEnv& env) { - return jni::Make<jni::String>(env, map->getStyleURL()); + return jni::Make<jni::String>(env, map->getStyle().getURL()); } void NativeMapView::setStyleUrl(jni::JNIEnv& env, jni::String url) { - map->setStyleURL(jni::Make<std::string>(env, url)); + map->getStyle().loadURL(jni::Make<std::string>(env, url)); } jni::String NativeMapView::getStyleJson(jni::JNIEnv& env) { - return jni::Make<jni::String>(env, map->getStyleJSON()); + return jni::Make<jni::String>(env, map->getStyle().getJSON()); } void NativeMapView::setStyleJson(jni::JNIEnv& env, jni::String json) { - map->setStyleJSON(jni::Make<std::string>(env, json)); + map->getStyle().loadJSON(jni::Make<std::string>(env, json)); } void NativeMapView::setLatLngBounds(jni::JNIEnv& env, jni::Object<mbgl::android::LatLngBounds> jBounds) { @@ -480,7 +323,6 @@ void NativeMapView::resetZoom(jni::JNIEnv&) { void NativeMapView::setMinZoom(jni::JNIEnv&, jni::jdouble zoom) { map->setMinZoom(zoom); - recalculateSourceTileCacheSize(); } jni::jdouble NativeMapView::getMinZoom(jni::JNIEnv&) { @@ -489,7 +331,6 @@ jni::jdouble NativeMapView::getMinZoom(jni::JNIEnv&) { void NativeMapView::setMaxZoom(jni::JNIEnv&, jni::jdouble zoom) { map->setMaxZoom(zoom); - recalculateSourceTileCacheSize(); } jni::jdouble NativeMapView::getMaxZoom(jni::JNIEnv&) { @@ -554,11 +395,15 @@ void NativeMapView::setContentPadding(JNIEnv&, double top, double left, double b } void NativeMapView::scheduleSnapshot(jni::JNIEnv&) { - snapshot = true; -} + mapRenderer.requestSnapshot([&](PremultipliedImage image) { + auto _env = android::AttachEnv(); + // Convert image to bitmap + auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image)); -void NativeMapView::enableFps(jni::JNIEnv&, jni::jboolean enable) { - fpsEnabled = enable; + // invoke Mapview#OnSnapshotReady + static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady"); + javaPeer->Call(*_env, onSnapshotReady, bitmap); + }); } jni::Object<CameraPosition> NativeMapView::getCameraPosition(jni::JNIEnv& env) { @@ -599,7 +444,7 @@ jni::Array<jni::jlong> NativeMapView::addMarkers(jni::JNIEnv& env, jni::Array<jn } void NativeMapView::onLowMemory(JNIEnv&) { - map->onLowMemory(); + rendererFrontend->onLowMemory(); } using DebugOptions = mbgl::MapDebugOptions; @@ -608,12 +453,10 @@ void NativeMapView::setDebug(JNIEnv&, jni::jboolean debug) { DebugOptions debugOptions = debug ? DebugOptions::TileBorders | DebugOptions::ParseStatus | DebugOptions::Collision : DebugOptions::NoDebug; map->setDebug(debugOptions); - fpsEnabled = debug; } void NativeMapView::cycleDebugOptions(JNIEnv&) { map->cycleDebugOptions(); - fpsEnabled = map->getDebug() != DebugOptions::NoDebug; } jni::jboolean NativeMapView::getDebug(JNIEnv&) { @@ -729,8 +572,13 @@ void NativeMapView::addAnnotationIcon(JNIEnv& env, jni::String symbol, jint w, j } jni::GetArrayRegion(env, *jpixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get())); - map->addAnnotationImage(symbolName, - std::make_unique<mbgl::style::Image>(std::move(premultipliedImage), float(scale))); + map->addAnnotationImage(std::make_unique<mbgl::style::Image>( + symbolName, std::move(premultipliedImage), float(scale))); +} + +void NativeMapView::removeAnnotationIcon(JNIEnv& env, jni::String symbol) { + const std::string symbolName = jni::Make<std::string>(env, symbol); + map->removeAnnotationImage(symbolName); } jdouble NativeMapView::getTopOffsetPixelsForAnnotationSymbol(JNIEnv& env, jni::String symbolName) { @@ -738,25 +586,25 @@ jdouble NativeMapView::getTopOffsetPixelsForAnnotationSymbol(JNIEnv& env, jni::S } jlong NativeMapView::getTransitionDuration(JNIEnv&) { - const auto transitionOptions = map->getTransitionOptions(); + const auto transitionOptions = map->getStyle().getTransitionOptions(); return std::chrono::duration_cast<std::chrono::milliseconds>(transitionOptions.duration.value_or(mbgl::Duration::zero())).count(); } void NativeMapView::setTransitionDuration(JNIEnv&, jlong duration) { - auto transitionOptions = map->getTransitionOptions(); + auto transitionOptions = map->getStyle().getTransitionOptions(); transitionOptions.duration.emplace(mbgl::Milliseconds(duration)); - map->setTransitionOptions(transitionOptions); + map->getStyle().setTransitionOptions(transitionOptions); } jlong NativeMapView::getTransitionDelay(JNIEnv&) { - const auto transitionOptions = map->getTransitionOptions(); + const auto transitionOptions = map->getStyle().getTransitionOptions(); return std::chrono::duration_cast<std::chrono::milliseconds>(transitionOptions.delay.value_or(mbgl::Duration::zero())).count(); } void NativeMapView::setTransitionDelay(JNIEnv&, jlong delay) { - auto transitionOptions = map->getTransitionOptions(); + auto transitionOptions = map->getStyle().getTransitionOptions(); transitionOptions.delay.emplace(mbgl::Milliseconds(delay)); - map->setTransitionOptions(transitionOptions); + map->getStyle().setTransitionOptions(transitionOptions); } jni::Array<jlong> NativeMapView::queryPointAnnotations(JNIEnv& env, jni::Object<RectF> rect) { @@ -770,7 +618,7 @@ jni::Array<jlong> NativeMapView::queryPointAnnotations(JNIEnv& env, jni::Object< }; // Assume only points for now - mbgl::AnnotationIDs ids = map->queryPointAnnotations(box); + mbgl::AnnotationIDs ids = rendererFrontend->queryPointAnnotations(box); // Convert result std::vector<jlong> longIds(ids.begin(), ids.end()); @@ -792,7 +640,9 @@ jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesFo } mapbox::geometry::point<double> point = {x, y}; - return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, map->queryRenderedFeatures(point, { layers, toFilter(env, jfilter) })); + return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>( + env, + rendererFrontend->queryRenderedFeatures(point, { layers, toFilter(env, jfilter) })); } jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesForBox(JNIEnv& env, jni::jfloat left, jni::jfloat top, @@ -810,11 +660,13 @@ jni::Array<jni::Object<geojson::Feature>> NativeMapView::queryRenderedFeaturesFo mapbox::geometry::point<double>{ right, bottom } }; - return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, map->queryRenderedFeatures(box, { layers, toFilter(env, jfilter) })); + return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>( + env, + rendererFrontend->queryRenderedFeatures(box, { layers, toFilter(env, jfilter) })); } jni::Object<Light> NativeMapView::getLight(JNIEnv& env) { - mbgl::style::Light* light = map->getLight(); + mbgl::style::Light* light = map->getStyle().getLight(); if (light) { return jni::Object<Light>(Light::createJavaLightPeer(env, *map, *light)); } else { @@ -825,7 +677,7 @@ jni::Object<Light> NativeMapView::getLight(JNIEnv& env) { jni::Array<jni::Object<Layer>> NativeMapView::getLayers(JNIEnv& env) { // Get the core layers - std::vector<style::Layer*> layers = map->getLayers(); + std::vector<style::Layer*> layers = map->getStyle().getLayers(); // Convert jni::Array<jni::Object<Layer>> jLayers = jni::Array<jni::Object<Layer>>::New(env, layers.size(), Layer::javaClass); @@ -843,7 +695,7 @@ jni::Array<jni::Object<Layer>> NativeMapView::getLayers(JNIEnv& env) { jni::Object<Layer> NativeMapView::getLayer(JNIEnv& env, jni::String layerId) { // Find the layer - mbgl::style::Layer* coreLayer = map->getLayer(jni::Make<std::string>(env, layerId)); + mbgl::style::Layer* coreLayer = map->getStyle().getLayer(jni::Make<std::string>(env, layerId)); if (!coreLayer) { mbgl::Log::Debug(mbgl::Event::JNI, "No layer found"); return jni::Object<Layer>(); @@ -870,7 +722,7 @@ void NativeMapView::addLayerAbove(JNIEnv& env, jlong nativeLayerPtr, jni::String Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr); // Find the sibling - auto layers = map->getLayers(); + auto layers = map->getStyle().getLayers(); auto siblingId = jni::Make<std::string>(env, above); size_t index = 0; @@ -905,7 +757,7 @@ void NativeMapView::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint inde assert(nativeLayerPtr != 0); Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr); - auto layers = map->getLayers(); + auto layers = map->getStyle().getLayers(); // Check index int numLayers = layers.size() - 1; @@ -928,7 +780,7 @@ void NativeMapView::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint inde * Remove by layer id. */ jni::Object<Layer> NativeMapView::removeLayerById(JNIEnv& env, jni::String id) { - std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(jni::Make<std::string>(env, id)); + std::unique_ptr<mbgl::style::Layer> coreLayer = map->getStyle().removeLayer(jni::Make<std::string>(env, id)); if (coreLayer) { return jni::Object<Layer>(createJavaLayerPeer(env, *map, std::move(coreLayer))); } else { @@ -940,7 +792,7 @@ jni::Object<Layer> NativeMapView::removeLayerById(JNIEnv& env, jni::String id) { * Remove layer at index. */ jni::Object<Layer> NativeMapView::removeLayerAt(JNIEnv& env, jni::jint index) { - auto layers = map->getLayers(); + auto layers = map->getStyle().getLayers(); // Check index int numLayers = layers.size() - 1; @@ -949,7 +801,7 @@ jni::Object<Layer> NativeMapView::removeLayerAt(JNIEnv& env, jni::jint index) { return jni::Object<Layer>(); } - std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(layers.at(index)->getID()); + std::unique_ptr<mbgl::style::Layer> coreLayer = map->getStyle().removeLayer(layers.at(index)->getID()); if (coreLayer) { return jni::Object<Layer>(createJavaLayerPeer(env, *map, std::move(coreLayer))); } else { @@ -964,7 +816,7 @@ void NativeMapView::removeLayer(JNIEnv&, jlong layerPtr) { assert(layerPtr != 0); mbgl::android::Layer *layer = reinterpret_cast<mbgl::android::Layer *>(layerPtr); - std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(layer->get().getID()); + std::unique_ptr<mbgl::style::Layer> coreLayer = map->getStyle().removeLayer(layer->get().getID()); if (coreLayer) { layer->setLayer(std::move(coreLayer)); } @@ -972,13 +824,13 @@ void NativeMapView::removeLayer(JNIEnv&, jlong layerPtr) { jni::Array<jni::Object<Source>> NativeMapView::getSources(JNIEnv& env) { // Get the core sources - std::vector<style::Source*> sources = map->getSources(); + std::vector<style::Source*> sources = map->getStyle().getSources(); // Convert jni::Array<jni::Object<Source>> jSources = jni::Array<jni::Object<Source>>::New(env, sources.size(), Source::javaClass); int index = 0; for (auto source : sources) { - auto jSource = jni::Object<Source>(createJavaSourcePeer(env, *map, *source)); + auto jSource = jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *source)); jSources.Set(env, index, jSource); jni::DeleteLocalRef(env, jSource); index++; @@ -989,14 +841,14 @@ jni::Array<jni::Object<Source>> NativeMapView::getSources(JNIEnv& env) { jni::Object<Source> NativeMapView::getSource(JNIEnv& env, jni::String sourceId) { // Find the source - mbgl::style::Source* coreSource = map->getSource(jni::Make<std::string>(env, sourceId)); + mbgl::style::Source* coreSource = map->getStyle().getSource(jni::Make<std::string>(env, sourceId)); if (!coreSource) { mbgl::Log::Debug(mbgl::Event::JNI, "No source found"); return jni::Object<Source>(); } // Create and return the source's native peer - return jni::Object<Source>(createJavaSourcePeer(env, *map, *coreSource)); + return jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *coreSource)); } void NativeMapView::addSource(JNIEnv& env, jni::jlong sourcePtr) { @@ -1005,15 +857,16 @@ void NativeMapView::addSource(JNIEnv& env, jni::jlong sourcePtr) { Source *source = reinterpret_cast<Source *>(sourcePtr); try { source->addToMap(*map); + source->setRendererFrontend(*rendererFrontend); } catch (const std::runtime_error& error) { jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/sources/CannotAddSourceException"), error.what()); } } jni::Object<Source> NativeMapView::removeSourceById(JNIEnv& env, jni::String id) { - std::unique_ptr<mbgl::style::Source> coreSource = map->removeSource(jni::Make<std::string>(env, id)); + std::unique_ptr<mbgl::style::Source> coreSource = map->getStyle().removeSource(jni::Make<std::string>(env, id)); if (coreSource) { - return jni::Object<Source>(createJavaSourcePeer(env, *map, *coreSource)); + return jni::Object<Source>(createJavaSourcePeer(env, *rendererFrontend, *coreSource)); } else { return jni::Object<Source>(); } @@ -1023,7 +876,7 @@ void NativeMapView::removeSource(JNIEnv&, jlong sourcePtr) { assert(sourcePtr != 0); mbgl::android::Source *source = reinterpret_cast<mbgl::android::Source *>(sourcePtr); - std::unique_ptr<mbgl::style::Source> coreSource = map->removeSource(source->get().getID()); + std::unique_ptr<mbgl::style::Source> coreSource = map->getStyle().removeSource(source->get().getID()); if (coreSource) { source->setSource(std::move(coreSource)); } @@ -1040,415 +893,31 @@ void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::jint w, jni::ji jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get())); - map->addImage(jni::Make<std::string>(env, name), - std::make_unique<mbgl::style::Image>(std::move(premultipliedImage), float(scale))); + map->getStyle().addImage(std::make_unique<mbgl::style::Image>( + jni::Make<std::string>(env, name), + std::move(premultipliedImage), + float(scale))); } void NativeMapView::removeImage(JNIEnv& env, jni::String name) { - map->removeImage(jni::Make<std::string>(env, name)); -} - -// Private methods // - -void NativeMapView::_initializeDisplay() { - assert(display == EGL_NO_DISPLAY); - assert(config == nullptr); - assert(format < 0); - - display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (display == EGL_NO_DISPLAY) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetDisplay() returned error %d", eglGetError()); - throw std::runtime_error("eglGetDisplay() failed"); - } - - EGLint major, minor; - if (!eglInitialize(display, &major, &minor)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglInitialize() returned error %d", eglGetError()); - throw std::runtime_error("eglInitialize() failed"); - } - if ((major <= 1) && (minor < 3)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "EGL version is too low, need 1.3, got %d.%d", major, - minor); - throw std::runtime_error("EGL version is too low"); - } - - // Detect if we are in emulator. - const bool inEmulator = []() { - char prop[PROP_VALUE_MAX]; - __system_property_get("ro.kernel.qemu", prop); - return strtol(prop, nullptr, 0) == 1; - }(); - - if (inEmulator) { - // XXX https://code.google.com/p/android/issues/detail?id=78977 - mbgl::Log::Warning(mbgl::Event::Android, "Running SDK in emulator!"); - } - - if (!eglBindAPI(EGL_OPENGL_ES_API)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglBindAPI(EGL_OPENGL_ES_API) returned error %d", eglGetError()); - throw std::runtime_error("eglBindAPI() failed"); - } - - // Get all configs at least RGB 565 with 16 depth and 8 stencil - EGLint configAttribs[] = { - EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_BUFFER_SIZE, 16, - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, - EGL_DEPTH_SIZE, 16, - EGL_STENCIL_SIZE, 8, - (inEmulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT, - (inEmulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER, - EGL_NONE - }; - - EGLint numConfigs; - if (!eglChooseConfig(display, configAttribs, nullptr, 0, &numConfigs)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d", - eglGetError()); - throw std::runtime_error("eglChooseConfig() failed"); - } - if (numConfigs < 1) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs."); - throw std::runtime_error("eglChooseConfig() failed"); - } - - const auto configs = std::make_unique<EGLConfig[]>(numConfigs); - if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError()); - throw std::runtime_error("eglChooseConfig() failed"); - } - - config = chooseConfig(configs.get(), numConfigs); - if (config == nullptr) { - mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen"); - throw std::runtime_error("No config chosen"); - } - - if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib() returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } -} - -// Quality -typedef enum { - Format16Bit = 3, - Format32BitNoAlpha = 1, - Format32BitAlpha = 2, - Format24Bit = 0, - Unknown = 4 -} BufferFormat; - -typedef enum { - Format16Depth8Stencil = 1, - Format24Depth8Stencil = 0, -} DepthStencilFormat; - -// Tuple is <buffer_format, depth_stencil_format, is_not_conformant, is_caveat, config_num, -// config_id> -typedef std::tuple<BufferFormat, DepthStencilFormat, bool, bool, int, EGLConfig> ConfigProperties; - -EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfigs) { - // Create a list of configs that pass our filters - std::list<ConfigProperties> configList; - for (int i = 0; i < numConfigs; i++) { - EGLint caveat, conformant, bits, red, green, blue, alpha, alphaMask, depth, stencil, - sampleBuffers, samples; - - if (!eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alphaMask)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sampleBuffers)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d", - eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError()); - throw std::runtime_error("eglGetConfigAttrib() failed"); - } - - bool configOk = true; - configOk &= (depth == 24) || (depth == 16); - configOk &= stencil == 8; - configOk &= sampleBuffers == 0; - configOk &= samples == 0; - - // Filter our configs first for depth, stencil and anti-aliasing - if (configOk) { - // Work out the config's buffer format - BufferFormat bufferFormat; - if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) { - bufferFormat = Format16Bit; - } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) { - bufferFormat = Format32BitNoAlpha; - } else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) { - bufferFormat = Format32BitAlpha; - } else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) { - bufferFormat = Format24Bit; - } else { - bufferFormat = Unknown; - } - - // Work out the config's depth stencil format - DepthStencilFormat depthStencilFormat; - if ((depth == 16) && (stencil == 8)) { - depthStencilFormat = Format16Depth8Stencil; - } else { - depthStencilFormat = Format24Depth8Stencil; - } - - bool isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT; - bool isCaveat = caveat != EGL_NONE; - EGLConfig configId = configs[i]; - - // Ignore formats we don't recognise - if (bufferFormat != Unknown) { - configList.push_back(std::make_tuple(bufferFormat, depthStencilFormat, - isNotConformant, isCaveat, i, configId)); - } - } - } - - if (configList.empty()) { - mbgl::Log::Error(mbgl::Event::OpenGL, "Config list was empty."); - } - - // Sort the configs to find the best one - configList.sort(); - bool isConformant = !std::get<2>(configList.front()); - bool isCaveat = std::get<3>(configList.front()); - EGLConfig configId = std::get<5>(configList.front()); - - if (isCaveat) { - mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config has a caveat."); - } - if (!isConformant) { - mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config is not conformant."); - } - - return configId; + map->getStyle().removeImage(jni::Make<std::string>(env, name)); } -void NativeMapView::_terminateDisplay() { - if (display != EGL_NO_DISPLAY) { - // Destroy the surface first, if it still exists. This call needs a valid surface. - if (surface != EGL_NO_SURFACE) { - if (!eglDestroySurface(display, surface)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d", - eglGetError()); - throw std::runtime_error("eglDestroySurface() failed"); - } - surface = EGL_NO_SURFACE; - } - - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - if (!eglTerminate(display)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d", - eglGetError()); - throw std::runtime_error("eglTerminate() failed"); - } - } - - display = EGL_NO_DISPLAY; - config = nullptr; - format = -1; -} - -void NativeMapView::_initializeContext() { - assert(display != EGL_NO_DISPLAY); - assert(context == EGL_NO_CONTEXT); - assert(config != nullptr); - - const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs); - if (context == EGL_NO_CONTEXT) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error %d", - eglGetError()); - throw std::runtime_error("eglCreateContext() failed"); - } -} - -void NativeMapView::_terminateContext() { - if (display != EGL_NO_DISPLAY) { - - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - if (context != EGL_NO_CONTEXT) { - if (!eglDestroyContext(display, context)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroyContext() returned error %d", - eglGetError()); - throw std::runtime_error("eglDestroyContext() failed"); - } - } - } - - context = EGL_NO_CONTEXT; -} - -void NativeMapView::_createSurface(ANativeWindow *window_) { - assert(window == nullptr); - assert(window_ != nullptr); - window = window_; - - assert(display != EGL_NO_DISPLAY); - assert(surface == EGL_NO_SURFACE); - assert(config != nullptr); - assert(format >= 0); - - ANativeWindow_setBuffersGeometry(window, 0, 0, format); - - const EGLint surfaceAttribs[] = {EGL_NONE}; - surface = eglCreateWindowSurface(display, config, window, surfaceAttribs); - if (surface == EGL_NO_SURFACE) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d", - eglGetError()); - throw std::runtime_error("eglCreateWindowSurface() failed"); - } - - if (firstRender) { - firstRender = false; - - BackendScope guard(*this); - - if (!eglMakeCurrent(display, surface, surface, context)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - } -} - -void NativeMapView::_destroySurface() { - if (surface != EGL_NO_SURFACE) { - if (!eglDestroySurface(display, surface)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d", - eglGetError()); - throw std::runtime_error("eglDestroySurface() failed"); - } - } - - surface = EGL_NO_SURFACE; - firstRender = true; - - if (window != nullptr) { - ANativeWindow_release(window); - window = nullptr; +jni::Object<Bitmap> NativeMapView::getImage(JNIEnv& env, jni::String name) { + const mbgl::style::Image *image = map->getStyle().getImage(jni::Make<std::string>(env, name)); + if (image) { + return Bitmap::CreateBitmap(env, image->getImage()); + } else { + return jni::Object<Bitmap>(); } } -mbgl::Size NativeMapView::getFramebufferSize() const { - return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) }; +void NativeMapView::setPrefetchesTiles(JNIEnv&, jni::jboolean enable) { + map->setPrefetchZoomDelta(enable ? util::DEFAULT_PREFETCH_ZOOM_DELTA : uint8_t(0)); } -void NativeMapView::updateAssumedState() { - assumeFramebufferBinding(0); - assumeViewportSize(getFramebufferSize()); -} - -void NativeMapView::updateFps() { - if (!fpsEnabled) { - return; - } - - static int frames = 0; - static int64_t timeElapsed = 0LL; - - frames++; - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - int64_t currentTime = now.tv_sec * 1000000000LL + now.tv_nsec; - - if (currentTime - timeElapsed >= 1) { - fps = frames / ((currentTime - timeElapsed) / 1E9); - mbgl::Log::Info(mbgl::Event::Render, "FPS: %4.2f", fps); - timeElapsed = currentTime; - frames = 0; - } - - assert(vm != nullptr); - - android::UniqueEnv _env = android::AttachEnv(); - static auto onFpsChanged = javaClass.GetMethod<void (double)>(*_env, "onFpsChanged"); - javaPeer->Call(*_env, onFpsChanged, fps); +jni::jboolean NativeMapView::getPrefetchesTiles(JNIEnv&) { + return jni::jboolean(map->getPrefetchZoomDelta() > 0); } // Static methods // @@ -1463,19 +932,10 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { // Register the peer jni::RegisterNativePeer<NativeMapView>(env, NativeMapView::javaClass, "nativePtr", - std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::jfloat, jni::String, jni::jint, jni::jlong>, + std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::Object<MapRenderer>, jni::jfloat>, "nativeInitialize", "nativeDestroy", - METHOD(&NativeMapView::render, "nativeRender"), - METHOD(&NativeMapView::update, "nativeUpdate"), METHOD(&NativeMapView::resizeView, "nativeResizeView"), - METHOD(&NativeMapView::resizeFramebuffer, "nativeResizeFramebuffer"), - METHOD(&NativeMapView::initializeDisplay, "nativeInitializeDisplay"), - METHOD(&NativeMapView::terminateDisplay, "nativeTerminateDisplay"), - METHOD(&NativeMapView::initializeContext, "nativeInitializeContext"), - METHOD(&NativeMapView::terminateContext, "nativeTerminateContext"), - METHOD(&NativeMapView::createSurface, "nativeCreateSurface"), - METHOD(&NativeMapView::destroySurface, "nativeDestroySurface"), METHOD(&NativeMapView::getStyleUrl, "nativeGetStyleUrl"), METHOD(&NativeMapView::setStyleUrl, "nativeSetStyleUrl"), METHOD(&NativeMapView::getStyleJson, "nativeGetStyleJson"), @@ -1508,7 +968,6 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { METHOD(&NativeMapView::setVisibleCoordinateBounds, "nativeSetVisibleCoordinateBounds"), METHOD(&NativeMapView::setContentPadding, "nativeSetContentPadding"), METHOD(&NativeMapView::scheduleSnapshot, "nativeTakeSnapshot"), - METHOD(&NativeMapView::enableFps, "nativeSetEnableFps"), METHOD(&NativeMapView::getCameraPosition, "nativeGetCameraPosition"), METHOD(&NativeMapView::updateMarker, "nativeUpdateMarker"), METHOD(&NativeMapView::addMarkers, "nativeAddMarkers"), @@ -1528,6 +987,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { METHOD(&NativeMapView::updatePolygon, "nativeUpdatePolygon"), METHOD(&NativeMapView::removeAnnotations, "nativeRemoveAnnotations"), METHOD(&NativeMapView::addAnnotationIcon, "nativeAddAnnotationIcon"), + METHOD(&NativeMapView::removeAnnotationIcon, "nativeRemoveAnnotationIcon"), METHOD(&NativeMapView::getTopOffsetPixelsForAnnotationSymbol, "nativeGetTopOffsetPixelsForAnnotationSymbol"), METHOD(&NativeMapView::getTransitionDuration, "nativeGetTransitionDuration"), METHOD(&NativeMapView::setTransitionDuration, "nativeSetTransitionDuration"), @@ -1552,9 +1012,12 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { METHOD(&NativeMapView::removeSource, "nativeRemoveSource"), METHOD(&NativeMapView::addImage, "nativeAddImage"), METHOD(&NativeMapView::removeImage, "nativeRemoveImage"), - METHOD(&NativeMapView::setLatLngBounds, "nativeSetLatLngBounds") + METHOD(&NativeMapView::getImage, "nativeGetImage"), + METHOD(&NativeMapView::setLatLngBounds, "nativeSetLatLngBounds"), + METHOD(&NativeMapView::setPrefetchesTiles, "nativeSetPrefetchesTiles"), + METHOD(&NativeMapView::getPrefetchesTiles, "nativeGetPrefetchesTiles") ); } -} -} +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index c638de44fb..72c7b1a9eb 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -1,17 +1,14 @@ #pragma once -#include <mbgl/map/backend.hpp> #include <mbgl/map/change.hpp> #include <mbgl/map/camera.hpp> #include <mbgl/map/map.hpp> -#include <mbgl/map/view.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/default_thread_pool.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/storage/default_file_source.hpp> #include <mbgl/storage/network_status.hpp> -#include "file_source.hpp" #include "annotation/marker.hpp" #include "annotation/polygon.hpp" #include "annotation/polyline.hpp" @@ -25,6 +22,7 @@ #include "geometry/lat_lng_bounds.hpp" #include "map/camera_position.hpp" #include "style/light.hpp" +#include "bitmap.hpp" #include <exception> #include <string> @@ -36,7 +34,11 @@ namespace mbgl { namespace android { -class NativeMapView : public View, public Backend { +class AndroidRendererFrontend; +class FileSource; +class MapRenderer; + +class NativeMapView : public MapObserver { public: static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/NativeMapView"; }; @@ -48,26 +50,15 @@ public: NativeMapView(jni::JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, - jni::jfloat pixelRatio, - jni::String programCacheDir, - jni::jint availableProcessors, - jni::jlong totalMemory); + jni::Object<MapRenderer>, + jni::jfloat pixelRatio); virtual ~NativeMapView(); - // mbgl::View // - - void bind() override; - - // mbgl::Backend // - - void updateAssumedState() override; - void invalidate() override; - // Deprecated // void notifyMapChange(mbgl::MapChange); - // mbgl::Backend (mbgl::MapObserver) // + // mbgl::RendererBackend (mbgl::MapObserver) // void onCameraWillChange(MapObserver::CameraChangeMode) override; void onCameraIsChanging() override; void onCameraDidChange(MapObserver::CameraChangeMode) override; @@ -83,28 +74,8 @@ public: // JNI // - void destroy(jni::JNIEnv&); - - void render(jni::JNIEnv&); - - void update(jni::JNIEnv&); - void resizeView(jni::JNIEnv&, int, int); - void resizeFramebuffer(jni::JNIEnv&, int, int); - - void initializeDisplay(jni::JNIEnv&); - - void terminateDisplay(jni::JNIEnv&); - - void initializeContext(jni::JNIEnv&); - - void terminateContext(jni::JNIEnv&); - - void createSurface(jni::JNIEnv&, jni::Object<>); - - void destroySurface(jni::JNIEnv&); - jni::String getStyleUrl(jni::JNIEnv&); void setStyleUrl(jni::JNIEnv&, jni::String); @@ -171,8 +142,6 @@ public: void scheduleSnapshot(jni::JNIEnv&); - void enableFps(jni::JNIEnv&, jni::jboolean enable); - jni::Object<CameraPosition> getCameraPosition(jni::JNIEnv&); void updateMarker(jni::JNIEnv&, jni::jlong, jni::jdouble, jni::jdouble, jni::String); @@ -211,6 +180,8 @@ public: void addAnnotationIcon(JNIEnv&, jni::String, jint, jint, jfloat, jni::Array<jbyte>); + void removeAnnotationIcon(JNIEnv&, jni::String); + jni::jdouble getTopOffsetPixelsForAnnotationSymbol(JNIEnv&, jni::String); jni::jlong getTransitionDuration(JNIEnv&); @@ -263,72 +234,28 @@ public: void removeImage(JNIEnv&, jni::String); -protected: - // mbgl::Backend // + jni::Object<Bitmap> getImage(JNIEnv&, jni::String); - gl::ProcAddress initializeExtension(const char*) override; - void activate() override; - void deactivate() override; + void setPrefetchesTiles(JNIEnv&, jni::jboolean); -private: - void _initializeDisplay(); - - void _terminateDisplay(); - - void _initializeContext(); - - void _terminateContext(); - - void _createSurface(ANativeWindow*); - - void _destroySurface(); - - EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs); - - mbgl::Size getFramebufferSize() const; - - void updateFps(); + jni::jboolean getPrefetchesTiles(JNIEnv&); private: - void recalculateSourceTileCacheSize(); + std::unique_ptr<AndroidRendererFrontend> rendererFrontend; JavaVM *vm = nullptr; jni::UniqueWeakObject<NativeMapView> javaPeer; + MapRenderer& mapRenderer; + std::string styleUrl; std::string apiKey; - ANativeWindow *window = nullptr; - - EGLConfig config = nullptr; - EGLint format = -1; - - EGLDisplay oldDisplay = EGL_NO_DISPLAY; - EGLSurface oldReadSurface = EGL_NO_SURFACE; - EGLSurface oldDrawSurface = EGL_NO_SURFACE; - EGLContext oldContext = EGL_NO_CONTEXT; - - EGLDisplay display = EGL_NO_DISPLAY; - EGLSurface surface = EGL_NO_SURFACE; - EGLContext context = EGL_NO_CONTEXT; - - float pixelRatio; - bool fpsEnabled = false; - bool snapshot = false; - bool firstRender = true; - double fps = 0.0; // Minimum texture size according to OpenGL ES 2.0 specification. int width = 64; int height = 64; - int fbWidth = 64; - int fbHeight = 64; - - bool framebufferSizeChanged = true; - - int availableProcessors = 0; - size_t totalMemory = 0; // Ensure these are initialised last std::shared_ptr<mbgl::ThreadPool> threadPool; diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp index 49d28f2ebb..dff7d1d984 100644 --- a/platform/android/src/run_loop.cpp +++ b/platform/android/src/run_loop.cpp @@ -1,10 +1,10 @@ #include "run_loop_impl.hpp" #include <mbgl/util/platform.hpp> -#include <mbgl/util/thread.hpp> -#include <mbgl/util/thread_context.hpp> #include <mbgl/util/thread_local.hpp> +#include <mbgl/util/thread.hpp> #include <mbgl/util/timer.hpp> +#include <mbgl/actor/scheduler.hpp> #include <android/looper.h> @@ -24,7 +24,6 @@ namespace { using namespace mbgl::util; -static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>; int looperCallbackNew(int fd, int, void* data) { int buffer[1]; @@ -62,9 +61,13 @@ namespace util { // timeout, but on the main thread `ALooper_pollAll` is called by the activity // automatically, thus we cannot set the timeout. Instead we wake the loop // with an external file descriptor event coming from this thread. +// +// Usually an actor should not carry pointers to other threads, but in +// this case the RunLoop itself owns the Alarm and calling wake() is the most +// efficient way of waking up the RunLoop and it is also thread-safe. class Alarm { public: - Alarm(RunLoop::Impl* loop_) : loop(loop_) {} + Alarm(ActorRef<Alarm>, RunLoop::Impl* loop_) : loop(loop_) {} void set(const Milliseconds& timeout) { alarm.start(timeout, mbgl::Duration::zero(), [this]() { loop->wake(); }); @@ -102,7 +105,7 @@ RunLoop::Impl::Impl(RunLoop* runLoop_, RunLoop::Type type) : runLoop(runLoop_) { case Type::Default: ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, looperCallbackDefault, this); - alarm = std::make_unique<Thread<Alarm>>(ThreadContext{"Alarm"}, this); + alarm = std::make_unique<Thread<Alarm>>("Alarm", this); running = true; break; } @@ -190,26 +193,27 @@ Milliseconds RunLoop::Impl::processRunnables() { auto timeout = std::chrono::duration_cast<Milliseconds>(nextDue - now); if (alarm) { - alarm->invoke(&Alarm::set, timeout); + alarm->actor().invoke(&Alarm::set, timeout); } return timeout; } RunLoop* RunLoop::Get() { - return current.get(); + assert(static_cast<RunLoop*>(Scheduler::GetCurrent())); + return static_cast<RunLoop*>(Scheduler::GetCurrent()); } RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>(this, type)) { - current.set(this); + Scheduler::SetCurrent(this); } RunLoop::~RunLoop() { - current.set(nullptr); + Scheduler::SetCurrent(nullptr); } LOOP_HANDLE RunLoop::getLoopHandle() { - return current.get()->impl.get(); + return Get()->impl.get(); } void RunLoop::push(std::shared_ptr<WorkTask> task) { diff --git a/platform/android/src/snapshotter/map_snapshotter.cpp b/platform/android/src/snapshotter/map_snapshotter.cpp new file mode 100644 index 0000000000..d64218d11a --- /dev/null +++ b/platform/android/src/snapshotter/map_snapshotter.cpp @@ -0,0 +1,110 @@ +#include "map_snapshotter.hpp" + +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/util/shared_thread_pool.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/actor/scheduler.hpp> + +#include "../attach_env.hpp" +#include "../bitmap.hpp" + +namespace mbgl { +namespace android { + +MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env, + jni::Object<MapSnapshotter> _obj, + jni::Object<FileSource> jFileSource, + jni::jfloat _pixelRatio, + jni::jint width, + jni::jint height, + jni::String styleURL, + jni::Object<LatLngBounds> region, + jni::Object<CameraPosition> position, + jni::String _programCacheDir) + : javaPeer(SeizeGenericWeakRef(_env, jni::Object<MapSnapshotter>(jni::NewWeakGlobalRef(_env, _obj.Get()).release()))) + , pixelRatio(_pixelRatio) + , threadPool(sharedThreadPool()) { + + // Get a reference to the JavaVM for callbacks + if (_env.GetJavaVM(&vm) < 0) { + _env.ExceptionDescribe(); + return; + } + + auto& fileSource = mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource); + auto size = mbgl::Size { static_cast<uint32_t>(width), static_cast<uint32_t>(height) }; + auto cameraOptions = position ? CameraPosition::getCameraOptions(_env, position) : CameraOptions(); + optional<mbgl::LatLngBounds> bounds; + if (region) { + bounds = LatLngBounds::getLatLngBounds(_env, region); + } + + // Create the core snapshotter + snapshotter = std::make_unique<mbgl::MapSnapshotter>(fileSource, + *threadPool, + jni::Make<std::string>(_env, styleURL), + size, + pixelRatio, + cameraOptions, + bounds, + jni::Make<std::string>(_env, _programCacheDir)); + +} + +MapSnapshotter::~MapSnapshotter() = default; + +void MapSnapshotter::start(JNIEnv&) { + MBGL_VERIFY_THREAD(tid); + + snapshotCallback = std::make_unique<Actor<mbgl::MapSnapshotter::Callback>>(*Scheduler::GetCurrent(), [this](std::exception_ptr err, PremultipliedImage image) { + MBGL_VERIFY_THREAD(tid); + android::UniqueEnv _env = android::AttachEnv(); + + if (err) { + // error handler callback + static auto onSnapshotFailed = javaClass.GetMethod<void (jni::String)>(*_env, "onSnapshotFailed"); + javaPeer->Call(*_env, onSnapshotFailed, jni::Make<jni::String>(*_env, util::toString(err))); + } else { + // Create the bitmap + auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image)); + + // invoke callback + static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady"); + javaPeer->Call(*_env, onSnapshotReady, bitmap); + } + }); + + snapshotter->snapshot(snapshotCallback->self()); +} + +void MapSnapshotter::cancel(JNIEnv&) { + MBGL_VERIFY_THREAD(tid); + + snapshotCallback.reset(); + snapshotter.reset(); +} + +// Static methods // + +jni::Class<MapSnapshotter> MapSnapshotter::javaClass; + +void MapSnapshotter::registerNative(jni::JNIEnv& env) { + // Lookup the class + MapSnapshotter::javaClass = *jni::Class<MapSnapshotter>::Find(env).NewGlobalRef(env).release(); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<MapSnapshotter>(env, MapSnapshotter::javaClass, "nativePtr", + std::make_unique<MapSnapshotter, JNIEnv&, jni::Object<MapSnapshotter>, jni::Object<FileSource>, jni::jfloat, jni::jint, jni::jint, jni::String, jni::Object<LatLngBounds>, jni::Object<CameraPosition>, jni::String>, + "nativeInitialize", + "finalize", + METHOD(&MapSnapshotter::start, "nativeStart"), + METHOD(&MapSnapshotter::cancel, "nativeCancel") + ); +} + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/snapshotter/map_snapshotter.hpp b/platform/android/src/snapshotter/map_snapshotter.hpp new file mode 100644 index 0000000000..093f589c05 --- /dev/null +++ b/platform/android/src/snapshotter/map_snapshotter.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include <mbgl/map/map_snapshotter.hpp> +#include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/util.hpp> + +#include "../file_source.hpp" +#include "../geometry/lat_lng_bounds.hpp" +#include "../map/camera_position.hpp" + +#include <jni/jni.hpp> +#include "../jni/generic_global_ref_deleter.hpp" + +#include <memory> + +namespace mbgl { +namespace android { + +class SnapshotterRendererFrontend; + +class MapSnapshotter { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/snapshotter/MapSnapshotter"; }; + + static jni::Class<MapSnapshotter> javaClass; + + static void registerNative(jni::JNIEnv&); + + MapSnapshotter(jni::JNIEnv&, + jni::Object<MapSnapshotter>, + jni::Object<FileSource>, + jni::jfloat pixelRatio, + jni::jint width, + jni::jint height, + jni::String styleURL, + jni::Object<LatLngBounds> region, + jni::Object<CameraPosition> position, + jni::String programCacheDir); + + ~MapSnapshotter(); + + void start(JNIEnv&); + + void cancel(JNIEnv&); + +private: + MBGL_STORE_THREAD(tid); + + JavaVM *vm = nullptr; + GenericUniqueWeakObject<MapSnapshotter> javaPeer; + + float pixelRatio; + + std::shared_ptr<mbgl::ThreadPool> threadPool; + std::unique_ptr<Actor<mbgl::MapSnapshotter::Callback>> snapshotCallback; + std::unique_ptr<mbgl::MapSnapshotter> snapshotter; +}; + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/style/android_conversion.hpp b/platform/android/src/style/android_conversion.hpp index e2b2685928..082fe411e2 100644 --- a/platform/android/src/style/android_conversion.hpp +++ b/platform/android/src/style/android_conversion.hpp @@ -2,6 +2,7 @@ #include "value.hpp" +#include <mbgl/util/feature.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/style/conversion.hpp> #include <mbgl/util/optional.hpp> @@ -66,6 +67,14 @@ inline optional<float> toNumber(const mbgl::android::Value& value) { } } +inline optional<double> toDouble(const mbgl::android::Value& value) { + if (value.isNumber()) { + return value.toDouble(); + } else { + return {}; + } +} + inline optional<std::string> toString(const mbgl::android::Value& value) { if (value.isString()) { return value.toString(); diff --git a/platform/android/src/style/conversion/latlngquad.hpp b/platform/android/src/style/conversion/latlngquad.hpp new file mode 100644 index 0000000000..9d1a83e164 --- /dev/null +++ b/platform/android/src/style/conversion/latlngquad.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <mapbox/geojson.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/geojson.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +optional<std::array<LatLng, 4>> Converter<std::array<LatLng, 4>>::operator()(const mbgl::android::Value& value, Error& error) const { + if (value.isNull() || !value.isArray()) { + error = { "value cannot be converted to LatLng array" }; + return {}; + } + + return convert<GeoJSON>(value.toString(), error); +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/platform/android/src/style/conversion/transition_options.hpp b/platform/android/src/style/conversion/transition_options.hpp index 3614878f43..ae65a32194 100644 --- a/platform/android/src/style/conversion/transition_options.hpp +++ b/platform/android/src/style/conversion/transition_options.hpp @@ -3,6 +3,7 @@ #include "../../conversion/conversion.hpp" #include <jni/jni.hpp> +#include <mbgl/style/transition_options.hpp> #include "../../jni/local_object.hpp" #include "../transition_options.hpp" diff --git a/platform/android/src/style/conversion/types.hpp b/platform/android/src/style/conversion/types.hpp index a00f668c24..375d1a33aa 100644 --- a/platform/android/src/style/conversion/types.hpp +++ b/platform/android/src/style/conversion/types.hpp @@ -58,15 +58,15 @@ struct Converter<jni::jobject*, mbgl::style::IconTextFitType> { }; template <> -struct Converter<jni::jobject*, mbgl::style::TextJustifyType> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextJustifyType& value) const { +struct Converter<jni::jobject*, mbgl::style::SymbolAnchorType> { + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::SymbolAnchorType& value) const { return convert<jni::jobject*, std::string>(env, toString(value)); } }; template <> -struct Converter<jni::jobject*, mbgl::style::TextAnchorType> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextAnchorType& value) const { +struct Converter<jni::jobject*, mbgl::style::TextJustifyType> { + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::TextJustifyType& value) const { return convert<jni::jobject*, std::string>(env, toString(value)); } }; diff --git a/platform/android/src/style/conversion/types_string_values.hpp b/platform/android/src/style/conversion/types_string_values.hpp index e96de3b01e..a19ca33a2f 100644 --- a/platform/android/src/style/conversion/types_string_values.hpp +++ b/platform/android/src/style/conversion/types_string_values.hpp @@ -109,51 +109,34 @@ namespace conversion { } } - // text-justify - inline std::string toString(mbgl::style::TextJustifyType value) { - switch (value) { - case mbgl::style::TextJustifyType::Left: - return "left"; - break; - case mbgl::style::TextJustifyType::Center: - return "center"; - break; - case mbgl::style::TextJustifyType::Right: - return "right"; - break; - default: - throw std::runtime_error("Not implemented"); - } - } - - // text-anchor - inline std::string toString(mbgl::style::TextAnchorType value) { + // icon-anchor + inline std::string toString(mbgl::style::SymbolAnchorType value) { switch (value) { - case mbgl::style::TextAnchorType::Center: + case mbgl::style::SymbolAnchorType::Center: return "center"; break; - case mbgl::style::TextAnchorType::Left: + case mbgl::style::SymbolAnchorType::Left: return "left"; break; - case mbgl::style::TextAnchorType::Right: + case mbgl::style::SymbolAnchorType::Right: return "right"; break; - case mbgl::style::TextAnchorType::Top: + case mbgl::style::SymbolAnchorType::Top: return "top"; break; - case mbgl::style::TextAnchorType::Bottom: + case mbgl::style::SymbolAnchorType::Bottom: return "bottom"; break; - case mbgl::style::TextAnchorType::TopLeft: + case mbgl::style::SymbolAnchorType::TopLeft: return "top-left"; break; - case mbgl::style::TextAnchorType::TopRight: + case mbgl::style::SymbolAnchorType::TopRight: return "top-right"; break; - case mbgl::style::TextAnchorType::BottomLeft: + case mbgl::style::SymbolAnchorType::BottomLeft: return "bottom-left"; break; - case mbgl::style::TextAnchorType::BottomRight: + case mbgl::style::SymbolAnchorType::BottomRight: return "bottom-right"; break; default: @@ -161,6 +144,23 @@ namespace conversion { } } + // text-justify + inline std::string toString(mbgl::style::TextJustifyType value) { + switch (value) { + case mbgl::style::TextJustifyType::Left: + return "left"; + break; + case mbgl::style::TextJustifyType::Center: + return "center"; + break; + case mbgl::style::TextJustifyType::Right: + return "right"; + break; + default: + throw std::runtime_error("Not implemented"); + } + } + // text-transform inline std::string toString(mbgl::style::TextTransformType value) { switch (value) { diff --git a/platform/android/src/style/layers/circle_layer.cpp b/platform/android/src/style/layers/circle_layer.cpp index 96a9356679..4c7f69e956 100644 --- a/platform/android/src/style/layers/circle_layer.cpp +++ b/platform/android/src/style/layers/circle_layer.cpp @@ -142,6 +142,12 @@ namespace android { return jni::Object<jni::ObjectTag>(*converted); } + jni::Object<jni::ObjectTag> CircleLayer::getCirclePitchAlignment(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCirclePitchAlignment()); + return jni::Object<jni::ObjectTag>(*converted); + } + jni::Object<jni::ObjectTag> CircleLayer::getCircleStrokeWidth(jni::JNIEnv& env) { using namespace mbgl::android::conversion; Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::CircleLayer>()->CircleLayer::getCircleStrokeWidth()); @@ -236,6 +242,7 @@ namespace android { METHOD(&CircleLayer::getCircleTranslate, "nativeGetCircleTranslate"), METHOD(&CircleLayer::getCircleTranslateAnchor, "nativeGetCircleTranslateAnchor"), METHOD(&CircleLayer::getCirclePitchScale, "nativeGetCirclePitchScale"), + METHOD(&CircleLayer::getCirclePitchAlignment, "nativeGetCirclePitchAlignment"), METHOD(&CircleLayer::getCircleStrokeWidthTransition, "nativeGetCircleStrokeWidthTransition"), METHOD(&CircleLayer::setCircleStrokeWidthTransition, "nativeSetCircleStrokeWidthTransition"), METHOD(&CircleLayer::getCircleStrokeWidth, "nativeGetCircleStrokeWidth"), diff --git a/platform/android/src/style/layers/circle_layer.hpp b/platform/android/src/style/layers/circle_layer.hpp index 81737e8996..9d323e92bb 100644 --- a/platform/android/src/style/layers/circle_layer.hpp +++ b/platform/android/src/style/layers/circle_layer.hpp @@ -53,6 +53,8 @@ public: jni::Object<jni::ObjectTag> getCirclePitchScale(jni::JNIEnv&); + jni::Object<jni::ObjectTag> getCirclePitchAlignment(jni::JNIEnv&); + jni::Object<jni::ObjectTag> getCircleStrokeWidth(jni::JNIEnv&); void setCircleStrokeWidthTransition(jni::JNIEnv&, jlong duration, jlong delay); jni::Object<TransitionOptions> getCircleStrokeWidthTransition(jni::JNIEnv&); diff --git a/platform/android/src/style/layers/custom_layer.cpp b/platform/android/src/style/layers/custom_layer.cpp index 9bdc308d85..51a48520bf 100644 --- a/platform/android/src/style/layers/custom_layer.cpp +++ b/platform/android/src/style/layers/custom_layer.cpp @@ -7,11 +7,12 @@ namespace mbgl { namespace android { - CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, jni::jlong deinitializeFunction, jni::jlong context) + CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, jni::jlong contextLostFunction, jni::jlong deinitializeFunction, jni::jlong context) : Layer(env, std::make_unique<mbgl::style::CustomLayer>( jni::Make<std::string>(env, layerId), reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(initializeFunction), reinterpret_cast<mbgl::style::CustomLayerRenderFunction>(renderFunction), + reinterpret_cast<mbgl::style::CustomLayerContextLostFunction>(contextLostFunction), reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(deinitializeFunction), reinterpret_cast<void*>(context)) ) { @@ -21,6 +22,10 @@ namespace android { : Layer(map, coreLayer) { } + CustomLayer::CustomLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::CustomLayer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + CustomLayer::~CustomLayer() = default; void CustomLayer::update(jni::JNIEnv&) { @@ -48,7 +53,7 @@ namespace android { // Register the peer jni::RegisterNativePeer<CustomLayer>( env, CustomLayer::javaClass, "nativePtr", - std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong>, + std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong>, "initialize", "finalize", METHOD(&CustomLayer::update, "nativeUpdate")); diff --git a/platform/android/src/style/layers/custom_layer.hpp b/platform/android/src/style/layers/custom_layer.hpp index 1173d21bfd..9e079c1288 100644 --- a/platform/android/src/style/layers/custom_layer.hpp +++ b/platform/android/src/style/layers/custom_layer.hpp @@ -16,10 +16,12 @@ public: static void registerNative(jni::JNIEnv&); - CustomLayer(jni::JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong); + CustomLayer(jni::JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong); CustomLayer(mbgl::Map&, mbgl::style::CustomLayer&); + CustomLayer(mbgl::Map&, std::unique_ptr<mbgl::style::CustomLayer>); + ~CustomLayer(); void update(jni::JNIEnv&); diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index d571c3fd2e..02a1f0be82 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -3,6 +3,7 @@ #include <jni/jni.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/style/transition_options.hpp> #include <mbgl/util/logging.hpp> @@ -53,7 +54,7 @@ namespace android { } // Add layer to map - _map.addLayer(releaseCoreLayer(), before); + _map.getStyle().addLayer(releaseCoreLayer(), before); // Save pointer to the map this->map = &_map; @@ -91,19 +92,31 @@ namespace android { Value value(env, jvalue); // Convert and set property - optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), value, mbgl::optional<std::string>()); + optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), value); if (error) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message); return; } } + struct SetFilterEvaluator { + style::Filter filter; + + void operator()(style::BackgroundLayer&) { Log::Warning(mbgl::Event::JNI, "BackgroundLayer doesn't support filters"); } + void operator()(style::CustomLayer&) { Log::Warning(mbgl::Event::JNI, "CustomLayer doesn't support filters"); } + void operator()(style::RasterLayer&) { Log::Warning(mbgl::Event::JNI, "RasterLayer doesn't support filters"); } + + template <class LayerType> + void operator()(LayerType& layer) { + layer.setFilter(filter); + } + }; + void Layer::setFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter) { using namespace mbgl::style; using namespace mbgl::style::conversion; Value wrapped(env, jfilter); - Filter filter; Error error; optional<Filter> converted = convert<Filter>(wrapped, error); @@ -111,62 +124,45 @@ namespace android { mbgl::Log::Error(mbgl::Event::JNI, "Error setting filter: " + error.message); return; } - filter = std::move(*converted); - - if (layer.is<FillLayer>()) { - layer.as<FillLayer>()->setFilter(filter); - } else if (layer.is<LineLayer>()) { - layer.as<LineLayer>()->setFilter(filter); - } else if (layer.is<SymbolLayer>()) { - layer.as<SymbolLayer>()->setFilter(filter); - } else if (layer.is<CircleLayer>()) { - layer.as<CircleLayer>()->setFilter(filter); - } else if (layer.is<FillExtrusionLayer>()){ - layer.as<FillExtrusionLayer>()->setFilter(filter); - } else { - mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support filters"); - } + + layer.accept(SetFilterEvaluator {std::move(*converted)}); } - void Layer::setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer) { - using namespace mbgl::style; + struct SetSourceLayerEvaluator { + std::string sourceLayer; - std::string layerId = jni::Make<std::string>(env, sourceLayer); - - if (layer.is<FillLayer>()) { - layer.as<FillLayer>()->setSourceLayer(layerId); - } else if (layer.is<LineLayer>()) { - layer.as<LineLayer>()->setSourceLayer(layerId); - } else if (layer.is<SymbolLayer>()) { - layer.as<SymbolLayer>()->setSourceLayer(layerId); - } else if (layer.is<CircleLayer>()) { - layer.as<CircleLayer>()->setSourceLayer(layerId); - } else if(layer.is<FillExtrusionLayer>()) { - layer.as<FillExtrusionLayer>()->setSourceLayer(layerId); - } else { - mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer"); + void operator()(style::BackgroundLayer&) { Log::Warning(mbgl::Event::JNI, "BackgroundLayer doesn't support source layer"); } + void operator()(style::CustomLayer&) { Log::Warning(mbgl::Event::JNI, "CustomLayer doesn't support source layer"); } + void operator()(style::RasterLayer&) { Log::Warning(mbgl::Event::JNI, "RasterLayer doesn't support source layer"); } + + template <class LayerType> + void operator()(LayerType& layer) { + layer.setSourceLayer(sourceLayer); } + }; + + void Layer::setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer) { + layer.accept(SetSourceLayerEvaluator {jni::Make<std::string>(env, sourceLayer)}); } - jni::String Layer::getSourceLayer(jni::JNIEnv& env) { - using namespace mbgl::style; + struct GetSourceLayerEvaluator { + std::string noop(std::string layerType) { + Log::Warning(mbgl::Event::JNI, "%s doesn't support source layer", layerType.c_str()); + return {}; + } - std::string sourceLayerId; - if (layer.is<FillLayer>()) { - sourceLayerId = layer.as<FillLayer>()->getSourceLayer(); - } else if (layer.is<LineLayer>()) { - sourceLayerId = layer.as<LineLayer>()->getSourceLayer(); - } else if (layer.is<SymbolLayer>()) { - sourceLayerId = layer.as<SymbolLayer>()->getSourceLayer(); - } else if (layer.is<CircleLayer>()) { - sourceLayerId = layer.as<CircleLayer>()->getSourceLayer(); - } else if (layer.is<FillExtrusionLayer>()) { - sourceLayerId = layer.as<FillExtrusionLayer>()->getSourceLayer(); - } else { - mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer"); + std::string operator()(style::BackgroundLayer&) { return noop("BackgroundLayer"); } + std::string operator()(style::CustomLayer&) { return noop("CustomLayer"); } + std::string operator()(style::RasterLayer&) { return noop("RasterLayer"); } + + template <class LayerType> + std::string operator()(LayerType& layer) { + return layer.getSourceLayer(); } + }; - return jni::Make<jni::String>(env, sourceLayerId); + jni::String Layer::getSourceLayer(jni::JNIEnv& env) { + return jni::Make<jni::String>(env, layer.accept(GetSourceLayerEvaluator())); } jni::jfloat Layer::getMinZoom(jni::JNIEnv&){ diff --git a/platform/android/src/style/layers/layers.cpp b/platform/android/src/style/layers/layers.cpp index 5c49f875ee..9803b6f841 100644 --- a/platform/android/src/style/layers/layers.cpp +++ b/platform/android/src/style/layers/layers.cpp @@ -24,53 +24,51 @@ namespace mbgl { namespace android { -static Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) { - if (coreLayer.is<mbgl::style::BackgroundLayer>()) { - return new BackgroundLayer(map, *coreLayer.as<mbgl::style::BackgroundLayer>()); - } else if (coreLayer.is<mbgl::style::CircleLayer>()) { - return new CircleLayer(map, *coreLayer.as<mbgl::style::CircleLayer>()); - } else if (coreLayer.is<mbgl::style::FillExtrusionLayer>()) { - return new FillExtrusionLayer(map, *coreLayer.as<mbgl::style::FillExtrusionLayer>()); - } else if (coreLayer.is<mbgl::style::FillLayer>()) { - return new FillLayer(map, *coreLayer.as<mbgl::style::FillLayer>()); - } else if (coreLayer.is<mbgl::style::LineLayer>()) { - return new LineLayer(map, *coreLayer.as<mbgl::style::LineLayer>()); - } else if (coreLayer.is<mbgl::style::RasterLayer>()) { - return new RasterLayer(map, *coreLayer.as<mbgl::style::RasterLayer>()); - } else if (coreLayer.is<mbgl::style::SymbolLayer>()) { - return new SymbolLayer(map, *coreLayer.as<mbgl::style::SymbolLayer>()); - } else if (coreLayer.is<mbgl::style::CustomLayer>()) { - return new CustomLayer(map, *coreLayer.as<mbgl::style::CustomLayer>()); - } else { - return new UnknownLayer(map, coreLayer); +// Mapping from style layers to peer classes +template <class> struct PeerType {}; +template <> struct PeerType<style::BackgroundLayer> { using Type = android::BackgroundLayer; }; +template <> struct PeerType<style::CircleLayer> { using Type = android::CircleLayer; }; +template <> struct PeerType<style::FillExtrusionLayer> { using Type = android::FillExtrusionLayer; }; +template <> struct PeerType<style::FillLayer> { using Type = android::FillLayer; }; +template <> struct PeerType<style::LineLayer> { using Type = android::LineLayer; }; +template <> struct PeerType<style::RasterLayer> { using Type = android::RasterLayer; }; +template <> struct PeerType<style::SymbolLayer> { using Type = android::SymbolLayer; }; +template <> struct PeerType<style::CustomLayer> { using Type = android::CustomLayer; }; + +// Inititalizes a non-owning peer +struct LayerPeerIntitializer { + mbgl::Map& map; + + template <class LayerType> + Layer* operator()(LayerType& layer) { + return new typename PeerType<LayerType>::Type(map, layer); } -} +}; -template <class LayerType, class PeerType> -static PeerType* createPeer(Map& map, std::unique_ptr<mbgl::style::Layer> layer) { - return new PeerType(map, std::move(std::unique_ptr<LayerType>(layer.release()->as<LayerType>()))); +static Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) { + Layer* layer = coreLayer.accept(LayerPeerIntitializer {map}); + return layer ? layer : new UnknownLayer(map, coreLayer); } -static Layer* initializeLayerPeer(Map& map, std::unique_ptr<mbgl::style::Layer> coreLayer) { - if (coreLayer->is<style::BackgroundLayer>()) { - return createPeer<style::BackgroundLayer, BackgroundLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::CircleLayer>()) { - return createPeer<style::CircleLayer, CircleLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::FillExtrusionLayer>()) { - return createPeer<style::FillExtrusionLayer, FillExtrusionLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::FillLayer>()) { - return createPeer<style::FillLayer, FillLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::LineLayer>()) { - return createPeer<style::LineLayer, LineLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::RasterLayer>()) { - return createPeer<style::RasterLayer, RasterLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<style::SymbolLayer>()) { - return createPeer<style::SymbolLayer, SymbolLayer>(map, std::move(coreLayer)); - } else if (coreLayer->is<mbgl::style::CustomLayer>()) { - return createPeer<style::SymbolLayer, SymbolLayer>(map, std::move(coreLayer)); - } else { - return new UnknownLayer(map, std::move(coreLayer)); +// Initializes an owning peer +// Only usable once since it needs to pass on ownership +// of the given layer and thus enforced to be an rvalue +struct UniqueLayerPeerIntitializer { + mbgl::Map& map; + std::unique_ptr<style::Layer> layer; + + template <class LayerType> + Layer* operator()(LayerType&) && { + return new typename PeerType<LayerType>::Type( + map, + std::unique_ptr<LayerType>(layer.release()->as<LayerType>()) + ); } +}; + +static Layer* initializeLayerPeer(Map& map, std::unique_ptr<mbgl::style::Layer> coreLayer) { + Layer* layer = coreLayer->accept(UniqueLayerPeerIntitializer {map, std::move(coreLayer)}); + return layer ? layer : new UnknownLayer(map, std::move(coreLayer)); } jni::jobject* createJavaLayerPeer(jni::JNIEnv& env, Map& map, style::Layer& coreLayer) { diff --git a/platform/android/src/style/layers/symbol_layer.cpp b/platform/android/src/style/layers/symbol_layer.cpp index 3a560a5deb..d44744a6cf 100644 --- a/platform/android/src/style/layers/symbol_layer.cpp +++ b/platform/android/src/style/layers/symbol_layer.cpp @@ -125,6 +125,18 @@ namespace android { return jni::Object<jni::ObjectTag>(*converted); } + jni::Object<jni::ObjectTag> SymbolLayer::getIconAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconAnchor()); + return jni::Object<jni::ObjectTag>(*converted); + } + + jni::Object<jni::ObjectTag> SymbolLayer::getIconPitchAlignment(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconPitchAlignment()); + return jni::Object<jni::ObjectTag>(*converted); + } + jni::Object<jni::ObjectTag> SymbolLayer::getTextPitchAlignment(jni::JNIEnv& env) { using namespace mbgl::android::conversion; Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getTextPitchAlignment()); @@ -514,6 +526,8 @@ namespace android { METHOD(&SymbolLayer::getIconPadding, "nativeGetIconPadding"), METHOD(&SymbolLayer::getIconKeepUpright, "nativeGetIconKeepUpright"), METHOD(&SymbolLayer::getIconOffset, "nativeGetIconOffset"), + METHOD(&SymbolLayer::getIconAnchor, "nativeGetIconAnchor"), + METHOD(&SymbolLayer::getIconPitchAlignment, "nativeGetIconPitchAlignment"), METHOD(&SymbolLayer::getTextPitchAlignment, "nativeGetTextPitchAlignment"), METHOD(&SymbolLayer::getTextRotationAlignment, "nativeGetTextRotationAlignment"), METHOD(&SymbolLayer::getTextField, "nativeGetTextField"), diff --git a/platform/android/src/style/layers/symbol_layer.hpp b/platform/android/src/style/layers/symbol_layer.hpp index 8366051c6e..417e5e143f 100644 --- a/platform/android/src/style/layers/symbol_layer.hpp +++ b/platform/android/src/style/layers/symbol_layer.hpp @@ -59,6 +59,10 @@ public: jni::Object<jni::ObjectTag> getIconOffset(jni::JNIEnv&); + jni::Object<jni::ObjectTag> getIconAnchor(jni::JNIEnv&); + + jni::Object<jni::ObjectTag> getIconPitchAlignment(jni::JNIEnv&); + jni::Object<jni::ObjectTag> getTextPitchAlignment(jni::JNIEnv&); jni::Object<jni::ObjectTag> getTextRotationAlignment(jni::JNIEnv&); diff --git a/platform/android/src/style/sources/geojson_source.cpp b/platform/android/src/style/sources/geojson_source.cpp index 780cc4b6f6..90ef851eba 100644 --- a/platform/android/src/style/sources/geojson_source.cpp +++ b/platform/android/src/style/sources/geojson_source.cpp @@ -1,5 +1,7 @@ #include "geojson_source.hpp" +#include <mbgl/renderer/query.hpp> + // Java -> C++ conversion #include "../android_conversion.hpp" #include "../conversion/filter.hpp" @@ -41,8 +43,8 @@ namespace android { ) { } - GeoJSONSource::GeoJSONSource(mbgl::Map& map, mbgl::style::GeoJSONSource& coreSource) - : Source(map, coreSource) { + GeoJSONSource::GeoJSONSource(mbgl::style::GeoJSONSource& coreSource) + : Source(coreSource) { } GeoJSONSource::~GeoJSONSource() = default; @@ -108,8 +110,8 @@ namespace android { using namespace mbgl::android::geojson; std::vector<mbgl::Feature> features; - if (map) { - features = map->querySourceFeatures(source.getID(), { {}, toFilter(env, jfilter) }); + if (rendererFrontend) { + features = rendererFrontend->querySourceFeatures(source.getID(), { {}, toFilter(env, jfilter) }); } return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, features); } diff --git a/platform/android/src/style/sources/geojson_source.hpp b/platform/android/src/style/sources/geojson_source.hpp index 938a20612c..52dd632bfa 100644 --- a/platform/android/src/style/sources/geojson_source.hpp +++ b/platform/android/src/style/sources/geojson_source.hpp @@ -21,7 +21,7 @@ public: GeoJSONSource(jni::JNIEnv&, jni::String, jni::Object<>); - GeoJSONSource(mbgl::Map&, mbgl::style::GeoJSONSource&); + GeoJSONSource(mbgl::style::GeoJSONSource&); ~GeoJSONSource(); diff --git a/platform/android/src/style/sources/image_source.cpp b/platform/android/src/style/sources/image_source.cpp new file mode 100644 index 0000000000..d46b367c53 --- /dev/null +++ b/platform/android/src/style/sources/image_source.cpp @@ -0,0 +1,72 @@ +#include "image_source.hpp" + +// Java -> C++ conversion +#include "../android_conversion.hpp" + +// C++ -> Java conversion +#include "../../conversion/conversion.hpp" +#include <mbgl/style/conversion.hpp> +#include <mbgl/util/premultiply.hpp> + +#include "../../bitmap.hpp" +#include <string> +#include <array> + +namespace mbgl { +namespace android { + + ImageSource::ImageSource(jni::JNIEnv& env, jni::String sourceId, jni::Object<LatLngQuad> coordinatesObject) + : Source(env, std::make_unique<mbgl::style::ImageSource>( + jni::Make<std::string>(env, sourceId), + LatLngQuad::getLatLngArray(env, coordinatesObject) + ) + ) { + } + + ImageSource::ImageSource(mbgl::style::ImageSource& coreSource) + : Source(coreSource) { + } + + ImageSource::~ImageSource() = default; + + void ImageSource::setURL(jni::JNIEnv& env, jni::String url) { + // Update the core source + source.as<mbgl::style::ImageSource>()->ImageSource::setURL(jni::Make<std::string>(env, url)); + } + + jni::String ImageSource::getURL(jni::JNIEnv& env) { + optional<std::string> url = source.as<mbgl::style::ImageSource>()->ImageSource::getURL(); + return url ? jni::Make<jni::String>(env, *url) : jni::String(); + } + + void ImageSource::setImage(jni::JNIEnv& env, jni::Object<Bitmap> bitmap) { + source.as<mbgl::style::ImageSource>()->setImage(Bitmap::GetImage(env, bitmap)); + } + + jni::Class<ImageSource> ImageSource::javaClass; + + jni::jobject* ImageSource::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = ImageSource::javaClass.template GetConstructor<jni::jlong>(env); + return ImageSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)); + } + + void ImageSource::registerNative(jni::JNIEnv& env) { + // Lookup the class + ImageSource::javaClass = *jni::Class<ImageSource>::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<ImageSource>( + env, ImageSource::javaClass, "nativePtr", + std::make_unique<ImageSource, JNIEnv&, jni::String, jni::Object<LatLngQuad>>, + "initialize", + "finalize", + METHOD(&ImageSource::setURL, "nativeSetUrl"), + METHOD(&ImageSource::getURL, "nativeGetUrl"), + METHOD(&ImageSource::setImage, "nativeSetImage") + ); + } + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/image_source.hpp b/platform/android/src/style/sources/image_source.hpp new file mode 100644 index 0000000000..9787a7294f --- /dev/null +++ b/platform/android/src/style/sources/image_source.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "source.hpp" +#include "../../geometry/lat_lng_quad.hpp" +#include <mbgl/style/sources/image_source.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class Bitmap; + +class ImageSource : public Source { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/ImageSource"; }; + + static jni::Class<ImageSource> javaClass; + + static void registerNative(jni::JNIEnv&); + + ImageSource(jni::JNIEnv&, jni::String, jni::Object<LatLngQuad>); + + ImageSource(mbgl::style::ImageSource&); + + ~ImageSource(); + + void setURL(jni::JNIEnv&, jni::String); + jni::String getURL(jni::JNIEnv&); + + void setImage(jni::JNIEnv&, jni::Object<Bitmap>); + + jni::jobject* createJavaPeer(jni::JNIEnv&); + +}; // class ImageSource + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/raster_source.cpp b/platform/android/src/style/sources/raster_source.cpp index 32fdb163b0..d45342a1ad 100644 --- a/platform/android/src/style/sources/raster_source.cpp +++ b/platform/android/src/style/sources/raster_source.cpp @@ -22,8 +22,8 @@ namespace android { ) { } - RasterSource::RasterSource(mbgl::Map& map, mbgl::style::RasterSource& coreSource) - : Source(map, coreSource) { + RasterSource::RasterSource(mbgl::style::RasterSource& coreSource) + : Source(coreSource) { } RasterSource::~RasterSource() = default; diff --git a/platform/android/src/style/sources/raster_source.hpp b/platform/android/src/style/sources/raster_source.hpp index a79ccc10a4..84c49d7381 100644 --- a/platform/android/src/style/sources/raster_source.hpp +++ b/platform/android/src/style/sources/raster_source.hpp @@ -18,7 +18,7 @@ public: RasterSource(jni::JNIEnv&, jni::String, jni::Object<>, jni::jint); - RasterSource(mbgl::Map&, mbgl::style::RasterSource&); + RasterSource(mbgl::style::RasterSource&); ~RasterSource(); diff --git a/platform/android/src/style/sources/source.cpp b/platform/android/src/style/sources/source.cpp index e0e9bb9870..447b13019d 100644 --- a/platform/android/src/style/sources/source.cpp +++ b/platform/android/src/style/sources/source.cpp @@ -3,6 +3,7 @@ #include <jni/jni.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/util/logging.hpp> // Java -> C++ conversion @@ -25,11 +26,11 @@ namespace android { , source(*ownedSource) { } - Source::Source(mbgl::Map& coreMap, mbgl::style::Source& coreSource) : source(coreSource) , map(&coreMap) { + Source::Source(mbgl::style::Source& coreSource) + : source(coreSource) { } - Source::~Source() { - } + Source::~Source() = default; style::Source& Source::get() { return source; @@ -55,10 +56,11 @@ namespace android { } // Add source to map - _map.addSource(releaseCoreSource()); + _map.getStyle().addSource(releaseCoreSource()); + } - // Save pointer to the map - this->map = &_map; + void Source::setRendererFrontend(AndroidRendererFrontend& frontend_) { + rendererFrontend = &frontend_; } std::unique_ptr<mbgl::style::Source> Source::releaseCoreSource() { diff --git a/platform/android/src/style/sources/source.hpp b/platform/android/src/style/sources/source.hpp index 49fc50d754..383017b66f 100644 --- a/platform/android/src/style/sources/source.hpp +++ b/platform/android/src/style/sources/source.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/source.hpp> #include "../value.hpp" +#include "../../android_renderer_frontend.hpp" #include <jni/jni.hpp> @@ -23,7 +24,7 @@ public: /* * Called when a Java object is created on the c++ side */ - Source(mbgl::Map&, mbgl::style::Source&); + Source(mbgl::style::Source&); /* * Called when a Java object was created from the jvm side @@ -41,6 +42,8 @@ public: void addToMap(mbgl::Map&); + void setRendererFrontend(AndroidRendererFrontend&); + virtual jni::jobject* createJavaPeer(jni::JNIEnv&) = 0; jni::String getId(jni::JNIEnv&); @@ -57,8 +60,9 @@ protected: // Raw pointer that is valid until the source is removed from the map mbgl::style::Source& source; - // Map pointer is valid for newly created sources only after adding to the map - mbgl::Map* map; + // RendererFrontend pointer is valid only when + // added to the map + AndroidRendererFrontend* rendererFrontend; }; } // namespace android diff --git a/platform/android/src/style/sources/sources.cpp b/platform/android/src/style/sources/sources.cpp index b4e70202b4..9ab3ca8e84 100644 --- a/platform/android/src/style/sources/sources.cpp +++ b/platform/android/src/style/sources/sources.cpp @@ -2,35 +2,46 @@ #include <mbgl/style/source.hpp> #include <mbgl/style/sources/geojson_source.hpp> +#include <mbgl/style/sources/image_source.hpp> #include <mbgl/style/sources/raster_source.hpp> #include <mbgl/style/sources/vector_source.hpp> #include "source.hpp" #include "geojson_source.hpp" +#include "image_source.hpp" #include "raster_source.hpp" #include "unknown_source.hpp" #include "vector_source.hpp" +namespace { + + using namespace mbgl::android; + + Source* initializeSourcePeer(mbgl::style::Source& coreSource) { + Source* source; + if (coreSource.is<mbgl::style::VectorSource>()) { + source = new VectorSource(*coreSource.as<mbgl::style::VectorSource>()); + } else if (coreSource.is<mbgl::style::RasterSource>()) { + source = new RasterSource(*coreSource.as<mbgl::style::RasterSource>()); + } else if (coreSource.is<mbgl::style::GeoJSONSource>()) { + source = new GeoJSONSource(*coreSource.as<mbgl::style::GeoJSONSource>()); + } else if (coreSource.is<mbgl::style::ImageSource>()) { + source = new ImageSource(*coreSource.as<mbgl::style::ImageSource>()); + } else { + source = new UnknownSource(coreSource); + } + + return source; + } +} // namespace + namespace mbgl { namespace android { -Source* initializeSourcePeer(mbgl::Map& map, mbgl::style::Source& coreSource) { - Source* source; - if (coreSource.is<mbgl::style::VectorSource>()) { - source = new VectorSource(map, *coreSource.as<mbgl::style::VectorSource>()); - } else if (coreSource.is<mbgl::style::RasterSource>()) { - source = new RasterSource(map, *coreSource.as<mbgl::style::RasterSource>()); - } else if (coreSource.is<mbgl::style::GeoJSONSource>()) { - source = new GeoJSONSource(map, *coreSource.as<mbgl::style::GeoJSONSource>()); - } else { - source = new UnknownSource(map, coreSource); - } - - return source; -} -jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style::Source& coreSource) { - std::unique_ptr<Source> peerSource = std::unique_ptr<Source>(initializeSourcePeer(map, coreSource)); +jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, AndroidRendererFrontend& frontend, mbgl::style::Source& coreSource) { + std::unique_ptr<Source> peerSource = std::unique_ptr<Source>(initializeSourcePeer(coreSource)); + peerSource->setRendererFrontend(frontend); jni::jobject* result = peerSource->createJavaPeer(env); peerSource.release(); return result; @@ -39,6 +50,7 @@ jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style void registerNativeSources(jni::JNIEnv& env) { Source::registerNative(env); GeoJSONSource::registerNative(env); + ImageSource::registerNative(env); RasterSource::registerNative(env); UnknownSource::registerNative(env); VectorSource::registerNative(env); diff --git a/platform/android/src/style/sources/sources.hpp b/platform/android/src/style/sources/sources.hpp index 09a8b35067..c7b36818b2 100644 --- a/platform/android/src/style/sources/sources.hpp +++ b/platform/android/src/style/sources/sources.hpp @@ -1,20 +1,18 @@ #pragma once -#include <mbgl/map/map.hpp> #include <mbgl/style/source.hpp> #include "source.hpp" +#include "../../android_renderer_frontend.hpp" #include <jni/jni.hpp> namespace mbgl { namespace android { - mbgl::android::Source* initializeSourcePeer(mbgl::Map&, mbgl::style::Source&); - - jni::jobject* createJavaSourcePeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Source&); + jni::jobject* createJavaSourcePeer(jni::JNIEnv&, AndroidRendererFrontend&, mbgl::style::Source&); void registerNativeSources(jni::JNIEnv&); -} -} +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/unknown_source.cpp b/platform/android/src/style/sources/unknown_source.cpp index 86736597c3..79f27bdfbf 100644 --- a/platform/android/src/style/sources/unknown_source.cpp +++ b/platform/android/src/style/sources/unknown_source.cpp @@ -12,8 +12,8 @@ namespace { namespace mbgl { namespace android { - UnknownSource::UnknownSource(mbgl::Map& map, mbgl::style::Source& coreSource) - : Source(map, coreSource) { + UnknownSource::UnknownSource(mbgl::style::Source& coreSource) + : Source(coreSource) { } jni::Class<UnknownSource> UnknownSource::javaClass; diff --git a/platform/android/src/style/sources/unknown_source.hpp b/platform/android/src/style/sources/unknown_source.hpp index 3c37239792..4a003c9a7f 100644 --- a/platform/android/src/style/sources/unknown_source.hpp +++ b/platform/android/src/style/sources/unknown_source.hpp @@ -16,7 +16,7 @@ public: static void registerNative(jni::JNIEnv&); - UnknownSource(mbgl::Map&, mbgl::style::Source&); + UnknownSource(mbgl::style::Source&); ~UnknownSource() = default; diff --git a/platform/android/src/style/sources/vector_source.cpp b/platform/android/src/style/sources/vector_source.cpp index e2d9f60dec..7fe45441bd 100644 --- a/platform/android/src/style/sources/vector_source.cpp +++ b/platform/android/src/style/sources/vector_source.cpp @@ -1,5 +1,7 @@ #include "vector_source.hpp" +#include <mbgl/renderer/query.hpp> + // Java -> C++ conversion #include "../android_conversion.hpp" #include "../conversion/filter.hpp" @@ -28,8 +30,8 @@ namespace android { ) { } - VectorSource::VectorSource(mbgl::Map& map, mbgl::style::VectorSource& coreSource) - : Source(map, coreSource) { + VectorSource::VectorSource(mbgl::style::VectorSource& coreSource) + : Source(coreSource) { } VectorSource::~VectorSource() = default; @@ -46,8 +48,8 @@ namespace android { using namespace mbgl::android::geojson; std::vector<mbgl::Feature> features; - if (map) { - features = map->querySourceFeatures(source.getID(), { toVector(env, jSourceLayerIds), toFilter(env, jfilter) }); + if (rendererFrontend) { + features = rendererFrontend->querySourceFeatures(source.getID(), { toVector(env, jSourceLayerIds), toFilter(env, jfilter) }); } return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, features); } diff --git a/platform/android/src/style/sources/vector_source.hpp b/platform/android/src/style/sources/vector_source.hpp index 643b468338..509fe068d1 100644 --- a/platform/android/src/style/sources/vector_source.hpp +++ b/platform/android/src/style/sources/vector_source.hpp @@ -19,7 +19,7 @@ public: VectorSource(jni::JNIEnv&, jni::String, jni::Object<>); - VectorSource(mbgl::Map&, mbgl::style::VectorSource&); + VectorSource(mbgl::style::VectorSource&); ~VectorSource(); diff --git a/platform/android/tests/docs/UI_TESTS.md b/platform/android/tests/docs/UI_TESTS.md index 6d8541a406..1014b56845 100644 --- a/platform/android/tests/docs/UI_TESTS.md +++ b/platform/android/tests/docs/UI_TESTS.md @@ -61,7 +61,7 @@ You can generate JaCoCo reports from espresso tests by ## Running Espresso test automatically on AWS Device Farm To run tests on AWS device farm you need to execute `./gradlew -Pmapbox.abis=none devicefarmUpload`. You can configure the different steps in the testapp `build.gradle`. -AWS credentials are found in bitrise. +AWS credentials are found in CircleCI. diff --git a/platform/android/tests/docs/UNIT_TESTS.md b/platform/android/tests/docs/UNIT_TESTS.md index fefb435684..458e8869f3 100644 --- a/platform/android/tests/docs/UNIT_TESTS.md +++ b/platform/android/tests/docs/UNIT_TESTS.md @@ -77,7 +77,7 @@ The Unit tests are executed as part of the build process on our CI and are automatically run for each new commit pushed to this repo. If a Unit tests fails, this will fail and stop the build. -You can find this gradle command in our [buildscript](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/bitrise.yml#L48): +You can find this gradle command in our [buildscript](https://github.com/mapbox/mapbox-gl-native/blob/master/circle.yml#L146-L215): ``` $ ./gradlew -Pmapbox.abis=none testReleaseUnitTest |