diff options
author | Leith Bade <leith@mapbox.com> | 2015-10-25 01:07:16 +1100 |
---|---|---|
committer | Leith Bade <leith@mapbox.com> | 2015-10-25 01:07:16 +1100 |
commit | 39911e581c7bc82116c1c70608f246924de33b85 (patch) | |
tree | a66716809ec61f742f1d1fbec5297a68504adfd2 /platform/android | |
parent | d2e01290ebc1f9b894b4b2d05e0e7c22d876e40b (diff) | |
download | qtlocation-mapboxgl-39911e581c7bc82116c1c70608f246924de33b85.tar.gz |
[android] Move android files
Fixes #2032
Diffstat (limited to 'platform/android')
-rw-r--r-- | platform/android/jni.cpp | 2199 | ||||
-rw-r--r-- | platform/android/mapboxgl-app.gypi | 101 | ||||
-rw-r--r-- | platform/android/native_map_view.cpp | 745 |
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); +} + +} +} |