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 d2371fcc2..09acda792 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 @@ -10,6 +10,7 @@ import android.support.annotation.NonNull; import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.constants.MapboxConstants; +import com.mapbox.mapboxsdk.storage.Resource; import java.io.File; @@ -81,6 +82,22 @@ public class OfflineManager { void onError(String error); } + /** + * This callback allows implementors to transform URLs before they are requested + * from the internet. This can be used add or remove custom parameters, or reroute + * certain requests to other servers or endpoints. + */ + public interface ResourceTransformCallback { + /** + * Called whenever a URL needs to be transformed. + * + * @param kind The kind of URL to be transformed. + * @param offlineRegions The original URL to be transformed. + * @return A URL that will now be downloaded. + */ + String onURL(@Resource.Kind int kind, String url); + } + /* * Constructors */ @@ -265,6 +282,18 @@ public class OfflineManager { }); } + /** + * Sets a callback for transforming URLs requested from the internet + *
+ * The callback will be executed on the main thread once for every requested URL. + *
+ * + * @param callback the callback to be invoked + */ + public void setResourceTransform(@NonNull final ResourceTransformCallback callback) { + setResourceTransform(mDefaultFileSourcePtr, callback); + } + /* * Changing or bypassing this limit without permission from Mapbox is prohibited * by the Mapbox Terms of Service. @@ -291,6 +320,9 @@ public class OfflineManager { long defaultFileSourcePtr, OfflineRegionDefinition definition, byte[] metadata, CreateOfflineRegionCallback callback); + private native void setResourceTransform( + long defaultFileSourcePtr, ResourceTransformCallback callback); + private native void setOfflineMapboxTileCountLimit( long defaultFileSourcePtr, long limit); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/Resource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/Resource.java new file mode 100644 index 000000000..af98a46a9 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/Resource.java @@ -0,0 +1,54 @@ +package com.mapbox.mapboxsdk.storage; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +public final class Resource { + // Note: Keep this in sync with include/mbgl/storage/resource.hpp + + @IntDef( {UNKNOWN, STYLE, SOURCE, TILE, GLYPHS, SPRITE_IMAGE, SPRITE_JSON}) + @Retention(RetentionPolicy.SOURCE) + public @interface Kind { + } + + /** + * Unknown type + */ + public static final int UNKNOWN = 0; + + /** + * Style sheet JSON file + */ + public static final int STYLE = 1; + + /** + * TileJSON file as specified in https://www.mapbox.com/mapbox-gl-js/style-spec/#root-sources + */ + public static final int SOURCE = 2; + + /** + * A vector or raster tile as described in the style sheet at + * https://www.mapbox.com/mapbox-gl-js/style-spec/#sources + */ + public static final int TILE = 3; + + /** + * Signed distance field glyphs for text rendering. These are the URLs specified in the style + * in https://www.mapbox.com/mapbox-gl-js/style-spec/#root-glyphs + */ + public static final int GLYPHS = 4; + + /** + * Image part of a sprite sheet. It is constructed of the prefix in + * https://www.mapbox.com/mapbox-gl-js/style-spec/#root-sprite and a PNG file extension. + */ + public static final int SPRITE_IMAGE = 5; + + /** + * JSON part of a sprite sheet. It is constructed of the prefix in + * https://www.mapbox.com/mapbox-gl-js/style-spec/#root-sprite and a JSON file extension. + */ + public static final int SPRITE_JSON = 6; +} diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 6864356fd..a0ec21eb9 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -15,6 +15,7 @@ #include "bitmap_factory.hpp" #include "connectivity_listener.hpp" #include "default_file_source.hpp" +#include "attach_env.hpp" #include "style/functions/categorical_stops.hpp" #include "style/functions/exponential_stops.hpp" #include "style/functions/identity_stops.hpp" @@ -145,6 +146,8 @@ jni::jfieldID* offlineRegionDefinitionPixelRatioId = nullptr; jni::jmethodID* createOnCreateMethodId = nullptr; jni::jmethodID* createOnErrorMethodId = nullptr; +jni::jmethodID* transformOnURLMethodId = nullptr; + jni::jmethodID* updateMetadataOnUpdateMethodId = nullptr; jni::jmethodID* updateMetadataOnErrorMethodId = nullptr; @@ -1448,6 +1451,45 @@ void createOfflineRegion(JNIEnv *env, jni::jobject* obj, jlong defaultFileSource }); } +// A deleter that doesn't retain an JNIEnv handle but instead tries to attach the JVM. This means +// it can be used on any thread to delete a global ref. +struct GenericGlobalRefDeleter { + void operator()(jni::jobject* p) const { + if (p) { + auto env = AttachEnv(); + env->DeleteGlobalRef(jni::Unwrap(p)); + } + } +}; + +void setResourceTransform(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourcePtr, jni::jobject* transformCallback) { + // Checks + assert(defaultFileSourcePtr != 0); + + mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast