diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/CustomLayer.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/CustomLayer.java
new file mode 100644
index 000000000..f6fb297dc
--- /dev/null
+++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/CustomLayer.java
@@ -0,0 +1,21 @@
+package com.mapbox.mapboxsdk.layers;
+
+public class CustomLayer {
+ public CustomLayer(String id,
+ long context,
+ long initializeFunction,
+ long renderFunction,
+ long deinitializeFunction) {
+ this.mID = id;
+ this.mContext = context;
+ this.mInitializeFunction = initializeFunction;
+ this.mRenderFunction = renderFunction;
+ this.mDeinitializeFunction = deinitializeFunction;
+ }
+
+ public String mID;
+ public long mContext;
+ public long mInitializeFunction;
+ public long mRenderFunction;
+ public long mDeinitializeFunction;
+}
diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/package-info.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/package-info.java
new file mode 100644
index 000000000..fda1a493e
--- /dev/null
+++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/layers/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * This package contains classes to add and manage layers in your map.
+ */
+package com.mapbox.mapboxsdk.layers;
diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java
index 94cfe23f8..157f1ae95 100644
--- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java
+++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java
@@ -74,6 +74,7 @@ import com.mapbox.mapboxsdk.geometry.BoundingBox;
import com.mapbox.mapboxsdk.geometry.CoordinateBounds;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngZoom;
+import com.mapbox.mapboxsdk.layers.CustomLayer;
import com.mapbox.mapboxsdk.utils.ApiAccess;
import java.lang.annotation.Retention;
@@ -3782,6 +3783,11 @@ public final class MapView extends FrameLayout {
mAttributionsView.setVisibility(visibility);
}
+ @UiThread
+ public void addCustomLayer(CustomLayer customLayer, String before) {
+ mNativeMapView.addCustomLayer(customLayer, before);
+ }
+
private void setWidgetGravity(@NonNull final View view, int gravity) {
LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
layoutParams.gravity = gravity;
diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java
index f09d0e902..c14267077 100644
--- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java
+++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/NativeMapView.java
@@ -11,6 +11,7 @@ import com.mapbox.mapboxsdk.geometry.BoundingBox;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngZoom;
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
+import com.mapbox.mapboxsdk.layers.CustomLayer;
import java.lang.ref.WeakReference;
import java.util.List;
@@ -449,6 +450,10 @@ final class NativeMapView {
return nativeGetTopOffsetPixelsForAnnotationSymbol(mNativeMapViewPtr, symbolName);
}
+ public void addCustomLayer(CustomLayer customLayer, String before) {
+ nativeAddCustomLayer(mNativeMapViewPtr, customLayer, before);
+ }
+
//
// Callbacks
//
@@ -629,4 +634,6 @@ final class NativeMapView {
private native LatLng nativeLatLngForPixel(long nativeMapViewPtr, PointF pixel);
private native double nativeGetTopOffsetPixelsForAnnotationSymbol(long nativeMapViewPtr, String symbolName);
+
+ private native void nativeAddCustomLayer(long nativeMapViewPtr, CustomLayer customLayer, String before);
}
diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ExampleCustomLayer.java b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ExampleCustomLayer.java
new file mode 100644
index 000000000..e6c799214
--- /dev/null
+++ b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/ExampleCustomLayer.java
@@ -0,0 +1,12 @@
+package com.mapbox.mapboxsdk.testapp;
+
+public class ExampleCustomLayer {
+ static {
+ System.loadLibrary("example-custom-layer");
+ }
+
+ public native static long createContext();
+ public static long InitializeFunction;
+ public static long RenderFunction;
+ public static long DeinitializeFunction;
+}
diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java
index c303d94cd..c6ec8cbd7 100644
--- a/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java
+++ b/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MainActivity.java
@@ -34,6 +34,7 @@ import com.mapbox.mapboxsdk.constants.MyBearingTracking;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.layers.CustomLayer;
import com.mapbox.mapboxsdk.testapp.utils.GeoParseUtil;
import com.mapbox.mapboxsdk.utils.ApiAccess;
import com.mapbox.mapboxsdk.views.MapView;
@@ -395,6 +396,10 @@ public class MainActivity extends AppCompatActivity {
startActivity(new Intent(getApplicationContext(), PolylineActivity.class));
return true;
+ case R.id.action_custom_layer:
+ addCustomLayer();
+ return true;
+
default:
return changeMapStyle(menuItem.getItemId());
}
@@ -570,6 +575,16 @@ public class MainActivity extends AppCompatActivity {
mMapView.removeAllAnnotations();
}
+ private void addCustomLayer() {
+ mMapView.addCustomLayer(
+ new CustomLayer("custom",
+ ExampleCustomLayer.createContext(),
+ ExampleCustomLayer.InitializeFunction,
+ ExampleCustomLayer.RenderFunction,
+ ExampleCustomLayer.DeinitializeFunction),
+ null);
+ }
+
// Called when FPS changes
private class MyOnFpsChangedListener implements MapView.OnFpsChangedListener {
diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml b/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml
index bfd70a936..9a8118283 100644
--- a/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml
+++ b/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_drawer.xml
@@ -138,6 +138,12 @@
android:icon="@drawable/ic_trending_up_24dp"
android:title="@string/activity_polyline" />
+
+
diff --git a/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index 8cc299e39..10d622d38 100644
--- a/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -16,6 +16,7 @@
User tracking mode
Polyline Activity
Animate Coordinate Change
+ Custom Layer
Main Activity Controls
Main Activity Styles
diff --git a/platform/android/example_custom_layer.cpp b/platform/android/example_custom_layer.cpp
new file mode 100644
index 000000000..e85354535
--- /dev/null
+++ b/platform/android/example_custom_layer.cpp
@@ -0,0 +1,104 @@
+#include
+#include
+
+static const GLchar * vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }";
+static const GLchar * fragmentShaderSource = "void main() { gl_FragColor = vec4(0, 1, 0, 1); }";
+
+class ExampleCustomLayer {
+public:
+ ~ExampleCustomLayer() {
+ if (program) {
+ glDeleteBuffers(1, &buffer);
+ glDetachShader(program, vertexShader);
+ glDetachShader(program, fragmentShader);
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+ glDeleteProgram(program);
+ }
+ }
+
+ void initialize() {
+ program = glCreateProgram();
+ vertexShader = glCreateShader(GL_VERTEX_SHADER);
+ fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
+ glCompileShader(vertexShader);
+ glAttachShader(program, vertexShader);
+ glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
+ glCompileShader(fragmentShader);
+ glAttachShader(program, fragmentShader);
+ glLinkProgram(program);
+ a_pos = glGetAttribLocation(program, "a_pos");
+
+ GLfloat background[] = { -1,-1, 1,-1, -1,1, 1,1 };
+ glGenBuffers(1, &buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+ glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW);
+ }
+
+ void render() {
+ glUseProgram(program);
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+ glEnableVertexAttribArray(a_pos);
+ glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL);
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_DEPTH_TEST);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ }
+
+ GLuint program = 0;
+ GLuint vertexShader = 0;
+ GLuint fragmentShader = 0;
+ GLuint buffer = 0;
+ GLuint a_pos = 0;
+};
+
+jlong JNICALL nativeCreateContext(JNIEnv*, jobject) {
+ return reinterpret_cast(new ExampleCustomLayer());
+}
+
+void nativeInitialize(void *context) {
+ reinterpret_cast(context)->initialize();
+}
+
+void nativeRender(void *context) {
+ reinterpret_cast(context)->render();
+}
+
+void nativeDenitialize(void *context) {
+ delete reinterpret_cast(context);
+}
+
+extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
+ JNIEnv *env = nullptr;
+ vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6);
+
+ jclass customLayerClass = env->FindClass("com/mapbox/mapboxsdk/testapp/ExampleCustomLayer");
+
+ JNINativeMethod methods[] = {
+ {"createContext", "()J", reinterpret_cast(&nativeCreateContext)}
+ };
+
+ if (env->RegisterNatives(customLayerClass, methods, 1) < 0) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ env->SetStaticLongField(customLayerClass,
+ env->GetStaticFieldID(customLayerClass, "InitializeFunction", "J"),
+ reinterpret_cast(nativeInitialize));
+
+ env->SetStaticLongField(customLayerClass,
+ env->GetStaticFieldID(customLayerClass, "RenderFunction", "J"),
+ reinterpret_cast(nativeRender));
+
+ env->SetStaticLongField(customLayerClass,
+ env->GetStaticFieldID(customLayerClass, "DeinitializeFunction", "J"),
+ reinterpret_cast(nativeDenitialize));
+
+ return JNI_VERSION_1_6;
+}
+
+extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *, void *) {
+}
diff --git a/platform/android/jni.cpp b/platform/android/jni.cpp
index acefc87f3..fc657a6cb 100644
--- a/platform/android/jni.cpp
+++ b/platform/android/jni.cpp
@@ -111,6 +111,13 @@ jclass httpRequestClass = nullptr;
jmethodID httpRequestStartId = nullptr;
jmethodID httpRequestCancelId = nullptr;
+jclass customLayerClass = nullptr;
+jfieldID customLayerIdId = nullptr;
+jfieldID customLayerContextId = nullptr;
+jfieldID customLayerInitializeFunctionId = nullptr;
+jfieldID customLayerRenderFunctionId = nullptr;
+jfieldID customLayerDeinitializeFunctionId = nullptr;
+
bool throw_jni_error(JNIEnv *env, const char *msg) {
if (env->ThrowNew(runtimeExceptionClass, msg) < 0) {
env->ExceptionDescribe();
@@ -1471,6 +1478,19 @@ jdouble JNICALL nativeGetTopOffsetPixelsForAnnotationSymbol(JNIEnv *env, jobject
return nativeMapView->getMap().getTopOffsetPixelsForAnnotationIcon(std_string_from_jstring(env, symbolName));
}
+void JNICALL nativeAddCustomLayer(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject customLayer, jstring before) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddCustomLayer");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr);
+ nativeMapView->getMap().addCustomLayer(
+ std_string_from_jstring(env, reinterpret_cast(env->GetObjectField(customLayer, customLayerIdId))),
+ reinterpret_cast(env->GetLongField(customLayer, customLayerInitializeFunctionId)),
+ reinterpret_cast(env->GetLongField(customLayer, customLayerRenderFunctionId)),
+ reinterpret_cast(env->GetLongField(customLayer, customLayerDeinitializeFunctionId)),
+ reinterpret_cast(env->GetLongField(customLayer, customLayerContextId)),
+ before ? std_string_from_jstring(env, before).c_str() : nullptr);
+}
+
}
@@ -1848,6 +1868,36 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
env->ExceptionDescribe();
}
+ customLayerClass = env->FindClass("com/mapbox/mapboxsdk/layers/CustomLayer");
+ if (customLayerClass == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ customLayerIdId = env->GetFieldID(customLayerClass, "mID", "Ljava/lang/String;");
+ if (customLayerIdId == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ customLayerContextId = env->GetFieldID(customLayerClass, "mContext", "J");
+ if (customLayerContextId == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ customLayerInitializeFunctionId = env->GetFieldID(customLayerClass, "mInitializeFunction", "J");
+ if (customLayerInitializeFunctionId == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ customLayerRenderFunctionId = env->GetFieldID(customLayerClass, "mRenderFunction", "J");
+ if (customLayerRenderFunctionId == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ customLayerDeinitializeFunctionId = env->GetFieldID(customLayerClass, "mDeinitializeFunction", "J");
+ if (customLayerDeinitializeFunctionId == nullptr) {
+ env->ExceptionDescribe();
+ }
+
const std::vector methods = {
{"nativeCreate", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;FIJ)J",
reinterpret_cast(&nativeCreate)},
@@ -1965,6 +2015,8 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
reinterpret_cast(&nativeLatLngForPixel)},
{"nativeGetTopOffsetPixelsForAnnotationSymbol", "(JLjava/lang/String;)D",
reinterpret_cast(&nativeGetTopOffsetPixelsForAnnotationSymbol)},
+ {"nativeAddCustomLayer", "(JLcom/mapbox/mapboxsdk/layers/CustomLayer;Ljava/lang/String;)V",
+ reinterpret_cast(&nativeAddCustomLayer)},
};
if (env->RegisterNatives(nativeMapViewClass, methods.data(), methods.size()) < 0) {
diff --git a/platform/android/mapboxgl-app.gypi b/platform/android/mapboxgl-app.gypi
index 8080b29cd..ced569a4b 100644
--- a/platform/android/mapboxgl-app.gypi
+++ b/platform/android/mapboxgl-app.gypi
@@ -59,11 +59,46 @@
],
},
+ { 'target_name': 'example-custom-layer-lib',
+ 'product_name': 'example-custom-layer',
+ 'type': 'shared_library',
+ 'hard_dependency': 1,
+
+ 'sources': [
+ './example_custom_layer.cpp',
+ ],
+
+ 'variables': {
+ 'ldflags': [
+ '-llog',
+ '-landroid',
+ '-lEGL',
+ '-lGLESv2',
+ '-lstdc++',
+ '-latomic',
+ ],
+ },
+
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [ '<@(ldflags)' ],
+ }
+ }, {
+ 'libraries': [ '<@(ldflags)' ],
+ }]
+ ],
+ },
{ 'target_name': 'androidapp',
'type': 'none',
'hard_dependency': 1,
+ 'dependencies': [
+ 'android-lib',
+ 'example-custom-layer-lib',
+ ],
+
'variables': {
'pwd': ' layer, mapbox::util::optional after) {
style->addLayer(std::move(layer), after);
+ updateFlags |= Update::Classes;
+ asyncUpdate.send();
}
void MapContext::setSourceTileCacheSize(size_t size) {