summaryrefslogtreecommitdiff
path: root/platform/android
diff options
context:
space:
mode:
authorLeith Bade <leith@mapbox.com>2015-10-25 01:07:16 +1100
committerLeith Bade <leith@mapbox.com>2015-10-25 01:07:16 +1100
commit39911e581c7bc82116c1c70608f246924de33b85 (patch)
treea66716809ec61f742f1d1fbec5297a68504adfd2 /platform/android
parentd2e01290ebc1f9b894b4b2d05e0e7c22d876e40b (diff)
downloadqtlocation-mapboxgl-39911e581c7bc82116c1c70608f246924de33b85.tar.gz
[android] Move android files
Fixes #2032
Diffstat (limited to 'platform/android')
-rw-r--r--platform/android/jni.cpp2199
-rw-r--r--platform/android/mapboxgl-app.gypi101
-rw-r--r--platform/android/native_map_view.cpp745
3 files changed, 3045 insertions, 0 deletions
diff --git a/platform/android/jni.cpp b/platform/android/jni.cpp
new file mode 100644
index 0000000000..d0d3ea2970
--- /dev/null
+++ b/platform/android/jni.cpp
@@ -0,0 +1,2199 @@
+#include <cstdint>
+#include <cinttypes>
+#include <cassert>
+
+#include <string>
+#include <locale>
+#include <codecvt>
+#include <array>
+#include <vector>
+
+#include <jni.h>
+
+#include <android/native_window_jni.h>
+#include <sys/system_properties.h>
+
+#include <mbgl/android/jni.hpp>
+#include <mbgl/android/native_map_view.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/map/camera.hpp>
+#include <mbgl/annotation/point_annotation.hpp>
+#include <mbgl/annotation/shape_annotation.hpp>
+#include <mbgl/annotation/sprite_image.hpp>
+#include <mbgl/platform/event.hpp>
+#include <mbgl/platform/log.hpp>
+#include <mbgl/storage/network_status.hpp>
+
+#pragma clang diagnostic ignored "-Wunused-parameter"
+
+namespace mbgl {
+namespace android {
+
+JavaVM* theJVM;
+
+std::string cachePath;
+std::string dataPath;
+std::string apkPath;
+std::string androidRelease;
+
+jmethodID onInvalidateId = nullptr;
+jmethodID onMapChangedId = nullptr;
+jmethodID onFpsChangedId = nullptr;
+
+jclass latLngClass = nullptr;
+jmethodID latLngConstructorId = nullptr;
+jfieldID latLngLatitudeId = nullptr;
+jfieldID latLngLongitudeId = nullptr;
+
+jclass latLngZoomClass = nullptr;
+jmethodID latLngZoomConstructorId = nullptr;
+jfieldID latLngZoomLatitudeId = nullptr;
+jfieldID latLngZoomLongitudeId = nullptr;
+jfieldID latLngZoomZoomId = nullptr;
+
+jclass bboxClass = nullptr;
+jmethodID bboxConstructorId = nullptr;
+jfieldID bboxLatNorthId = nullptr;
+jfieldID bboxLatSouthId = nullptr;
+jfieldID bboxLonEastId = nullptr;
+jfieldID bboxLonWestId = nullptr;
+
+jclass spriteClass = nullptr;
+jfieldID spriteIdId = nullptr;
+
+jclass markerClass = nullptr;
+jfieldID markerPositionId = nullptr;
+jfieldID markerIconId = nullptr;
+
+jclass polylineClass = nullptr;
+jfieldID polylineAlphaId = nullptr;
+jfieldID polylineColorId = nullptr;
+jfieldID polylineWidthId = nullptr;
+jfieldID polylinePointsId = nullptr;
+
+jclass polygonClass = nullptr;
+jfieldID polygonAlphaId = nullptr;
+jfieldID polygonFillColorId = nullptr;
+jfieldID polygonStrokeColorId = nullptr;
+jfieldID polygonPointsId = nullptr;
+
+jclass runtimeExceptionClass = nullptr;
+jclass nullPointerExceptionClass = nullptr;
+
+jmethodID listToArrayId = nullptr;
+
+jclass arrayListClass = nullptr;
+jmethodID arrayListConstructorId = nullptr;
+jmethodID arrayListAddId = nullptr;
+
+jclass projectedMetersClass = nullptr;
+jmethodID projectedMetersConstructorId = nullptr;
+jfieldID projectedMetersNorthingId = nullptr;
+jfieldID projectedMetersEastingId = nullptr;
+
+jclass pointFClass = nullptr;
+jmethodID pointFConstructorId = nullptr;
+jfieldID pointFXId = nullptr;
+jfieldID pointFYId = nullptr;
+
+jclass rectFClass = nullptr;
+jmethodID rectFConstructorId = nullptr;
+jfieldID rectFLeftId = nullptr;
+jfieldID rectFTopId = nullptr;
+jfieldID rectFRightId = nullptr;
+jfieldID rectFBottomId = nullptr;
+
+jclass httpContextClass = nullptr;
+jmethodID httpContextGetInstanceId = nullptr;
+jmethodID httpContextCreateRequestId = nullptr;
+
+jclass httpRequestClass = nullptr;
+jmethodID httpRequestStartId = nullptr;
+jmethodID httpRequestCancelId = nullptr;
+
+bool throw_jni_error(JNIEnv *env, const char *msg) {
+ if (env->ThrowNew(runtimeExceptionClass, msg) < 0) {
+ env->ExceptionDescribe();
+ return false;
+ }
+
+ return true;
+}
+
+bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName) {
+ assert(vm != nullptr);
+ assert(env != nullptr);
+
+ JavaVMAttachArgs args = {JNI_VERSION_1_2, threadName.c_str(), NULL};
+
+ jint ret;
+ *env = nullptr;
+ bool detach = false;
+ ret = vm->GetEnv(reinterpret_cast<void **>(env), JNI_VERSION_1_6);
+ if (ret != JNI_OK) {
+ if (ret != JNI_EDETACHED) {
+ mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
+ throw new std::runtime_error("GetEnv() failed");
+ } else {
+ ret = vm->AttachCurrentThread(env, &args);
+ if (ret != JNI_OK) {
+ mbgl::Log::Error(mbgl::Event::JNI, "AttachCurrentThread() failed with %i", ret);
+ throw new std::runtime_error("AttachCurrentThread() failed");
+ }
+ detach = true;
+ }
+ }
+
+ return detach;
+}
+
+void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach) {
+ if (detach) {
+ assert(vm != nullptr);
+ assert(env != nullptr);
+
+ jint ret;
+ if ((ret = vm->DetachCurrentThread()) != JNI_OK) {
+ mbgl::Log::Error(mbgl::Event::JNI, "DetachCurrentThread() failed with %i", ret);
+ throw new std::runtime_error("DetachCurrentThread() failed");
+ }
+ }
+ *env = nullptr;
+}
+
+std::string std_string_from_jstring(JNIEnv *env, jstring jstr) {
+ std::string str;
+
+ if (jstr == nullptr) {
+ if (env->ThrowNew(nullPointerExceptionClass, "String cannot be null.") < 0) {
+ env->ExceptionDescribe();
+ return str;
+ }
+
+ return str;
+ }
+
+ jsize len = env->GetStringLength(jstr);
+ if (len < 0) {
+ env->ExceptionDescribe();
+ return str;
+ }
+
+ const jchar *chars = env->GetStringChars(jstr, nullptr);
+ if (chars == nullptr) {
+ env->ExceptionDescribe();
+ return str;
+ }
+
+ std::u16string ustr(reinterpret_cast<const char16_t *>(chars), len);
+ env->ReleaseStringChars(jstr, chars);
+ chars = nullptr;
+ str = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().to_bytes(ustr);
+ return str;
+}
+
+jstring std_string_to_jstring(JNIEnv *env, std::string str) {
+ std::u16string ustr =
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>().from_bytes(str);
+
+ jstring jstr = env->NewString(reinterpret_cast<const jchar *>(ustr.c_str()), ustr.size());
+ if (jstr == nullptr) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ return jstr;
+}
+
+std::vector<std::string> std_vector_string_from_jobject(JNIEnv *env, jobject jlist) {
+ std::vector<std::string> vector;
+
+ if (jlist == nullptr) {
+ if (env->ThrowNew(nullPointerExceptionClass, "List cannot be null.") < 0) {
+ env->ExceptionDescribe();
+ return vector;
+ }
+
+ return vector;
+ }
+
+ jobjectArray jarray =
+ reinterpret_cast<jobjectArray>(env->CallObjectMethod(jlist, listToArrayId));
+ if (env->ExceptionCheck() || (jarray == nullptr)) {
+ env->ExceptionDescribe();
+ return vector;
+ }
+
+ jsize len = env->GetArrayLength(jarray);
+ if (len < 0) {
+ env->ExceptionDescribe();
+ return vector;
+ }
+
+ for (jsize i = 0; i < len; i++) {
+ jstring jstr = reinterpret_cast<jstring>(env->GetObjectArrayElement(jarray, i));
+ if (jstr == nullptr) {
+ env->ExceptionDescribe();
+ return vector;
+ }
+
+ vector.push_back(std_string_from_jstring(env, jstr));
+ }
+
+ env->DeleteLocalRef(jarray);
+ jarray = nullptr;
+
+ return vector;
+}
+
+jobject std_vector_string_to_jobject(JNIEnv *env, std::vector<std::string> vector) {
+ jobject jlist = env->NewObject(arrayListClass, arrayListConstructorId);
+ if (jlist == nullptr) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ for (const auto& str : vector) {
+ env->CallBooleanMethod(jlist, arrayListAddId, std_string_to_jstring(env, str));
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+ }
+
+ return jlist;
+}
+
+jlongArray std_vector_uint_to_jobject(JNIEnv *env, std::vector<uint32_t> vector) {
+ jlongArray jarray = env->NewLongArray(vector.size());
+ if (jarray == nullptr) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ std::vector<jlong> v;
+ for (const uint32_t& id : vector) {
+ v.push_back((jlong)id);
+ }
+
+ env->SetLongArrayRegion(jarray, 0, v.size(), &(v[0]));
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ return jarray;
+}
+
+mbgl::AnnotationSegment annotation_segment_from_latlng_jlist(JNIEnv *env, jobject jlist) {
+ mbgl::AnnotationSegment segment;
+
+ if (jlist == nullptr) {
+ if (env->ThrowNew(nullPointerExceptionClass, "List cannot be null.") < 0) {
+ env->ExceptionDescribe();
+ return segment;
+ }
+ return segment;
+ }
+
+ jobjectArray jarray =
+ reinterpret_cast<jobjectArray>(env->CallObjectMethod(jlist, listToArrayId));
+ if (env->ExceptionCheck() || (jarray == nullptr)) {
+ env->ExceptionDescribe();
+ return segment;
+ }
+
+ jsize len = env->GetArrayLength(jarray);
+ if (len < 0) {
+ env->ExceptionDescribe();
+ return segment;
+ }
+
+ segment.reserve(len);
+
+ for (jsize i = 0; i < len; i++) {
+ jobject latLng = reinterpret_cast<jobject>(env->GetObjectArrayElement(jarray, i));
+ if (latLng == nullptr) {
+ env->ExceptionDescribe();
+ return segment;
+ }
+
+ jdouble latitude = env->GetDoubleField(latLng, latLngLatitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return segment;
+ }
+
+ jdouble longitude = env->GetDoubleField(latLng, latLngLongitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return segment;
+ }
+
+ segment.push_back(mbgl::LatLng(latitude, longitude));
+ env->DeleteLocalRef(latLng);
+ }
+
+ env->DeleteLocalRef(jarray);
+ jarray = nullptr;
+
+ return segment;
+}
+
+std::pair<mbgl::AnnotationSegment, mbgl::ShapeAnnotation::Properties> annotation_std_pair_from_polygon_jobject(JNIEnv *env, jobject polygon) {
+ jfloat alpha = env->GetFloatField(polygon, polygonAlphaId);
+ jint fillColor = env->GetIntField(polygon, polygonFillColorId);
+ jint strokeColor = env->GetIntField(polygon, polygonStrokeColorId);
+
+ int rF = (fillColor >> 16) & 0xFF;
+ int gF = (fillColor >> 8) & 0xFF;
+ int bF = (fillColor) & 0xFF;
+ int aF = (fillColor >> 24) & 0xFF;
+
+ int rS = (strokeColor >> 16) & 0xFF;
+ int gS = (strokeColor >> 8) & 0xFF;
+ int bS = (strokeColor) & 0xFF;
+ int aS = (strokeColor >> 24) & 0xFF;
+
+ mbgl::ShapeAnnotation::Properties shapeProperties;
+ mbgl::FillPaintProperties fillProperties;
+ fillProperties.opacity = alpha;
+ fillProperties.stroke_color = {{ static_cast<float>(rS) / 255.0f, static_cast<float>(gS) / 255.0f, static_cast<float>(bS) / 255.0f, static_cast<float>(aS) / 255.0f }};
+ fillProperties.fill_color = {{ static_cast<float>(rF) / 255.0f, static_cast<float>(gF) / 255.0f, static_cast<float>(bF) / 255.0f, static_cast<float>(aF) / 255.0f }};
+ shapeProperties.set<mbgl::FillPaintProperties>(fillProperties);
+
+ jobject points = env->GetObjectField(polygon, polygonPointsId);
+ mbgl::AnnotationSegment segment = annotation_segment_from_latlng_jlist(env, points);
+ env->DeleteLocalRef(points);
+
+ return std::make_pair(segment, shapeProperties);
+}
+
+}
+}
+
+namespace {
+
+using namespace mbgl::android;
+
+jlong JNICALL nativeCreate(JNIEnv *env, jobject obj, jstring cachePath_, jstring dataPath_, jstring apkPath_, jfloat pixelRatio, jint availableProcessors, jlong totalMemory) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreate");
+ cachePath = std_string_from_jstring(env, cachePath_);
+ dataPath = std_string_from_jstring(env, dataPath_);
+ apkPath = std_string_from_jstring(env, apkPath_);
+ NativeMapView *nativeMapView = new NativeMapView(env, obj, pixelRatio, availableProcessors, totalMemory);
+ jlong mapViewPtr = reinterpret_cast<jlong>(nativeMapView);
+ return mapViewPtr;
+}
+
+void JNICALL nativeDestroy(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeDestroy");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ delete nativeMapView;
+ nativeMapView = nullptr;
+}
+
+void JNICALL nativeInitializeDisplay(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeDisplay");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ try
+ {
+ nativeMapView->initializeDisplay();
+ } catch(const std::exception& e) {
+ throw_jni_error(env, "Unable to initialize GL display.");
+ }
+}
+
+void JNICALL nativeTerminateDisplay(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminateDisplay");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->terminateDisplay();
+}
+
+void JNICALL nativeInitializeContext(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeContext");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ try {
+ nativeMapView->initializeContext();
+ } catch(const std::exception& e) {
+ throw_jni_error(env, "Unable to initialize GL context.");
+ }
+}
+
+void JNICALL nativeTerminateContext(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminateContext");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->terminateContext();
+}
+
+void JNICALL nativeCreateSurface(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject surface) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreateSurface");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ try {
+ nativeMapView->createSurface(ANativeWindow_fromSurface(env, surface));
+ } catch(const std::exception& e) {
+ throw_jni_error(env, "Unable to create GL surface.");
+ }
+}
+
+void JNICALL nativeDestroySurface(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeDestroySurface");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->destroySurface();
+}
+
+void JNICALL nativePause(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativePause");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->pause();
+}
+
+jboolean JNICALL nativeIsPaused(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeIsPaused");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().isPaused();
+}
+
+void JNICALL nativeResume(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeResume");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->resume();
+}
+
+void JNICALL nativeUpdate(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdate");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().update(mbgl::Update::Repaint);
+}
+
+void JNICALL nativeRenderSync(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeRenderSync");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().renderSync();
+}
+
+void JNICALL nativeViewResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint width, jint height) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeViewResize");
+ assert(nativeMapViewPtr != 0);
+ assert(width >= 0);
+ assert(height >= 0);
+ assert(width <= UINT16_MAX);
+ assert(height <= UINT16_MAX);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->resizeView(width, height);
+}
+
+void JNICALL nativeFramebufferResize(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jint fbWidth, jint fbHeight) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeFramebufferResize");
+ assert(nativeMapViewPtr != 0);
+ assert(fbWidth >= 0);
+ assert(fbHeight >= 0);
+ assert(fbWidth <= UINT16_MAX);
+ assert(fbHeight <= UINT16_MAX);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->resizeFramebuffer(fbWidth, fbHeight);
+}
+
+void JNICALL nativeRemoveClass(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring clazz) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveClass");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().removeClass(std_string_from_jstring(env, clazz));
+}
+
+jboolean JNICALL nativeHasClass(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring clazz) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeHasClass");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().hasClass(std_string_from_jstring(env, clazz));
+}
+
+void JNICALL nativeAddClass(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring clazz) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddClass");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().addClass(std_string_from_jstring(env, clazz));
+}
+
+void JNICALL nativeSetClasses(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject classes) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetClasses");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setClasses(std_vector_string_from_jobject(env, classes));
+}
+
+jobject JNICALL nativeGetClasses(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetClasses");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return std_vector_string_to_jobject(env, nativeMapView->getMap().getClasses());
+}
+
+void JNICALL nativeSetDefaultTransitionDuration(JNIEnv *env, jobject obj, jlong nativeMapViewPtr,
+ jlong duration) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetDefaultTransitionDuration");
+ assert(nativeMapViewPtr != 0);
+ assert(duration >= 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setDefaultTransitionDuration(std::chrono::milliseconds(duration));
+}
+
+jlong JNICALL nativeGetDefaultTransitionDuration(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetDefaultTransitionDuration");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return std::chrono::duration_cast<std::chrono::milliseconds>(nativeMapView->getMap().getDefaultTransitionDuration()).count();
+}
+
+void JNICALL nativeSetStyleURL(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring url) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetStyleURL");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setStyleURL(std_string_from_jstring(env, url));
+}
+
+void JNICALL nativeSetStyleJSON(JNIEnv *env, jobject obj, jlong nativeMapViewPtr,
+ jstring newStyleJson, jstring base) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetStyleJSON");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setStyleJSON(std_string_from_jstring(env, newStyleJson),
+ std_string_from_jstring(env, base));
+}
+
+jstring JNICALL nativeGetStyleJSON(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetStyleJSON");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return std_string_to_jstring(env, nativeMapView->getMap().getStyleJSON());
+}
+
+void JNICALL nativeSetAccessToken(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring accessToken) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetAccessToken");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getFileSource().setAccessToken(std_string_from_jstring(env, accessToken));
+}
+
+jstring JNICALL nativeGetAccessToken(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetAccessToken");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return std_string_to_jstring(env, nativeMapView->getFileSource().getAccessToken());
+}
+
+void JNICALL nativeCancelTransitions(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeCancelTransitions");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().cancelTransitions();
+}
+
+void JNICALL nativeSetGestureInProgress(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jboolean inProgress) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetGestureInProgress");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setGestureInProgress(inProgress);
+}
+
+void JNICALL nativeMoveBy(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble dx, jdouble dy,
+ jlong duration) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeMoveBy");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().moveBy(dx, dy, std::chrono::milliseconds(duration));
+}
+
+void JNICALL nativeSetLatLng(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject latLng,
+ jlong duration) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetLatLng");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ jdouble latitude = env->GetDoubleField(latLng, latLngLatitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ jdouble longitude = env->GetDoubleField(latLng, latLngLongitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ nativeMapView->getMap().setLatLng(mbgl::LatLng(latitude, longitude), std::chrono::milliseconds(duration));
+}
+
+jobject JNICALL nativeGetLatLng(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetLatLng");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ mbgl::LatLng latLng = nativeMapView->getMap().getLatLng();
+
+ jobject ret = env->NewObject(latLngClass, latLngConstructorId, latLng.latitude, latLng.longitude);
+ if (ret == nullptr) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ return ret;
+}
+
+void JNICALL nativeResetPosition(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeResetPosition");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().resetPosition();
+}
+
+void JNICALL nativeScaleBy(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble ds, jdouble cx,
+ jdouble cy, jlong duration) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeScaleBy");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().scaleBy(ds, cx, cy, std::chrono::milliseconds(duration));
+}
+
+void JNICALL nativeSetScale(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble scale,
+ jdouble cx, jdouble cy, jlong duration) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetScale");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setScale(scale, cx, cy, std::chrono::milliseconds(duration));
+}
+
+jdouble JNICALL nativeGetScale(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetScale");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().getScale();
+}
+
+void JNICALL nativeSetZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble zoom, jlong duration) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetZoom");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setZoom(zoom, std::chrono::milliseconds(duration));
+}
+
+jdouble JNICALL nativeGetZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetZoom");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().getZoom();
+}
+
+void JNICALL nativeSetLatLngZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr,
+ jobject latLngZoom, jlong duration) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetLatLngZoom");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ jdouble latitude = env->GetDoubleField(latLngZoom, latLngZoomLatitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ jdouble longitude = env->GetDoubleField(latLngZoom, latLngZoomLongitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ jdouble zoom = env->GetDoubleField(latLngZoom, latLngZoomZoomId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ nativeMapView->getMap().setLatLngZoom(mbgl::LatLng(latitude, longitude), zoom, std::chrono::milliseconds(duration));
+}
+
+jobject JNICALL nativeGetLatLngZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetLatLngZoom");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ mbgl::LatLng latLng = nativeMapView->getMap().getLatLng();
+ jdouble zoom = nativeMapView->getMap().getZoom();
+
+ jobject ret = env->NewObject(latLngZoomClass, latLngZoomConstructorId, latLng.longitude, latLng.latitude, zoom);
+ if (ret == nullptr) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ return ret;
+}
+
+void JNICALL nativeResetZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeResetZoom");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().resetZoom();
+}
+
+jdouble JNICALL nativeGetMinZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetMinZoom");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().getMinZoom();
+}
+
+jdouble JNICALL nativeGetMaxZoom(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetMaxZoom");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().getMaxZoom();
+}
+
+void JNICALL nativeRotateBy(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble sx,
+ jdouble sy, jdouble ex, jdouble ey, jlong duration) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeRotateBy");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().rotateBy(sx, sy, ex, ey, std::chrono::milliseconds(duration));
+}
+
+void JNICALL nativeSetBearing(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble degrees,
+ jlong duration) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetBearing");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setBearing(degrees, std::chrono::milliseconds(duration));
+}
+
+void JNICALL nativeSetBearing(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble degrees,
+ jdouble cx, jdouble cy) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetBearing");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setBearing(degrees, cx, cy);
+}
+
+jdouble JNICALL nativeGetBearing(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetBearing");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().getBearing();
+}
+
+void JNICALL nativeResetNorth(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeResetNorth");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().resetNorth();
+}
+
+jlong JNICALL nativeAddMarker(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject marker) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddMarker");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ jobject position = env->GetObjectField(marker, markerPositionId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return -1;
+ }
+
+ jobject icon = env->GetObjectField(marker, markerIconId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return -1;
+ }
+
+ jstring jid = reinterpret_cast<jstring>(env->GetObjectField(icon, spriteIdId));
+ std::string id = std_string_from_jstring(env, jid);
+
+ jdouble latitude = env->GetDoubleField(position, latLngLatitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return -1;
+ }
+
+ jdouble longitude = env->GetDoubleField(position, latLngLongitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return -1;
+ }
+
+ // Because Java only has int, not unsigned int, we need to bump the annotation id up to a long.
+ return nativeMapView->getMap().addPointAnnotation(mbgl::PointAnnotation(mbgl::LatLng(latitude, longitude), id));
+}
+
+jlongArray JNICALL nativeAddMarkers(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject jlist) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddMarkers");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ std::vector<mbgl::PointAnnotation> markers;
+
+ if (jlist == nullptr) {
+ if (env->ThrowNew(nullPointerExceptionClass, "List cannot be null.") < 0) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+ return nullptr;
+ }
+
+ jobjectArray jarray =
+ reinterpret_cast<jobjectArray>(env->CallObjectMethod(jlist, listToArrayId));
+ if (env->ExceptionCheck() || (jarray == nullptr)) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jsize len = env->GetArrayLength(jarray);
+ if (len < 0) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ markers.reserve(len);
+
+ for (jsize i = 0; i < len; i++) {
+ jobject marker = reinterpret_cast<jobject>(env->GetObjectArrayElement(jarray, i));
+
+ jobject position = env->GetObjectField(marker, markerPositionId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jobject icon = env->GetObjectField(marker, markerIconId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+ env->DeleteLocalRef(marker);
+
+ jstring jid = reinterpret_cast<jstring>(env->GetObjectField(icon, spriteIdId));
+ env->DeleteLocalRef(icon);
+
+ std::string id = std_string_from_jstring(env, jid);
+ env->DeleteLocalRef(jid);
+
+ jdouble latitude = env->GetDoubleField(position, latLngLatitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jdouble longitude = env->GetDoubleField(position, latLngLongitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+ env->DeleteLocalRef(position);
+
+ markers.emplace_back(mbgl::PointAnnotation(mbgl::LatLng(latitude, longitude), id));
+ }
+
+ env->DeleteLocalRef(jarray);
+
+ std::vector<uint32_t> pointAnnotationIDs = nativeMapView->getMap().addPointAnnotations(markers);
+ return std_vector_uint_to_jobject(env, pointAnnotationIDs);
+}
+
+
+jlong JNICALL nativeAddPolyline(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject polyline) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddPolyline");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ jfloat alpha = env->GetFloatField(polyline, polylineAlphaId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return -1;
+ }
+
+ jint color = env->GetIntField(polyline, polylineColorId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return -1;
+ }
+
+ int r = (color >> 16) & 0xFF;
+ int g = (color >> 8) & 0xFF;
+ int b = (color) & 0xFF;
+ int a = (color >> 24) & 0xFF;
+
+ jfloat width = env->GetFloatField(polyline, polylineWidthId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return -1;
+ }
+
+ mbgl::ShapeAnnotation::Properties shapeProperties;
+ mbgl::LinePaintProperties lineProperties;
+ lineProperties.opacity = alpha;
+ lineProperties.color = {{ static_cast<float>(r) / 255.0f, static_cast<float>(g) / 255.0f, static_cast<float>(b) / 255.0f, static_cast<float>(a) / 255.0f }};
+ lineProperties.width = width;
+ shapeProperties.set<mbgl::LinePaintProperties>(lineProperties);
+
+ jobject points = env->GetObjectField(polyline, polylinePointsId);
+ mbgl::AnnotationSegment segment = annotation_segment_from_latlng_jlist(env, points);
+
+ std::vector<mbgl::ShapeAnnotation> shapes;
+ shapes.emplace_back(mbgl::AnnotationSegments { segment }, shapeProperties);
+
+ std::vector<uint32_t> shapeAnnotationIDs = nativeMapView->getMap().addShapeAnnotations(shapes);
+ uint32_t id = shapeAnnotationIDs.at(0);
+
+ return id;
+}
+
+jlong JNICALL nativeAddPolygon(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject polygon) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddPolygon");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ std::vector<mbgl::ShapeAnnotation> shapes;
+ std::pair<mbgl::AnnotationSegment, mbgl::ShapeAnnotation::Properties> segment = annotation_std_pair_from_polygon_jobject(env, polygon);
+
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return -1;
+ }
+
+ shapes.emplace_back(mbgl::AnnotationSegments { segment.first }, segment.second);
+
+ std::vector<uint32_t> shapeAnnotationIDs = nativeMapView->getMap().addShapeAnnotations(shapes);
+ uint32_t id = shapeAnnotationIDs.at(0);
+ return id;
+}
+
+jlongArray JNICALL nativeAddPolygons(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject jlist) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddPolygons");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ std::vector<mbgl::ShapeAnnotation> shapes;
+
+ if (jlist == nullptr) {
+ if (env->ThrowNew(nullPointerExceptionClass, "List cannot be null.") < 0) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+ return nullptr;
+ }
+
+ jobjectArray jarray =
+ reinterpret_cast<jobjectArray>(env->CallObjectMethod(jlist, listToArrayId));
+ if (env->ExceptionCheck() || (jarray == nullptr)) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jsize len = env->GetArrayLength(jarray);
+ if (len < 0) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ shapes.reserve(len);
+
+ for (jsize i = 0; i < len; i++) {
+ jobject polygon = reinterpret_cast<jobject>(env->GetObjectArrayElement(jarray, i));
+
+ std::pair<mbgl::AnnotationSegment, mbgl::ShapeAnnotation::Properties> segment = annotation_std_pair_from_polygon_jobject(env, polygon);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ shapes.emplace_back(mbgl::AnnotationSegments { segment.first }, segment.second);
+
+ env->DeleteLocalRef(polygon);
+ }
+
+ env->DeleteLocalRef(jarray);
+
+ std::vector<uint32_t> shapeAnnotationIDs = nativeMapView->getMap().addShapeAnnotations(shapes);
+ return std_vector_uint_to_jobject(env, shapeAnnotationIDs);
+}
+
+void JNICALL nativeRemoveAnnotation(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jlong annotationId) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveAnnotation");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().removeAnnotation(static_cast<uint32_t>(annotationId));
+}
+
+void JNICALL nativeRemoveAnnotations(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jlongArray jarray) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveAnnotations");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ std::vector<uint32_t> ids;
+
+ if (env->ExceptionCheck() || (jarray == nullptr)) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ jsize len = env->GetArrayLength(jarray);
+ if (len < 0) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ ids.reserve(len);
+ jlong* jids = env->GetLongArrayElements(jarray, nullptr);
+
+ for (jsize i = 0; i < len; i++) {
+ if(jids[i] == -1L)
+ continue;
+ ids.push_back(static_cast<uint32_t>(jids[i]));
+ }
+
+ env->ReleaseLongArrayElements(jarray, jids, JNI_ABORT);
+
+ nativeMapView->getMap().removeAnnotations(ids);
+}
+
+jlongArray JNICALL nativeGetAnnotationsInBounds(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject bbox) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetAnnotationsInBounds");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ if (env->ExceptionCheck() || (bbox == nullptr)) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jdouble swLat = env->GetDoubleField(bbox, bboxLatSouthId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jdouble swLon = env->GetDoubleField(bbox, bboxLonWestId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jdouble neLat = env->GetDoubleField(bbox, bboxLatNorthId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jdouble neLon = env->GetDoubleField(bbox, bboxLonEastId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ mbgl::LatLngBounds bounds;
+ bounds.sw = { swLat, swLon };
+ bounds.ne = { neLat, neLon };
+
+ // assume only points for now
+ std::vector<uint32_t> annotations = nativeMapView->getMap().getPointAnnotationsInBounds(bounds);
+
+ return std_vector_uint_to_jobject(env, annotations);
+}
+
+void JNICALL nativeSetSprite(JNIEnv *env, jobject obj, jlong nativeMapViewPtr,
+ jstring symbol, jint width, jint height, jfloat scale, jbyteArray jpixels) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetSprite");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ const std::string symbolName = std_string_from_jstring(env, symbol);
+
+ jbyte* pixelData = env->GetByteArrayElements(jpixels, nullptr);
+ jsize size = env->GetArrayLength(jpixels);
+ std::string pixels(reinterpret_cast<char*>(pixelData), size);
+ env->ReleaseByteArrayElements(jpixels, pixelData, JNI_ABORT);
+
+ auto spriteImage = std::make_shared<mbgl::SpriteImage>(
+ uint16_t(width),
+ uint16_t(height),
+ float(scale),
+ std::move(pixels));
+
+ nativeMapView->getMap().setSprite(symbolName, spriteImage);
+}
+
+void JNICALL nativeSetVisibleCoordinateBounds(JNIEnv *env, jobject obj, jlong nativeMapViewPtr,
+ jobjectArray coordinates, jobject padding, jdouble direction, jlong duration) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetVisibleCoordinateBounds");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ jfloat left = env->GetFloatField(padding, rectFLeftId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ jfloat right = env->GetFloatField(padding, rectFRightId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ jfloat top = env->GetFloatField(padding, rectFTopId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ jfloat bottom = env->GetFloatField(padding, rectFBottomId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ jsize count = env->GetArrayLength(coordinates);
+
+ mbgl::EdgeInsets mbglInsets = {top, left, bottom, right};
+ mbgl::AnnotationSegment segment;
+ segment.reserve(count);
+
+ for (int i = 0; i < count; i++) {
+ jobject latLng = reinterpret_cast<jobject>(env->GetObjectArrayElement(coordinates, i));
+ jdouble latitude = env->GetDoubleField(latLng, latLngLatitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+ jdouble longitude = env->GetDoubleField(latLng, latLngLongitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return;
+ }
+ segment.push_back(mbgl::LatLng(latitude, longitude));
+ }
+
+ mbgl::CameraOptions options = nativeMapView->getMap().cameraForLatLngs(segment, mbglInsets);
+
+ if (direction >= 0) {
+ // convert from degrees to radians
+ options.angle = (-direction * M_PI) / 180;
+ }
+ if (duration > 0) {
+ options.duration = std::chrono::milliseconds(duration);
+ // equivalent to kCAMediaTimingFunctionDefault in iOS
+ options.easing = {0.25, 0.1, 0.25, 0.1};
+ }
+
+ nativeMapView->getMap().easeTo(options);
+}
+
+void JNICALL nativeOnLowMemory(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeOnLowMemory");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().onLowMemory();
+}
+
+void JNICALL nativeSetDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jboolean debug) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetDebug");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setDebug(debug);
+ nativeMapView->enableFps(debug);
+}
+
+void JNICALL nativeToggleDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeToggleDebug");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().toggleDebug();
+ nativeMapView->enableFps(nativeMapView->getMap().getDebug());
+}
+
+jboolean JNICALL nativeGetDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetDebug");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().getDebug();
+}
+
+void JNICALL nativeSetCollisionDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jboolean debug) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetCollisionDebug");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().setCollisionDebug(debug);
+}
+
+void JNICALL nativeToggleCollisionDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeToggleCollisionDebug");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().toggleCollisionDebug();
+}
+
+jboolean JNICALL nativeGetCollisionDebug(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetCollisionDebug");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().getCollisionDebug();
+}
+
+jboolean JNICALL nativeIsFullyLoaded(JNIEnv *env, jobject obj, jlong nativeMapViewPtr) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeIsFullyLoaded");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().isFullyLoaded();
+}
+
+void JNICALL nativeSetReachability(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jboolean status) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetReachability");
+ assert(nativeMapViewPtr != 0);
+ if (status) {
+ mbgl::NetworkStatus::Reachable();
+ }
+}
+
+jdouble JNICALL nativeGetMetersPerPixelAtLatitude(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jdouble lat, jdouble zoom) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetMetersPerPixelAtLatitude");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().getMetersPerPixelAtLatitude(lat, zoom);
+}
+
+jobject JNICALL nativeProjectedMetersForLatLng(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject latLng) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeProjectedMetersForLatLng");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ jdouble latitude = env->GetDoubleField(latLng, latLngLatitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jdouble longitude = env->GetDoubleField(latLng, latLngLongitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ mbgl::ProjectedMeters projectedMeters = nativeMapView->getMap().projectedMetersForLatLng(mbgl::LatLng(latitude, longitude));
+
+ jobject ret = env->NewObject(projectedMetersClass, projectedMetersConstructorId, projectedMeters.northing, projectedMeters.easting);
+ if (ret == nullptr) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ return ret;
+}
+
+jobject JNICALL nativeLatLngForProjectedMeters(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject projectedMeters) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeLatLngForProjectedMeters");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ jdouble northing = env->GetDoubleField(projectedMeters, projectedMetersNorthingId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jdouble easting = env->GetDoubleField(projectedMeters, projectedMetersEastingId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ mbgl::LatLng latLng = nativeMapView->getMap().latLngForProjectedMeters(mbgl::ProjectedMeters(northing, easting));
+
+ jobject ret = env->NewObject(latLngClass, latLngConstructorId, latLng.latitude, latLng.longitude);
+ if (ret == nullptr) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ return ret;
+}
+
+jobject JNICALL nativePixelForLatLng(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject latLng) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativePixelForLatLng");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ jdouble latitude = env->GetDoubleField(latLng, latLngLatitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jdouble longitude = env->GetDoubleField(latLng, latLngLongitudeId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ mbgl::vec2<double> pixel = nativeMapView->getMap().pixelForLatLng(mbgl::LatLng(latitude, longitude));
+
+ jobject ret = env->NewObject(pointFClass, pointFConstructorId, static_cast<jfloat>(pixel.x), static_cast<jfloat>(pixel.y));
+ if (ret == nullptr) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ return ret;
+}
+
+jobject JNICALL nativeLatLngForPixel(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject pixel) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeLatLngForPixel");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ jfloat x = env->GetFloatField(pixel, pointFXId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ jfloat y = env->GetFloatField(pixel, pointFYId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ mbgl::LatLng latLng = nativeMapView->getMap().latLngForPixel(mbgl::vec2<double>(x, y));
+
+ jobject ret = env->NewObject(latLngClass, latLngConstructorId, latLng.latitude, latLng.longitude);
+ if (ret == nullptr) {
+ env->ExceptionDescribe();
+ return nullptr;
+ }
+
+ return ret;
+}
+
+jdouble JNICALL nativeGetTopOffsetPixelsForAnnotationSymbol(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jstring symbolName) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetTopOffsetPixelsForAnnotationSymbol");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ return nativeMapView->getMap().getTopOffsetPixelsForAnnotationSymbol(std_string_from_jstring(env, symbolName));
+}
+
+
+}
+
+extern "C" {
+
+extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "JNI_OnLoad");
+
+ theJVM = vm;
+
+ JNIEnv *env = nullptr;
+ jint ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
+ if (ret != JNI_OK) {
+ mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
+ return JNI_ERR;
+ }
+
+ latLngClass = env->FindClass("com/mapbox/mapboxsdk/geometry/LatLng");
+ if (latLngClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ latLngConstructorId = env->GetMethodID(latLngClass, "<init>", "(DD)V");
+ if (latLngConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ latLngLatitudeId = env->GetFieldID(latLngClass, "latitude", "D");
+ if (latLngLatitudeId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ latLngLongitudeId = env->GetFieldID(latLngClass, "longitude", "D");
+ if (latLngLongitudeId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ latLngZoomClass = env->FindClass("com/mapbox/mapboxsdk/geometry/LatLngZoom");
+ if (latLngZoomClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ latLngZoomConstructorId = env->GetMethodID(latLngZoomClass, "<init>", "(DDD)V");
+ if (latLngZoomConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ latLngZoomLatitudeId = env->GetFieldID(latLngZoomClass, "latitude", "D");
+ if (latLngZoomLatitudeId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ latLngZoomLongitudeId = env->GetFieldID(latLngZoomClass, "longitude", "D");
+ if (latLngZoomLongitudeId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ latLngZoomZoomId = env->GetFieldID(latLngZoomClass, "zoom", "D");
+ if (latLngZoomZoomId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ bboxClass = env->FindClass("com/mapbox/mapboxsdk/geometry/BoundingBox");
+ if (bboxClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ bboxConstructorId = env->GetMethodID(bboxClass, "<init>", "(DDDD)V");
+ if (bboxConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ bboxLatNorthId = env->GetFieldID(bboxClass, "mLatNorth", "D");
+ if (bboxLatNorthId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ bboxLatSouthId = env->GetFieldID(bboxClass, "mLatSouth", "D");
+ if (bboxLatSouthId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ bboxLonEastId = env->GetFieldID(bboxClass, "mLonEast", "D");
+ if (bboxLonEastId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ bboxLonWestId = env->GetFieldID(bboxClass, "mLonWest", "D");
+ if (bboxLonWestId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ spriteClass = env->FindClass("com/mapbox/mapboxsdk/annotations/Sprite");
+ if (spriteClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ spriteIdId = env->GetFieldID(spriteClass, "mId", "Ljava/lang/String;");
+ if (spriteIdId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ markerClass = env->FindClass("com/mapbox/mapboxsdk/annotations/Marker");
+ if (markerClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ markerPositionId = env->GetFieldID(markerClass, "position", "Lcom/mapbox/mapboxsdk/geometry/LatLng;");
+ if (markerPositionId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ markerIconId = env->GetFieldID(markerClass, "icon", "Lcom/mapbox/mapboxsdk/annotations/Sprite;");
+ if (markerIconId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ polylineClass = env->FindClass("com/mapbox/mapboxsdk/annotations/Polyline");
+ if (polylineClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ polylineAlphaId = env->GetFieldID(polylineClass, "alpha", "F");
+ if (polylineAlphaId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ polylineColorId = env->GetFieldID(polylineClass, "color", "I");
+ if (polylineColorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ polylineWidthId = env->GetFieldID(polylineClass, "width", "F");
+ if (polylineWidthId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ polylinePointsId = env->GetFieldID(polylineClass, "points", "Ljava/util/List;");
+ if (polylineWidthId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ polygonClass = env->FindClass("com/mapbox/mapboxsdk/annotations/Polygon");
+ if (polygonClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ polygonAlphaId = env->GetFieldID(polygonClass, "alpha", "F");
+ if (polygonAlphaId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ polygonFillColorId = env->GetFieldID(polygonClass, "fillColor", "I");
+ if (polygonFillColorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ polygonStrokeColorId = env->GetFieldID(polygonClass, "strokeColor", "I");
+ if (polygonStrokeColorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ polygonPointsId = env->GetFieldID(polygonClass, "points", "Ljava/util/List;");
+ if (polygonPointsId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ jclass nativeMapViewClass = env->FindClass("com/mapbox/mapboxsdk/views/NativeMapView");
+ if (nativeMapViewClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ onInvalidateId = env->GetMethodID(nativeMapViewClass, "onInvalidate", "()V");
+ if (onInvalidateId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ onMapChangedId = env->GetMethodID(nativeMapViewClass, "onMapChanged", "(I)V");
+ if (onMapChangedId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ onFpsChangedId = env->GetMethodID(nativeMapViewClass, "onFpsChanged", "(D)V");
+ if (onFpsChangedId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ runtimeExceptionClass = env->FindClass("java/lang/RuntimeException");
+ if (runtimeExceptionClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ nullPointerExceptionClass = env->FindClass("java/lang/NullPointerException");
+ if (nullPointerExceptionClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ jclass listClass = env->FindClass("java/util/List");
+ if (listClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ listToArrayId = env->GetMethodID(listClass, "toArray", "()[Ljava/lang/Object;");
+ if (listToArrayId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ arrayListClass = env->FindClass("java/util/ArrayList");
+ if (arrayListClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ arrayListConstructorId = env->GetMethodID(arrayListClass, "<init>", "()V");
+ if (arrayListConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ arrayListAddId = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
+ if (arrayListAddId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ projectedMetersClass = env->FindClass("com/mapbox/mapboxsdk/geometry/ProjectedMeters");
+ if (projectedMetersClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ projectedMetersConstructorId = env->GetMethodID(projectedMetersClass, "<init>", "(DD)V");
+ if (projectedMetersConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ projectedMetersNorthingId = env->GetFieldID(projectedMetersClass, "northing", "D");
+ if (projectedMetersNorthingId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ projectedMetersEastingId = env->GetFieldID(projectedMetersClass, "easting", "D");
+ if (projectedMetersEastingId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ pointFClass = env->FindClass("android/graphics/PointF");
+ if (pointFClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ pointFConstructorId = env->GetMethodID(pointFClass, "<init>", "(FF)V");
+ if (pointFConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ pointFXId = env->GetFieldID(pointFClass, "x", "F");
+ if (pointFXId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ pointFYId = env->GetFieldID(pointFClass, "y", "F");
+ if (pointFYId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ rectFClass = env->FindClass("android/graphics/RectF");
+ if (rectFClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ rectFConstructorId = env->GetMethodID(rectFClass, "<init>", "()V");
+ if (rectFConstructorId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ rectFLeftId = env->GetFieldID(rectFClass, "left", "F");
+ if (rectFLeftId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ rectFRightId = env->GetFieldID(rectFClass, "right", "F");
+ if (rectFRightId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ rectFTopId = env->GetFieldID(rectFClass, "top", "F");
+ if (rectFTopId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ rectFBottomId = env->GetFieldID(rectFClass, "bottom", "F");
+ if (rectFBottomId == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ httpContextClass = env->FindClass("com/mapbox/mapboxsdk/http/HTTPContext");
+ if (httpContextClass == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ httpContextGetInstanceId = env->GetStaticMethodID(httpContextClass, "getInstance", "()Lcom/mapbox/mapboxsdk/http/HTTPContext;");
+ if (httpContextGetInstanceId == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ httpContextCreateRequestId = env->GetMethodID(httpContextClass, "createRequest", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/mapbox/mapboxsdk/http/HTTPContext$HTTPRequest;");
+ if (httpContextCreateRequestId == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ httpRequestClass = env->FindClass("com/mapbox/mapboxsdk/http/HTTPContext$HTTPRequest");
+ if (httpRequestClass == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ httpRequestStartId = env->GetMethodID(httpRequestClass, "start", "()V");
+ if (httpRequestStartId == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ httpRequestCancelId = env->GetMethodID(httpRequestClass, "cancel", "()V");
+ if (httpRequestCancelId == nullptr) {
+ env->ExceptionDescribe();
+ }
+
+ const std::vector<JNINativeMethod> methods = {
+ {"nativeCreate", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;FIJ)J",
+ reinterpret_cast<void *>(&nativeCreate)},
+ {"nativeDestroy", "(J)V", reinterpret_cast<void *>(&nativeDestroy)},
+ {"nativeInitializeDisplay", "(J)V", reinterpret_cast<void *>(&nativeInitializeDisplay)},
+ {"nativeTerminateDisplay", "(J)V", reinterpret_cast<void *>(&nativeTerminateDisplay)},
+ {"nativeInitializeContext", "(J)V", reinterpret_cast<void *>(&nativeInitializeContext)},
+ {"nativeTerminateContext", "(J)V", reinterpret_cast<void *>(&nativeTerminateContext)},
+ {"nativeCreateSurface", "(JLandroid/view/Surface;)V",
+ reinterpret_cast<void *>(&nativeCreateSurface)},
+ {"nativeDestroySurface", "(J)V", reinterpret_cast<void *>(&nativeDestroySurface)},
+ {"nativePause", "(J)V", reinterpret_cast<void *>(&nativePause)},
+ {"nativeIsPaused", "(J)Z", reinterpret_cast<void *>(&nativeIsPaused)},
+ {"nativeResume", "(J)V", reinterpret_cast<void *>(&nativeResume)},
+ {"nativeUpdate", "(J)V", reinterpret_cast<void *>(&nativeUpdate)},
+ {"nativeRenderSync", "(J)V", reinterpret_cast<void *>(&nativeRenderSync)},
+ {"nativeViewResize", "(JII)V",
+ reinterpret_cast<void *>(static_cast<void JNICALL (
+ *)(JNIEnv *, jobject, jlong, jint, jint)>(&nativeViewResize))},
+ {"nativeFramebufferResize", "(JII)V",
+ reinterpret_cast<void *>(static_cast<void JNICALL (
+ *)(JNIEnv *, jobject, jlong, jint, jint)>(&nativeFramebufferResize))},
+ {"nativeAddClass", "(JLjava/lang/String;)V",
+ reinterpret_cast<void *>(&nativeAddClass)},
+ {"nativeRemoveClass", "(JLjava/lang/String;)V",
+ reinterpret_cast<void *>(&nativeRemoveClass)},
+ {"nativeHasClass", "(JLjava/lang/String;)Z",
+ reinterpret_cast<void *>(&nativeHasClass)},
+ {"nativeSetClasses", "(JLjava/util/List;)V",
+ reinterpret_cast<void *>(&nativeSetClasses)},
+ {"nativeGetClasses", "(J)Ljava/util/List;",
+ reinterpret_cast<void *>(&nativeGetClasses)},
+ {"nativeSetDefaultTransitionDuration", "(JJ)V",
+ reinterpret_cast<void *>(&nativeSetDefaultTransitionDuration)},
+ {"nativeGetDefaultTransitionDuration", "(J)J",
+ reinterpret_cast<void *>(&nativeGetDefaultTransitionDuration)},
+ {"nativeSetStyleUrl", "(JLjava/lang/String;)V",
+ reinterpret_cast<void *>(&nativeSetStyleURL)},
+ {"nativeSetStyleJson", "(JLjava/lang/String;Ljava/lang/String;)V",
+ reinterpret_cast<void *>(&nativeSetStyleJSON)},
+ {"nativeGetStyleJson", "(J)Ljava/lang/String;",
+ reinterpret_cast<void *>(&nativeGetStyleJSON)},
+ {"nativeSetAccessToken", "(JLjava/lang/String;)V",
+ reinterpret_cast<void *>(&nativeSetAccessToken)},
+ {"nativeGetAccessToken", "(J)Ljava/lang/String;",
+ reinterpret_cast<void *>(&nativeGetAccessToken)},
+ {"nativeCancelTransitions", "(J)V", reinterpret_cast<void *>(&nativeCancelTransitions)},
+ {"nativeSetGestureInProgress", "(JZ)V", reinterpret_cast<void *>(&nativeSetGestureInProgress)},
+ {"nativeMoveBy", "(JDDJ)V", reinterpret_cast<void *>(&nativeMoveBy)},
+ {"nativeSetLatLng", "(JLcom/mapbox/mapboxsdk/geometry/LatLng;J)V",
+ reinterpret_cast<void *>(&nativeSetLatLng)},
+ {"nativeGetLatLng", "(J)Lcom/mapbox/mapboxsdk/geometry/LatLng;",
+ reinterpret_cast<void *>(&nativeGetLatLng)},
+ {"nativeResetPosition", "(J)V", reinterpret_cast<void *>(&nativeResetPosition)},
+ {"nativeScaleBy", "(JDDDJ)V", reinterpret_cast<void *>(&nativeScaleBy)},
+ {"nativeSetScale", "(JDDDJ)V", reinterpret_cast<void *>(&nativeSetScale)},
+ {"nativeGetScale", "(J)D", reinterpret_cast<void *>(&nativeGetScale)},
+ {"nativeSetZoom", "(JDJ)V", reinterpret_cast<void *>(&nativeSetZoom)},
+ {"nativeGetZoom", "(J)D", reinterpret_cast<void *>(&nativeGetZoom)},
+ {"nativeSetLatLngZoom", "(JLcom/mapbox/mapboxsdk/geometry/LatLngZoom;J)V",
+ reinterpret_cast<void *>(&nativeSetLatLngZoom)},
+ {"nativeGetLatLngZoom", "(J)Lcom/mapbox/mapboxsdk/geometry/LatLngZoom;",
+ reinterpret_cast<void *>(&nativeGetLatLngZoom)},
+ {"nativeResetZoom", "(J)V", reinterpret_cast<void *>(&nativeResetZoom)},
+ {"nativeGetMinZoom", "(J)D", reinterpret_cast<void *>(&nativeGetMinZoom)},
+ {"nativeGetMaxZoom", "(J)D", reinterpret_cast<void *>(&nativeGetMaxZoom)},
+ {"nativeRotateBy", "(JDDDDJ)V", reinterpret_cast<void *>(&nativeRotateBy)},
+ {"nativeSetBearing", "(JDJ)V",
+ reinterpret_cast<void *>(
+ static_cast<void JNICALL (*)(JNIEnv *, jobject, jlong, jdouble, jlong)>(
+ &nativeSetBearing))},
+ {"nativeSetBearing", "(JDDD)V",
+ reinterpret_cast<void *>(
+ static_cast<void JNICALL (*)(JNIEnv *, jobject, jlong, jdouble, jdouble, jdouble)>(
+ &nativeSetBearing))},
+ {"nativeGetBearing", "(J)D", reinterpret_cast<void *>(&nativeGetBearing)},
+ {"nativeResetNorth", "(J)V", reinterpret_cast<void *>(&nativeResetNorth)},
+ {"nativeAddMarker", "(JLcom/mapbox/mapboxsdk/annotations/Marker;)J",
+ reinterpret_cast<void *>(&nativeAddMarker)},
+ {"nativeAddMarkers", "(JLjava/util/List;)[J",
+ reinterpret_cast<void *>(&nativeAddMarkers)},
+ {"nativeAddPolyline", "(JLcom/mapbox/mapboxsdk/annotations/Polyline;)J",
+ reinterpret_cast<void *>(&nativeAddPolyline)},
+ {"nativeAddPolygon", "(JLcom/mapbox/mapboxsdk/annotations/Polygon;)J",
+ reinterpret_cast<void *>(&nativeAddPolygon)},
+ {"nativeAddPolygons", "(JLjava/util/List;)[J",
+ reinterpret_cast<void *>(&nativeAddPolygons)},
+ {"nativeRemoveAnnotation", "(JJ)V", reinterpret_cast<void *>(&nativeRemoveAnnotation)},
+ {"nativeRemoveAnnotations", "(J[J)V", reinterpret_cast<void *>(&nativeRemoveAnnotations)},
+ {"nativeGetAnnotationsInBounds", "(JLcom/mapbox/mapboxsdk/geometry/BoundingBox;)[J",
+ reinterpret_cast<void *>(&nativeGetAnnotationsInBounds)},
+ {"nativeSetSprite", "(JLjava/lang/String;IIF[B)V", reinterpret_cast<void *>(&nativeSetSprite)},
+ {"nativeSetVisibleCoordinateBounds", "(J[Lcom/mapbox/mapboxsdk/geometry/LatLng;Landroid/graphics/RectF;DJ)V",
+ reinterpret_cast<void *>(&nativeSetVisibleCoordinateBounds)},
+ {"nativeOnLowMemory", "(J)V", reinterpret_cast<void *>(&nativeOnLowMemory)},
+ {"nativeSetDebug", "(JZ)V", reinterpret_cast<void *>(&nativeSetDebug)},
+ {"nativeToggleDebug", "(J)V", reinterpret_cast<void *>(&nativeToggleDebug)},
+ {"nativeGetDebug", "(J)Z", reinterpret_cast<void *>(&nativeGetDebug)},
+ {"nativeSetCollisionDebug", "(JZ)V", reinterpret_cast<void *>(&nativeSetCollisionDebug)},
+ {"nativeToggleCollisionDebug", "(J)V", reinterpret_cast<void *>(&nativeToggleCollisionDebug)},
+ {"nativeGetCollisionDebug", "(J)Z", reinterpret_cast<void *>(&nativeGetCollisionDebug)},
+ {"nativeIsFullyLoaded", "(J)Z", reinterpret_cast<void *>(&nativeIsFullyLoaded)},
+ {"nativeSetReachability", "(JZ)V", reinterpret_cast<void *>(&nativeSetReachability)},
+ {"nativeGetMetersPerPixelAtLatitude", "(JDD)D", reinterpret_cast<void *>(&nativeGetMetersPerPixelAtLatitude)},
+ {"nativeProjectedMetersForLatLng",
+ "(JLcom/mapbox/mapboxsdk/geometry/LatLng;)Lcom/mapbox/mapboxsdk/geometry/ProjectedMeters;",
+ reinterpret_cast<void *>(&nativeProjectedMetersForLatLng)},
+ {"nativeLatLngForProjectedMeters",
+ "(JLcom/mapbox/mapboxsdk/geometry/ProjectedMeters;)Lcom/mapbox/mapboxsdk/geometry/LatLng;",
+ reinterpret_cast<void *>(&nativeLatLngForProjectedMeters)},
+ {"nativePixelForLatLng", "(JLcom/mapbox/mapboxsdk/geometry/LatLng;)Landroid/graphics/PointF;",
+ reinterpret_cast<void *>(&nativePixelForLatLng)},
+ {"nativeLatLngForPixel", "(JLandroid/graphics/PointF;)Lcom/mapbox/mapboxsdk/geometry/LatLng;",
+ reinterpret_cast<void *>(&nativeLatLngForPixel)},
+ {"nativeGetTopOffsetPixelsForAnnotationSymbol", "(JLjava/lang/String;)D",
+ reinterpret_cast<void *>(&nativeGetTopOffsetPixelsForAnnotationSymbol)},
+ };
+
+ if (env->RegisterNatives(nativeMapViewClass, methods.data(), methods.size()) < 0) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ latLngClass = reinterpret_cast<jclass>(env->NewGlobalRef(latLngClass));
+ if (latLngClass == nullptr) {
+ env->ExceptionDescribe();
+ return JNI_ERR;
+ }
+
+ latLngZoomClass = reinterpret_cast<jclass>(env->NewGlobalRef(latLngZoomClass));
+ if (latLngZoomClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ return JNI_ERR;
+ }
+
+ bboxClass = reinterpret_cast<jclass>(env->NewGlobalRef(bboxClass));
+ if (bboxClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ return JNI_ERR;
+ }
+
+ spriteClass = reinterpret_cast<jclass>(env->NewGlobalRef(spriteClass));
+ if (spriteClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ return JNI_ERR;
+ }
+
+ markerClass = reinterpret_cast<jclass>(env->NewGlobalRef(markerClass));
+ if (markerClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(spriteClass);
+ return JNI_ERR;
+ }
+
+ polylineClass = reinterpret_cast<jclass>(env->NewGlobalRef(polylineClass));
+ if (polylineClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(spriteClass);
+ env->DeleteGlobalRef(markerClass);
+ return JNI_ERR;
+ }
+
+ polygonClass = reinterpret_cast<jclass>(env->NewGlobalRef(polygonClass));
+ if (polygonClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(spriteClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ return JNI_ERR;
+ }
+
+ runtimeExceptionClass = reinterpret_cast<jclass>(env->NewGlobalRef(runtimeExceptionClass));
+ if (runtimeExceptionClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(spriteClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ return JNI_ERR;
+ }
+
+ nullPointerExceptionClass =
+ reinterpret_cast<jclass>(env->NewGlobalRef(nullPointerExceptionClass));
+ if (nullPointerExceptionClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(spriteClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ return JNI_ERR;
+ }
+
+ arrayListClass = reinterpret_cast<jclass>(env->NewGlobalRef(arrayListClass));
+ if (arrayListClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(spriteClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ return JNI_ERR;
+ }
+
+ projectedMetersClass = reinterpret_cast<jclass>(env->NewGlobalRef(projectedMetersClass));
+ if (projectedMetersClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(spriteClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ return JNI_ERR;
+ }
+
+ pointFClass = reinterpret_cast<jclass>(env->NewGlobalRef(pointFClass));
+ if (pointFClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(spriteClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ return JNI_ERR;
+ }
+
+ rectFClass = reinterpret_cast<jclass>(env->NewGlobalRef(rectFClass));
+ if (rectFClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ return JNI_ERR;
+ }
+
+ httpContextClass = reinterpret_cast<jclass>(env->NewGlobalRef(httpContextClass));
+ if (httpContextClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(spriteClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ }
+
+ httpRequestClass = reinterpret_cast<jclass>(env->NewGlobalRef(httpRequestClass));
+ if (httpRequestClass == nullptr) {
+ env->ExceptionDescribe();
+ env->DeleteGlobalRef(latLngClass);
+ env->DeleteGlobalRef(latLngZoomClass);
+ env->DeleteGlobalRef(bboxClass);
+ env->DeleteGlobalRef(spriteClass);
+ env->DeleteGlobalRef(markerClass);
+ env->DeleteGlobalRef(polylineClass);
+ env->DeleteGlobalRef(polygonClass);
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ env->DeleteGlobalRef(arrayListClass);
+ env->DeleteGlobalRef(projectedMetersClass);
+ env->DeleteGlobalRef(pointFClass);
+ env->DeleteGlobalRef(rectFClass);
+ env->DeleteGlobalRef(httpContextClass);
+ }
+
+ char release[PROP_VALUE_MAX] = "";
+ __system_property_get("ro.build.version.release", release);
+ androidRelease = std::string(release);
+
+ return JNI_VERSION_1_6;
+}
+
+extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "JNI_OnUnload");
+
+ theJVM = vm;
+
+ JNIEnv *env = nullptr;
+ jint ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
+ if (ret != JNI_OK) {
+ mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
+ throw new std::runtime_error("GetEnv() failed");
+ }
+
+ env->DeleteGlobalRef(latLngClass);
+ latLngClass = nullptr;
+ latLngConstructorId = nullptr;
+ latLngLongitudeId = nullptr;
+ latLngLatitudeId = nullptr;
+
+ env->DeleteGlobalRef(latLngZoomClass);
+ latLngZoomClass = nullptr;
+ latLngZoomConstructorId = nullptr;
+ latLngZoomLongitudeId = nullptr;
+ latLngZoomLatitudeId = nullptr;
+ latLngZoomZoomId = nullptr;
+
+ env->DeleteGlobalRef(bboxClass);
+ bboxClass = nullptr;
+ bboxConstructorId = nullptr;
+ bboxLatNorthId = nullptr;
+ bboxLatSouthId = nullptr;
+ bboxLonEastId = nullptr;
+ bboxLonWestId = nullptr;
+
+ env->DeleteGlobalRef(spriteClass);
+ spriteClass = nullptr;
+ spriteIdId = nullptr;
+
+ env->DeleteGlobalRef(markerClass);
+ markerClass = nullptr;
+ markerPositionId = nullptr;
+ markerIconId = nullptr;
+
+ env->DeleteGlobalRef(polylineClass);
+ polylineClass = nullptr;
+ polylineAlphaId = nullptr;
+ polylineColorId = nullptr;
+ polylineWidthId = nullptr;
+ polylinePointsId = nullptr;
+
+ env->DeleteGlobalRef(polygonClass);
+ polygonClass = nullptr;
+ polygonAlphaId = nullptr;
+ polygonFillColorId = nullptr;
+ polygonStrokeColorId = nullptr;
+ polygonPointsId = nullptr;
+
+ onInvalidateId = nullptr;
+ onMapChangedId = nullptr;
+ onFpsChangedId = nullptr;
+
+ env->DeleteGlobalRef(runtimeExceptionClass);
+ runtimeExceptionClass = nullptr;
+
+ env->DeleteGlobalRef(nullPointerExceptionClass);
+ nullPointerExceptionClass = nullptr;
+
+ listToArrayId = nullptr;
+
+ env->DeleteGlobalRef(arrayListClass);
+ arrayListClass = nullptr;
+ arrayListConstructorId = nullptr;
+ arrayListAddId = nullptr;
+
+ env->DeleteGlobalRef(projectedMetersClass);
+ projectedMetersClass = nullptr;
+ projectedMetersConstructorId = nullptr;
+ projectedMetersNorthingId = nullptr;
+ projectedMetersEastingId = nullptr;
+
+ env->DeleteGlobalRef(pointFClass);
+ pointFClass = nullptr;
+ pointFConstructorId = nullptr;
+ pointFXId = nullptr;
+ pointFYId = nullptr;
+
+ env->DeleteGlobalRef(rectFClass);
+ rectFClass = nullptr;
+ rectFConstructorId = nullptr;
+ rectFLeftId = nullptr;
+ rectFTopId = nullptr;
+ rectFRightId = nullptr;
+ rectFBottomId = nullptr;
+
+ env->DeleteGlobalRef(httpContextClass);
+ httpContextGetInstanceId = nullptr;
+ httpContextCreateRequestId = nullptr;
+
+ env->DeleteGlobalRef(httpRequestClass);
+ httpRequestStartId = nullptr;
+ httpRequestCancelId = nullptr;
+
+ theJVM = nullptr;
+}
+}
diff --git a/platform/android/mapboxgl-app.gypi b/platform/android/mapboxgl-app.gypi
new file mode 100644
index 0000000000..82ed377d31
--- /dev/null
+++ b/platform/android/mapboxgl-app.gypi
@@ -0,0 +1,101 @@
+{
+ 'includes': [
+ '../../gyp/common.gypi',
+ ],
+ 'targets': [
+ { 'target_name': 'android-lib',
+ 'product_name': 'mapbox-gl',
+ 'type': 'shared_library',
+ 'hard_dependency': 1,
+
+ 'dependencies': [
+ '../mbgl.gyp:core',
+ '../mbgl.gyp:platform-<(platform_lib)',
+ '../mbgl.gyp:http-<(http_lib)',
+ '../mbgl.gyp:asset-<(asset_lib)',
+ '../mbgl.gyp:cache-<(cache_lib)',
+ ],
+
+ 'sources': [
+ './native_map_view.cpp',
+ './jni.cpp',
+ ],
+
+ 'cflags_cc': [
+ '<@(boost_cflags)',
+ '<@(variant_cflags)',
+ ],
+ 'libraries': [
+ '<@(openssl_static_libs)',
+ '<@(libcurl_static_libs)',
+ '<@(libpng_static_libs)',
+ '<@(jpeg_static_libs)',
+ '<@(sqlite_static_libs)',
+ '<@(libuv_static_libs)',
+ '<@(nunicode_static_libs)',
+ '<@(libzip_static_libs)',
+ ],
+ 'variables': {
+ 'ldflags': [
+ '-llog',
+ '-landroid',
+ '-lEGL',
+ '-lGLESv2',
+ '-lstdc++',
+ '-latomic',
+ '<@(libpng_ldflags)',
+ '<@(jpeg_ldflags)',
+ '<@(sqlite_ldflags)',
+ '<@(openssl_ldflags)',
+ '<@(libcurl_ldflags)',
+ '<@(zlib_ldflags)',
+ '<@(libzip_ldflags)',
+ ],
+ },
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [ '<@(ldflags)' ],
+ }
+ }, {
+ 'libraries': [ '<@(ldflags)' ],
+ }]
+ ],
+ },
+
+
+ { 'target_name': 'androidapp',
+ 'type': 'none',
+ 'hard_dependency': 1,
+
+ 'variables': {
+ 'pwd': '<!(pwd)',
+ },
+
+ 'copies': [
+ {
+ 'files': [
+ '../../common/ca-bundle.crt',
+ '../../styles/styles'
+ ],
+ 'destination': '<(pwd)/../android/MapboxGLAndroidSDK/src/main/assets'
+ },
+ {
+ 'files': [
+ '<(PRODUCT_DIR)/obj.target'
+ ],
+ 'destination': '<(pwd)/../android/MapboxGLAndroidSDK/src/main'
+ },
+ ],
+
+ 'actions': [
+ {
+ 'action_name': 'Strip dynamic library',
+ 'inputs': [ '<(PRODUCT_DIR)/lib.target/libmapbox-gl.so' ],
+ 'outputs': [ '<(pwd)/../android/MapboxGLAndroidSDK/src/main/jniLibs/$(JNIDIR)/libmapbox-gl.so' ],
+ 'action': [ '$(STRIP)', '<@(_inputs)', '-o', '<@(_outputs)' ]
+ },
+ ],
+ },
+ ],
+}
diff --git a/platform/android/native_map_view.cpp b/platform/android/native_map_view.cpp
new file mode 100644
index 0000000000..9c4d4b1d10
--- /dev/null
+++ b/platform/android/native_map_view.cpp
@@ -0,0 +1,745 @@
+#include <cstdlib>
+#include <ctime>
+#include <cassert>
+#include <memory>
+#include <list>
+#include <tuple>
+
+#include <sys/system_properties.h>
+
+#include <GLES2/gl2.h>
+
+#include <mbgl/android/native_map_view.hpp>
+#include <mbgl/android/jni.hpp>
+#include <mbgl/platform/platform.hpp>
+#include <mbgl/platform/event.hpp>
+#include <mbgl/platform/log.hpp>
+#include <mbgl/platform/gl.hpp>
+#include <mbgl/util/constants.hpp>
+
+namespace mbgl {
+namespace android {
+
+void log_egl_string(EGLDisplay display, EGLint name, const char *label) {
+ const char *str = eglQueryString(display, name);
+ if (str == nullptr) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "eglQueryString(%d) returned error %d", name,
+ eglGetError());
+ throw new std::runtime_error("eglQueryString() failed");
+ } else {
+ char buf[513];
+ for (int len = std::strlen(str), pos = 0; len > 0; len -= 512, pos += 512) {
+ strncpy(buf, str + pos, 512);
+ buf[512] = 0;
+ mbgl::Log::Info(mbgl::Event::OpenGL, "EGL %s: %s", label, buf);
+ }
+ }
+}
+
+void log_gl_string(GLenum name, const char *label) {
+ const GLubyte *str = glGetString(name);
+ if (str == nullptr) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "glGetString(%d) returned error %d", name,
+ glGetError());
+ throw new std::runtime_error("glGetString() failed");
+ } else {
+ char buf[513];
+ for (int len = std::strlen(reinterpret_cast<const char *>(str)), pos = 0; len > 0;
+ len -= 512, pos += 512) {
+ strncpy(buf, reinterpret_cast<const char *>(str) + pos, 512);
+ buf[512] = 0;
+ mbgl::Log::Info(mbgl::Event::OpenGL, "GL %s: %s", label, buf);
+ }
+ }
+}
+
+NativeMapView::NativeMapView(JNIEnv *env, jobject obj_, float pixelRatio_, int availableProcessors_, size_t totalMemory_)
+ : mbgl::View(*this),
+ pixelRatio(pixelRatio_),
+ availableProcessors(availableProcessors_),
+ totalMemory(totalMemory_) {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::NativeMapView");
+
+ assert(env != nullptr);
+ assert(obj_ != nullptr);
+
+ if (env->GetJavaVM(&vm) < 0) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ obj = env->NewWeakGlobalRef(obj_);
+ if (obj == nullptr) {
+ env->ExceptionDescribe();
+ return;
+ }
+
+ fileCache = mbgl::SharedSQLiteCache::get(mbgl::android::cachePath + "/mbgl-cache.db");
+ fileSource = std::make_unique<mbgl::DefaultFileSource>(fileCache.get());
+ map = std::make_unique<mbgl::Map>(*this, *fileSource, MapMode::Continuous);
+
+ 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->getWidth()) / mbgl::util::tileSize) *
+ (static_cast<float>(map->getHeight()) / mbgl::util::tileSize);
+
+ size_t cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f;
+
+ map->setSourceTileCacheSize(cacheSize);
+
+ map->pause();
+}
+
+NativeMapView::~NativeMapView() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::~NativeMapView");
+ terminateContext();
+ destroySurface();
+ terminateDisplay();
+
+ assert(vm != nullptr);
+ assert(obj != nullptr);
+
+ map.reset();
+ fileSource.reset();
+ fileCache.reset();
+
+ jint ret;
+ JNIEnv *env = nullptr;
+ ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
+ if (ret == JNI_OK) {
+ env->DeleteWeakGlobalRef(obj);
+ } else {
+ mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
+ throw new std::runtime_error("GetEnv() failed");
+ }
+ obj = nullptr;
+ vm = nullptr;
+}
+
+float NativeMapView::getPixelRatio() const {
+ return pixelRatio;
+}
+
+std::array<uint16_t, 2> NativeMapView::getSize() const {
+ return {{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }};
+}
+
+std::array<uint16_t, 2> NativeMapView::getFramebufferSize() const {
+ return {{ static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) }};
+}
+
+void NativeMapView::activate() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::activate");
+
+ assert(vm != nullptr);
+
+ renderDetach = attach_jni_thread(vm, &renderEnv, "Map Thread");
+
+ 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 new std::runtime_error("eglMakeCurrent() failed");
+ }
+
+ if (!eglSwapInterval(display, 0)) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapInterval() returned error %d", eglGetError());
+ throw new std::runtime_error("eglSwapInterval() failed");
+ }
+ } else {
+ mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready");
+ }
+}
+
+void NativeMapView::deactivate() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::deactivate");
+
+ assert(vm != nullptr);
+
+ 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 new std::runtime_error("eglMakeCurrent() failed");
+ }
+ } else {
+ mbgl::Log::Info(mbgl::Event::Android, "Not deactivating as we are not ready");
+ }
+
+ detach_jni_thread(vm, &renderEnv, renderDetach);
+}
+
+void NativeMapView::invalidate() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::invalidate()");
+
+ assert(vm != nullptr);
+ assert(obj != nullptr);
+
+ JNIEnv *env = nullptr;
+ bool detach = attach_jni_thread(vm, &env, "NativeMapView::invalidate()");
+
+ env->CallVoidMethod(obj, onInvalidateId);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ }
+
+ detach_jni_thread(vm, &env, detach);
+}
+
+void NativeMapView::beforeRender() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::beforeRender()");
+ // no-op
+}
+
+void NativeMapView::afterRender() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::afterRender()");
+
+ assert(vm != nullptr);
+ assert(obj != nullptr);
+
+ 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 new std::runtime_error("eglSwapBuffers() failed");
+ }
+
+ updateFps();
+ } else {
+ mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready");
+ }
+}
+
+void NativeMapView::notify() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notify()");
+ // noop
+}
+
+mbgl::Map &NativeMapView::getMap() { return *map; }
+
+mbgl::DefaultFileSource &NativeMapView::getFileSource() { return *fileSource; }
+
+bool NativeMapView::inEmulator() {
+ // Detect if we are in emulator
+ char prop[PROP_VALUE_MAX];
+ __system_property_get("ro.kernel.qemu", prop);
+ return strtol(prop, nullptr, 0) == 1;
+}
+
+void NativeMapView::initializeDisplay() {
+ mbgl::Log::Debug(mbgl::Event::Android, "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 new 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 new 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 new std::runtime_error("EGL version is too low");
+ }
+
+ log_egl_string(display, EGL_VENDOR, "Vendor");
+ log_egl_string(display, EGL_VERSION, "Version");
+ log_egl_string(display, EGL_CLIENT_APIS, "Client APIs");
+ log_egl_string(display, EGL_EXTENSIONS, "Client Extensions");
+
+ // Detect if we are in emulator
+ if (inEmulator()) {
+ mbgl::Log::Warning(mbgl::Event::Android, "In emulator! Enabling hacks :-(");
+ }
+
+ // 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, // Ugly hack
+ (inEmulator() ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER, // Ugly hack
+ EGL_NONE};
+ EGLint numConfigs;
+ if (!eglChooseConfig(display, configAttribs, nullptr, 0, &numConfigs)) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d",
+ eglGetError());
+ throw new std::runtime_error("eglChooseConfig() failed");
+ }
+ if (numConfigs < 1) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs.");
+ throw new 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 new std::runtime_error("eglChooseConfig() failed");
+ }
+
+ config = chooseConfig(configs.get(), numConfigs);
+ if (config == nullptr) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen");
+ throw new 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 new std::runtime_error("eglGetConfigAttrib() failed");
+ }
+ mbgl::Log::Info(mbgl::Event::OpenGL, "Chosen window format is %d", format);
+}
+
+void NativeMapView::terminateDisplay() {
+ mbgl::Log::Debug(mbgl::Event::Android, "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 new 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 new std::runtime_error("eglMakeCurrent() failed");
+ }
+
+ if (!eglTerminate(display)) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d",
+ eglGetError());
+ throw new std::runtime_error("eglTerminate() failed");
+ }
+ }
+
+ display = EGL_NO_DISPLAY;
+ config = nullptr;
+ format = -1;
+}
+
+void NativeMapView::initializeContext() {
+ mbgl::Log::Debug(mbgl::Event::Android, "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 new std::runtime_error("eglCreateContext() failed");
+ }
+}
+
+void NativeMapView::terminateContext() {
+ mbgl::Log::Debug(mbgl::Event::Android, "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 new 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 new std::runtime_error("eglDestroyContext() failed");
+ }
+ }
+ }
+
+ context = EGL_NO_CONTEXT;
+}
+
+void NativeMapView::createSurface(ANativeWindow *window_) {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::createSurface");
+
+ 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 new std::runtime_error("eglCreateWindowSurface() failed");
+ }
+
+ if (!firstTime) {
+ firstTime = true;
+
+ if (!eglMakeCurrent(display, surface, surface, context)) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
+ eglGetError());
+ throw new std::runtime_error("eglMakeCurrent() failed");
+ }
+
+ log_gl_string(GL_VENDOR, "Vendor");
+ log_gl_string(GL_RENDERER, "Renderer");
+ log_gl_string(GL_VERSION, "Version");
+ if (!inEmulator()) {
+ log_gl_string(GL_SHADING_LANGUAGE_VERSION,
+ "SL Version"); // In the emulator this returns NULL with error code 0?
+ // https://code.google.com/p/android/issues/detail?id=78977
+ }
+
+ log_gl_string(GL_EXTENSIONS, "Extensions");
+ mbgl::gl::InitializeExtensions([] (const char * name) {
+ return reinterpret_cast<mbgl::gl::glProc>(eglGetProcAddress(name));
+ });
+
+ 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 new std::runtime_error("eglMakeCurrent() failed");
+ }
+ }
+
+ resume();
+}
+
+void NativeMapView::destroySurface() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::destroySurface");
+
+ pause();
+
+ if (surface != EGL_NO_SURFACE) {
+ if (!eglDestroySurface(display, surface)) {
+ mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
+ eglGetError());
+ throw new std::runtime_error("eglDestroySurface() failed");
+ }
+ }
+
+ surface = EGL_NO_SURFACE;
+
+ if (window != nullptr) {
+ ANativeWindow_release(window);
+ window = nullptr;
+ }
+}
+
+// Speed
+/*
+typedef enum {
+ Format16Bit = 0,
+ Format32BitNoAlpha = 1,
+ Format32BitAlpha = 2,
+ Format24Bit = 3,
+ Unknown = 4
+} BufferFormat;
+
+typedef enum {
+ Format16Depth8Stencil = 0,
+ Format24Depth8Stencil = 1,
+} DepthStencilFormat;
+*/
+
+// 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) {
+ mbgl::Log::Info(mbgl::Event::OpenGL, "Found %d configs", numConfigs);
+
+ // Create a list of configs that pass our filters
+ std::list<ConfigProperties> configList;
+ for (int i = 0; i < numConfigs; i++) {
+ mbgl::Log::Info(mbgl::Event::OpenGL, "Config %d:", 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 new 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 new 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 new 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 new 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 new 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 new 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 new 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 new 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 new 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 new 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 new 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 new std::runtime_error("eglGetConfigAttrib() failed");
+ }
+
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Caveat: %d", caveat);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Conformant: %d", conformant);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Color: %d", bits);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Red: %d", red);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Green: %d", green);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Blue: %d", blue);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Alpha: %d", alpha);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Alpha mask: %d", alphaMask);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Depth: %d", depth);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Stencil: %d", stencil);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Sample buffers: %d", sampleBuffers);
+ mbgl::Log::Info(mbgl::Event::OpenGL, "...Samples: %d", samples);
+
+ 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());
+ int configNum = std::get<4>(configList.front());
+ EGLConfig configId = std::get<5>(configList.front());
+
+ mbgl::Log::Info(mbgl::Event::OpenGL, "Chosen config is %d", configNum);
+
+ 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;
+}
+
+void NativeMapView::pause() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::pause");
+
+ if ((display != EGL_NO_DISPLAY) && (context != EGL_NO_CONTEXT)) {
+ map->pause();
+ }
+}
+
+void NativeMapView::resume() {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::resume");
+
+ assert(display != EGL_NO_DISPLAY);
+ assert(context != EGL_NO_CONTEXT);
+
+ if (surface != EGL_NO_SURFACE) {
+ map->resume();
+ } else {
+ mbgl::Log::Debug(mbgl::Event::Android, "Not resuming because we are not ready");
+ }
+}
+
+void NativeMapView::notifyMapChange(mbgl::MapChange change) {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notifyMapChange()");
+
+ assert(vm != nullptr);
+ assert(obj != nullptr);
+
+ JNIEnv *env = nullptr;
+ bool detach = attach_jni_thread(vm, &env, "NativeMapView::notifyMapChange()");
+
+ env->CallVoidMethod(obj, onMapChangedId, change);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ }
+
+ detach_jni_thread(vm, &env, detach);
+}
+
+void NativeMapView::enableFps(bool enable) {
+ mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::enableFps()");
+
+ fpsEnabled = enable;
+}
+
+void NativeMapView::updateFps() {
+ mbgl::Log::Debug(mbgl::Event::Android, "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::Debug(mbgl::Event::Render, "FPS: %4.2f", fps);
+ timeElapsed = currentTime;
+ frames = 0;
+ }
+
+ assert(vm != nullptr);
+ assert(obj != nullptr);
+
+ JNIEnv *env = nullptr;
+ bool detach = attach_jni_thread(vm, &env, "NativeMapView::updateFps()");
+
+ env->CallVoidMethod(obj, onFpsChangedId, fps);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ }
+
+ detach_jni_thread(vm, &env, detach);
+}
+
+void NativeMapView::resizeView(int w, int h) {
+ width = w;
+ height = h;
+ map->update(mbgl::Update::Dimensions);
+}
+
+void NativeMapView::resizeFramebuffer(int w, int h) {
+ fbWidth = w;
+ fbHeight = h;
+ map->update(mbgl::Update::Repaint);
+}
+
+}
+}