#include #include #include #include #include #include #include #include #include "jni.hpp" #include "java_types.hpp" #include "native_map_view.hpp" #include "connectivity_listener.hpp" #include "style/layers/layers.hpp" #include "style/sources/sources.hpp" #include "conversion/conversion.hpp" #include "conversion/collection.hpp" #include "geometry/conversion/feature.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma clang diagnostic ignored "-Wunused-parameter" namespace mbgl { namespace android { void RegisterNativeHTTPRequest(JNIEnv&); JavaVM* theJVM; std::string cachePath; std::string dataPath; std::string apkPath; std::string androidRelease; jni::jmethodID* onInvalidateId = nullptr; jni::jmethodID* onMapChangedId = nullptr; jni::jmethodID* onFpsChangedId = nullptr; jni::jmethodID* onSnapshotReadyId = nullptr; jni::jclass* latLngClass = nullptr; jni::jmethodID* latLngConstructorId = nullptr; jni::jfieldID* latLngLatitudeId = nullptr; jni::jfieldID* latLngLongitudeId = nullptr; jni::jclass* latLngBoundsClass = nullptr; jni::jmethodID* latLngBoundsConstructorId = nullptr; jni::jfieldID* latLngBoundsLatNorthId = nullptr; jni::jfieldID* latLngBoundsLatSouthId = nullptr; jni::jfieldID* latLngBoundsLonEastId = nullptr; jni::jfieldID* latLngBoundsLonWestId = nullptr; jni::jclass* iconClass = nullptr; jni::jfieldID* iconIdId = nullptr; jni::jclass* markerClass = nullptr; jni::jfieldID* markerPositionId = nullptr; jni::jfieldID* markerIconId = nullptr; jni::jfieldID* markerIdId = nullptr; jni::jclass* polylineClass = nullptr; jni::jfieldID* polylineAlphaId = nullptr; jni::jfieldID* polylineColorId = nullptr; jni::jfieldID* polylineWidthId = nullptr; jni::jfieldID* polylinePointsId = nullptr; jni::jclass* polygonClass = nullptr; jni::jfieldID* polygonAlphaId = nullptr; jni::jfieldID* polygonFillColorId = nullptr; jni::jfieldID* polygonStrokeColorId = nullptr; jni::jfieldID* polygonPointsId = nullptr; jni::jmethodID* listToArrayId = nullptr; jni::jclass* arrayListClass = nullptr; jni::jmethodID* arrayListConstructorId = nullptr; jni::jmethodID* arrayListAddId = nullptr; jni::jclass* projectedMetersClass = nullptr; jni::jmethodID* projectedMetersConstructorId = nullptr; jni::jfieldID* projectedMetersNorthingId = nullptr; jni::jfieldID* projectedMetersEastingId = nullptr; jni::jclass* pointFClass = nullptr; jni::jmethodID* pointFConstructorId = nullptr; jni::jfieldID* pointFXId = nullptr; jni::jfieldID* pointFYId = nullptr; jni::jclass* rectFClass = nullptr; jni::jmethodID* rectFConstructorId = nullptr; jni::jfieldID* rectFLeftId = nullptr; jni::jfieldID* rectFTopId = nullptr; jni::jfieldID* rectFRightId = nullptr; jni::jfieldID* rectFBottomId = nullptr; // Offline declarations start jni::jfieldID* offlineManagerClassPtrId = nullptr; jni::jmethodID* listOnListMethodId = nullptr; jni::jmethodID* listOnErrorMethodId = nullptr; jni::jclass* offlineRegionClass = nullptr; jni::jmethodID* offlineRegionConstructorId = nullptr; jni::jfieldID* offlineRegionOfflineManagerId = nullptr; jni::jfieldID* offlineRegionIdId = nullptr; jni::jfieldID* offlineRegionDefinitionId = nullptr; jni::jfieldID* offlineRegionMetadataId = nullptr; jni::jfieldID* offlineRegionPtrId = nullptr; jni::jclass* offlineRegionDefinitionClass = nullptr; jni::jmethodID* offlineRegionDefinitionConstructorId = nullptr; jni::jfieldID* offlineRegionDefinitionStyleURLId = nullptr; jni::jfieldID* offlineRegionDefinitionBoundsId = nullptr; jni::jfieldID* offlineRegionDefinitionMinZoomId = nullptr; jni::jfieldID* offlineRegionDefinitionMaxZoomId = nullptr; jni::jfieldID* offlineRegionDefinitionPixelRatioId = nullptr; jni::jmethodID* createOnCreateMethodId = nullptr; jni::jmethodID* createOnErrorMethodId = nullptr; jni::jmethodID* updateMetadataOnUpdateMethodId = nullptr; jni::jmethodID* updateMetadataOnErrorMethodId = nullptr; jni::jmethodID* offlineRegionObserveronStatusChangedId = nullptr; jni::jmethodID* offlineRegionObserveronErrorId = nullptr; jni::jmethodID* offlineRegionObserveronLimitId = nullptr; jni::jclass* offlineRegionStatusClass = nullptr; jni::jmethodID* offlineRegionStatusConstructorId = nullptr; jni::jfieldID* offlineRegionStatusDownloadStateId = nullptr; jni::jfieldID* offlineRegionStatusCompletedResourceCountId = nullptr; jni::jfieldID* offlineRegionStatusCompletedResourceSizeId = nullptr; jni::jfieldID* offlineRegionStatusCompletedTileCountId = nullptr; jni::jfieldID* offlineRegionStatusCompletedTileSizeId = nullptr; jni::jfieldID* offlineRegionStatusRequiredResourceCountId = nullptr; jni::jfieldID* offlineRegionStatusRequiredResourceCountIsPreciseId = nullptr; jni::jclass* offlineRegionErrorClass = nullptr; jni::jmethodID* offlineRegionErrorConstructorId = nullptr; jni::jfieldID* offlineRegionErrorReasonId = nullptr; jni::jfieldID* offlineRegionErrorMessageId = nullptr; jni::jmethodID* offlineRegionStatusOnStatusId = nullptr; jni::jmethodID* offlineRegionStatusOnErrorId = nullptr; jni::jmethodID* offlineRegionDeleteOnDeleteId = nullptr; jni::jmethodID* offlineRegionDeleteOnErrorId = nullptr; // Offline declarations end 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(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 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 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 std::runtime_error("DetachCurrentThread() failed"); } } *env = nullptr; } std::string std_string_from_jstring(JNIEnv *env, jni::jstring* jstr) { return jni::Make(*env, jni::String(jstr)); } jni::jstring* std_string_to_jstring(JNIEnv *env, std::string str) { return jni::Make(*env, str).Get(); } std::vector std_vector_string_from_jobject(JNIEnv *env, jni::jobject* jlist) { std::vector vector; jni::NullCheck(*env, jlist); jni::jarray* jarray = reinterpret_cast*>(jni::CallMethod(*env, jlist, *listToArrayId)); jni::NullCheck(*env, jarray); std::size_t len = jni::GetArrayLength(*env, *jarray); for (std::size_t i = 0; i < len; i++) { jni::jstring* jstr = reinterpret_cast(jni::GetObjectArrayElement(*env, *jarray, i)); vector.push_back(std_string_from_jstring(env, jstr)); } return vector; } jni::jobject* std_vector_string_to_jobject(JNIEnv *env, std::vector vector) { jni::jobject* jlist = &jni::NewObject(*env, *arrayListClass, *arrayListConstructorId); for (const auto& str : vector) { jni::CallMethod(*env, jlist, *arrayListAddId, std_string_to_jstring(env, str)); } return jlist; } jni::jarray* std_vector_uint_to_jobject(JNIEnv *env, const std::vector& vector) { jni::jarray& jarray = jni::NewArray(*env, vector.size()); std::vector v; v.reserve(vector.size()); std::move(vector.begin(), vector.end(), std::back_inserter(v)); jni::SetArrayRegion(*env, jarray, 0, v); return &jarray; } static std::vector metadata_from_java(JNIEnv* env, jni::jarray& j) { std::size_t length = jni::GetArrayLength(*env, j); std::vector c; c.resize(length); jni::GetArrayRegion(*env, j, 0, length, reinterpret_cast(c.data())); return c; } static jni::jarray* metadata_from_native(JNIEnv* env, const std::vector& c) { std::size_t length = static_cast(c.size()); jni::jarray& j = jni::NewArray(*env, length); jni::SetArrayRegion(*env, j, 0, c.size(), reinterpret_cast(c.data())); return &j; } static mbgl::LatLngBounds latlngbounds_from_java(JNIEnv *env, jni::jobject* latLngBounds) { jdouble swLat = jni::GetField(*env, latLngBounds, *latLngBoundsLatSouthId); jdouble swLon = jni::GetField(*env, latLngBounds, *latLngBoundsLonWestId); jdouble neLat = jni::GetField(*env, latLngBounds, *latLngBoundsLatNorthId); jdouble neLon = jni::GetField(*env, latLngBounds, *latLngBoundsLonEastId); return mbgl::LatLngBounds::hull({ swLat, swLon }, { neLat, neLon }); } static jni::jobject* latlngbounds_from_native(JNIEnv *env, mbgl::LatLngBounds bounds) { double northLatitude = bounds.north(); double eastLongitude = bounds.east(); double southLatitude = bounds.south(); double westLongitude = bounds.west(); jni::jobject* jbounds = &jni::NewObject(*env, *latLngBoundsClass, *latLngBoundsConstructorId, northLatitude, eastLongitude, southLatitude, westLongitude); return jbounds; } } } namespace { using namespace mbgl::android; using DebugOptions = mbgl::MapDebugOptions; jlong nativeCreate(JNIEnv *env, jni::jobject* obj, jni::jstring* cachePath_, jni::jstring* dataPath_, jni::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_); return reinterpret_cast(new NativeMapView(env, jni::Unwrap(obj), pixelRatio, availableProcessors, totalMemory)); } void nativeDestroy(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeDestroy"); assert(nativeMapViewPtr != 0); delete reinterpret_cast(nativeMapViewPtr); } void nativeInitializeDisplay(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeDisplay"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->initializeDisplay(); } void nativeTerminateDisplay(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminateDisplay"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->terminateDisplay(); } void nativeInitializeContext(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeContext"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->initializeContext(); } void nativeTerminateContext(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminateContext"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->terminateContext(); } void nativeCreateSurface(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* surface) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreateSurface"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->createSurface(ANativeWindow_fromSurface(env, jni::Unwrap(surface))); } void nativeDestroySurface(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeDestroySurface"); assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->destroySurface(); } void nativeUpdate(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->invalidate(); } void nativeRender(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->render(); } void nativeViewResize(JNIEnv *env, jni::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(nativeMapViewPtr); nativeMapView->resizeView(width, height); } void nativeFramebufferResize(JNIEnv *env, jni::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(nativeMapViewPtr); nativeMapView->resizeFramebuffer(fbWidth, fbHeight); } void nativeRemoveClass(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* clazz) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().removeClass(std_string_from_jstring(env, clazz)); } jboolean nativeHasClass(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* clazz) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().hasClass(std_string_from_jstring(env, clazz)); } void nativeAddClass(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* clazz) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().addClass(std_string_from_jstring(env, clazz)); } void nativeSetClasses(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* classes) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().setClasses(std_vector_string_from_jobject(env, classes)); } jni::jobject* nativeGetClasses(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return std_vector_string_to_jobject(env, nativeMapView->getMap().getClasses()); } void nativeSetAPIBaseURL(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* url) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getFileSource().setAPIBaseURL(std_string_from_jstring(env, url)); } void nativeSetStyleUrl(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* url) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().setStyleURL(std_string_from_jstring(env, url)); } void nativeSetStyleJson(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* newStyleJson) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().setStyleJSON(std_string_from_jstring(env, newStyleJson)); } jni::jstring* nativeGetStyleJson(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return std_string_to_jstring(env, nativeMapView->getMap().getStyleJSON()); } void nativeSetAccessToken(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* accessToken) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getFileSource().setAccessToken(std_string_from_jstring(env, accessToken)); } jni::jstring* nativeGetAccessToken(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return std_string_to_jstring(env, nativeMapView->getFileSource().getAccessToken()); } void nativeCancelTransitions(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().cancelTransitions(); } void nativeSetGestureInProgress(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jboolean inProgress) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().setGestureInProgress(inProgress); } void nativeMoveBy(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble dx, jdouble dy, jlong duration) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::ScreenCoordinate center(dx, dy); nativeMapView->getMap().moveBy(center, mbgl::Milliseconds(duration)); } void nativeSetLatLng(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble latitude, jdouble longitude, jlong duration) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().setLatLng(mbgl::LatLng(latitude, longitude), nativeMapView->getInsets(), mbgl::Duration(duration)); } jni::jobject* nativeGetLatLng(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::LatLng latLng = nativeMapView->getMap().getLatLng(nativeMapView->getInsets()); return &jni::NewObject(*env, *latLngClass, *latLngConstructorId, latLng.latitude, latLng.longitude); } jdoubleArray nativeGetCameraValues(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::LatLng latLng = nativeMapView->getMap().getLatLng(nativeMapView->getInsets()); jdoubleArray output = env->NewDoubleArray(5); jsize start = 0; jsize leng = 5; jdouble buf[5]; buf[0] = latLng.latitude; buf[1] = latLng.longitude; buf[2] = -(nativeMapView->getMap().getBearing()-360); buf[3] = nativeMapView->getMap().getPitch(); buf[4] = nativeMapView->getMap().getZoom(); env->SetDoubleArrayRegion(output, start, leng, buf); if (output == nullptr) { env->ExceptionDescribe(); return nullptr; } return output; } void nativeResetPosition(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().resetPosition(); } jdouble nativeGetPitch(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().getPitch(); } void nativeSetPitch(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble pitch, jlong milliseconds) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::Duration duration((mbgl::Milliseconds(milliseconds))); nativeMapView->getMap().setPitch(pitch, duration); } void nativeScaleBy(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble ds, jdouble cx, jdouble cy, jlong duration) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::ScreenCoordinate center(cx, cy); nativeMapView->getMap().scaleBy(ds, center, mbgl::Milliseconds(duration)); } void nativeSetScale(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble scale, jdouble cx, jdouble cy, jlong duration) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::ScreenCoordinate center(cx, cy); nativeMapView->getMap().setScale(scale, center, mbgl::Milliseconds(duration)); } jdouble nativeGetScale(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().getScale(); } void nativeSetZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble zoom, jlong duration) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().setZoom(zoom, mbgl::Milliseconds(duration)); } jdouble nativeGetZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().getZoom(); } void nativeResetZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().resetZoom(); } void nativeSetMinZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble zoom) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().setMinZoom(zoom); } jdouble nativeGetMinZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().getMinZoom(); } void nativeSetMaxZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble zoom) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().setMaxZoom(zoom); } jdouble nativeGetMaxZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().getMaxZoom(); } void nativeRotateBy(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble sx, jdouble sy, jdouble ex, jdouble ey, jlong duration) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::ScreenCoordinate first(sx, sy); mbgl::ScreenCoordinate second(ex, ey); nativeMapView->getMap().rotateBy(first, second, mbgl::Milliseconds(duration)); } void nativeSetBearing(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble degrees, jlong milliseconds) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::Duration duration((mbgl::Milliseconds(milliseconds))); nativeMapView->getMap().setBearing(degrees, duration); } void nativeSetBearingXY(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble degrees, jdouble cx, jdouble cy) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::ScreenCoordinate center(cx, cy); nativeMapView->getMap().setBearing(degrees, center); } jdouble nativeGetBearing(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().getBearing(); } void nativeResetNorth(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().resetNorth(); } void nativeUpdateMarker(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong markerId, jdouble lat, jdouble lon, jni::jstring* jid) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); if (markerId == -1) { return; } std::string iconId = std_string_from_jstring(env, jid); // Because Java only has int, not unsigned int, we need to bump the annotation id up to a long. nativeMapView->getMap().updateAnnotation(markerId, mbgl::SymbolAnnotation { mbgl::Point(lon, lat), iconId }); } jni::jarray* nativeAddMarkers(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray* jarray) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); NullCheck(*env, jarray); std::size_t len = jni::GetArrayLength(*env, *jarray); mbgl::AnnotationIDs ids; ids.reserve(len); for (std::size_t i = 0; i < len; i++) { jni::jobject* marker = jni::GetObjectArrayElement(*env, *jarray, i); jni::jobject* position = jni::GetField(*env, marker, *markerPositionId); jni::jobject* icon = jni::GetField(*env, marker, *markerIconId); jni::jstring* jid = reinterpret_cast(jni::GetField(*env, icon, *iconIdId)); jdouble latitude = jni::GetField(*env, position, *latLngLatitudeId); jdouble longitude = jni::GetField(*env, position, *latLngLongitudeId); ids.push_back(nativeMapView->getMap().addAnnotation(mbgl::SymbolAnnotation { mbgl::Point(longitude, latitude), std_string_from_jstring(env, jid) })); jni::DeleteLocalRef(*env, position); jni::DeleteLocalRef(*env, jid); jni::DeleteLocalRef(*env, icon); jni::DeleteLocalRef(*env, marker); } return std_vector_uint_to_jobject(env, ids); } static mbgl::Color toColor(jint color) { float r = (color >> 16) & 0xFF; float g = (color >> 8) & 0xFF; float b = (color) & 0xFF; float a = (color >> 24) & 0xFF; return { r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f }; } template Geometry toGeometry(JNIEnv *env, jni::jobject* jlist) { NullCheck(*env, jlist); jni::jarray* jarray = reinterpret_cast*>(jni::CallMethod(*env, jlist, *listToArrayId)); NullCheck(*env, jarray); std::size_t size = jni::GetArrayLength(*env, *jarray); Geometry geometry; geometry.reserve(size); for (std::size_t i = 0; i < size; i++) { jni::jobject* latLng = reinterpret_cast(jni::GetObjectArrayElement(*env, *jarray, i)); NullCheck(*env, latLng); geometry.push_back(mbgl::Point( jni::GetField(*env, latLng, *latLngLongitudeId), jni::GetField(*env, latLng, *latLngLatitudeId))); jni::DeleteLocalRef(*env, latLng); } jni::DeleteLocalRef(*env, jarray); jni::DeleteLocalRef(*env, jlist); return geometry; } jni::jarray* nativeAddPolylines(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray* jarray) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); NullCheck(*env, jarray); std::size_t len = jni::GetArrayLength(*env, *jarray); mbgl::AnnotationIDs ids; ids.reserve(len); for (std::size_t i = 0; i < len; i++) { jni::jobject* polyline = jni::GetObjectArrayElement(*env, *jarray, i); jni::jobject* points = jni::GetField(*env, polyline, *polylinePointsId); mbgl::LineAnnotation annotation { toGeometry>(env, points) }; annotation.opacity = { jni::GetField(*env, polyline, *polylineAlphaId) }; annotation.color = { toColor(jni::GetField(*env, polyline, *polylineColorId)) }; annotation.width = { jni::GetField(*env, polyline, *polylineWidthId) }; ids.push_back(nativeMapView->getMap().addAnnotation(annotation)); jni::DeleteLocalRef(*env, polyline); } return std_vector_uint_to_jobject(env, ids); } jni::jarray* nativeAddPolygons(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray* jarray) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); NullCheck(*env, jarray); std::size_t len = jni::GetArrayLength(*env, *jarray); mbgl::AnnotationIDs ids; ids.reserve(len); for (std::size_t i = 0; i < len; i++) { jni::jobject* polygon = jni::GetObjectArrayElement(*env, *jarray, i); jni::jobject* points = jni::GetField(*env, polygon, *polygonPointsId); mbgl::FillAnnotation annotation { mbgl::Polygon { toGeometry>(env, points) } }; annotation.opacity = { jni::GetField(*env, polygon, *polygonAlphaId) }; annotation.outlineColor = { toColor(jni::GetField(*env, polygon, *polygonStrokeColorId)) }; annotation.color = { toColor(jni::GetField(*env, polygon, *polygonFillColorId)) }; ids.push_back(nativeMapView->getMap().addAnnotation(annotation)); jni::DeleteLocalRef(*env, polygon); } return std_vector_uint_to_jobject(env, ids); } void nativeUpdatePolygon(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong polygonId, jni::jobject* polygon) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); jni::jobject* points = jni::GetField(*env, polygon, *polygonPointsId); mbgl::FillAnnotation annotation { mbgl::Polygon { toGeometry>(env, points) } }; annotation.opacity = { jni::GetField(*env, polygon, *polygonAlphaId) }; annotation.outlineColor = { toColor(jni::GetField(*env, polygon, *polygonStrokeColorId)) }; annotation.color = { toColor(jni::GetField(*env, polygon, *polygonFillColorId)) }; nativeMapView->getMap().updateAnnotation(polygonId, annotation); } void nativeUpdatePolyline(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong polylineId, jni::jobject* polyline) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); jni::jobject* points = jni::GetField(*env, polyline, *polylinePointsId); mbgl::LineAnnotation annotation { toGeometry>(env, points) }; annotation.opacity = { jni::GetField(*env, polyline, *polylineAlphaId) }; annotation.color = { toColor(jni::GetField(*env, polyline, *polylineColorId)) }; annotation.width = { jni::GetField(*env, polyline, *polylineWidthId) }; nativeMapView->getMap().updateAnnotation(polylineId, annotation); } void nativeRemoveAnnotations(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray* jarray) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); NullCheck(*env, jarray); std::size_t len = jni::GetArrayLength(*env, *jarray); auto elements = jni::GetArrayElements(*env, *jarray); jlong* jids = std::get<0>(elements).get(); for (std::size_t i = 0; i < len; i++) { if(jids[i] == -1L) continue; nativeMapView->getMap().removeAnnotation(jids[i]); } } jni::jarray* nativeQueryPointAnnotations(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* rect) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); // Conversion jfloat left = jni::GetField(*env, rect, *rectFLeftId); jfloat right = jni::GetField(*env, rect, *rectFRightId); jfloat top = jni::GetField(*env, rect, *rectFTopId); jfloat bottom = jni::GetField(*env, rect, *rectFBottomId); mbgl::ScreenBox box = { { left, top }, { right, bottom }, }; // Assume only points for now mbgl::AnnotationIDs ids = nativeMapView->getMap().queryPointAnnotations(box); return std_vector_uint_to_jobject(env, ids); } void nativeAddAnnotationIcon(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* symbol, jint width, jint height, jfloat scale, jni::jarray* jpixels) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); const std::string symbolName = std_string_from_jstring(env, symbol); NullCheck(*env, jpixels); std::size_t size = jni::GetArrayLength(*env, *jpixels); mbgl::PremultipliedImage premultipliedImage(width, height); if (premultipliedImage.size() != uint32_t(size)) { throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch"); } jni::GetArrayRegion(*env, *jpixels, 0, size, reinterpret_cast(premultipliedImage.data.get())); auto iconImage = std::make_shared( std::move(premultipliedImage), float(scale)); nativeMapView->getMap().addAnnotationIcon(symbolName, iconImage); } void nativeSetVisibleCoordinateBounds(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray* coordinates, jni::jobject* padding, jdouble direction, jlong duration) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); jfloat left = jni::GetField(*env, padding, *rectFLeftId); jfloat right = jni::GetField(*env, padding, *rectFRightId); jfloat top = jni::GetField(*env, padding, *rectFTopId); jfloat bottom = jni::GetField(*env, padding, *rectFBottomId); NullCheck(*env, coordinates); std::size_t count = jni::GetArrayLength(*env, *coordinates); mbgl::EdgeInsets mbglInsets = {top, left, bottom, right}; std::vector latLngs; latLngs.reserve(count); for (std::size_t i = 0; i < count; i++) { jni::jobject* latLng = jni::GetObjectArrayElement(*env, *coordinates, i); jdouble latitude = jni::GetField(*env, latLng, *latLngLatitudeId); jdouble longitude = jni::GetField(*env, latLng, *latLngLongitudeId); latLngs.push_back(mbgl::LatLng(latitude, longitude)); } mbgl::CameraOptions cameraOptions = nativeMapView->getMap().cameraForLatLngs(latLngs, mbglInsets); if (direction >= 0) { // convert from degrees to radians cameraOptions.angle = (-direction * M_PI) / 180; } mbgl::AnimationOptions animationOptions; if (duration > 0) { animationOptions.duration.emplace(mbgl::Milliseconds(duration)); // equivalent to kCAMediaTimingFunctionDefault in iOS animationOptions.easing.emplace(mbgl::util::UnitBezier { 0.25, 0.1, 0.25, 0.1 }); } nativeMapView->getMap().easeTo(cameraOptions, animationOptions); } jni::jarray* nativeQueryRenderedFeaturesForPoint(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jfloat x, jni::jfloat y, jni::jarray* layerIds) { using namespace mbgl::android::conversion; using namespace mapbox::geometry; assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::optional> layers; if (layerIds != nullptr && jni::GetArrayLength(*env, *layerIds) > 0) { layers = toVector(*env, *layerIds); } point point = {x, y}; return *convert*, std::vector>(*env, nativeMapView->getMap().queryRenderedFeatures(point, layers)); } jni::jarray* nativeQueryRenderedFeaturesForBox(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jfloat left, jni::jfloat top, jni::jfloat right, jni::jfloat bottom, jni::jarray* layerIds) { using namespace mbgl::android::conversion; using namespace mapbox::geometry; assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::optional> layers; if (layerIds != nullptr && jni::GetArrayLength(*env, *layerIds) > 0) { layers = toVector(*env, *layerIds); } box box = { point{ left, top}, point{ right, bottom } }; return *convert*, std::vector>(*env, nativeMapView->getMap().queryRenderedFeatures(box, layers)); } void nativeOnLowMemory(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().onLowMemory(); } void nativeSetDebug(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jboolean debug) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); DebugOptions debugOptions = debug ? DebugOptions::TileBorders | DebugOptions::ParseStatus | DebugOptions::Collision : DebugOptions::NoDebug; nativeMapView->getMap().setDebug(debugOptions); nativeMapView->enableFps(debug); } void nativeToggleDebug(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().cycleDebugOptions(); nativeMapView->enableFps(nativeMapView->getMap().getDebug() != DebugOptions::NoDebug); } jboolean nativeGetDebug(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().getDebug() != DebugOptions::NoDebug; } jboolean nativeIsFullyLoaded(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().isFullyLoaded(); } void nativeSetReachability(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jboolean status) { assert(nativeMapViewPtr != 0); if (status) { mbgl::NetworkStatus::Reachable(); } } jdouble nativeGetMetersPerPixelAtLatitude(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble lat, jdouble zoom) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().getMetersPerPixelAtLatitude(lat, zoom); } jni::jobject* nativeProjectedMetersForLatLng(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble latitude, jdouble longitude) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::ProjectedMeters projectedMeters = nativeMapView->getMap().projectedMetersForLatLng(mbgl::LatLng(latitude, longitude)); return &jni::NewObject(*env, *projectedMetersClass, *projectedMetersConstructorId, projectedMeters.northing, projectedMeters.easting); } jni::jobject* nativeLatLngForProjectedMeters(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble northing, jdouble easting) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::LatLng latLng = nativeMapView->getMap().latLngForProjectedMeters(mbgl::ProjectedMeters(northing, easting)); return &jni::NewObject(*env, *latLngClass, *latLngConstructorId, latLng.latitude, latLng.longitude); } jni::jobject* nativePixelForLatLng(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble latitude, jdouble longitude) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::ScreenCoordinate pixel = nativeMapView->getMap().pixelForLatLng(mbgl::LatLng(latitude, longitude)); return &jni::NewObject(*env, *pointFClass, *pointFConstructorId, static_cast(pixel.x), static_cast(pixel.y)); } jni::jobject* nativeLatLngForPixel(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jfloat x, jfloat y) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::LatLng latLng = nativeMapView->getMap().latLngForPixel(mbgl::ScreenCoordinate(x, y)); return &jni::NewObject(*env, *latLngClass, *latLngConstructorId, latLng.latitude, latLng.longitude); } jdouble nativeGetTopOffsetPixelsForAnnotationSymbol(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* symbolName) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); return nativeMapView->getMap().getTopOffsetPixelsForAnnotationIcon(std_string_from_jstring(env, symbolName)); } void nativeJumpTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jdouble latitude, jdouble longitude, jdouble pitch, jdouble zoom) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::CameraOptions options; if (angle != -1) { options.angle = angle * M_PI / 180; } options.center = mbgl::LatLng(latitude, longitude); options.padding = nativeMapView->getInsets(); if (pitch != -1) { options.pitch = pitch * M_PI / 180; } if (zoom != -1) { options.zoom = zoom; } nativeMapView->getMap().jumpTo(options); } void nativeEaseTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jdouble latitude, jdouble longitude, jlong duration, jdouble pitch, jdouble zoom, jboolean easing) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::CameraOptions cameraOptions; if (angle != -1) { cameraOptions.angle = angle * M_PI / 180; } cameraOptions.center = mbgl::LatLng(latitude, longitude); cameraOptions.padding = nativeMapView->getInsets(); if (pitch != -1) { cameraOptions.pitch = pitch * M_PI / 180; } if (zoom != -1) { cameraOptions.zoom = zoom; } mbgl::AnimationOptions animationOptions; animationOptions.duration.emplace(mbgl::Duration(duration)); if (!easing) { // add a linear interpolator instead of easing animationOptions.easing.emplace(mbgl::util::UnitBezier { 0, 0, 1, 1 }); } nativeMapView->getMap().easeTo(cameraOptions, animationOptions); } void nativeSetContentPadding(JNIEnv *env, jni::jobject* obj,long nativeMapViewPtr, double top, double left, double bottom, double right) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->setInsets({top, left, bottom, right}); } void nativeFlyTo(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble angle, jdouble latitude, jdouble longitude, jlong duration, jdouble pitch, jdouble zoom) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); mbgl::CameraOptions cameraOptions; if (angle != -1) { cameraOptions.angle = angle * M_PI / 180 ; } cameraOptions.center = mbgl::LatLng(latitude, longitude); cameraOptions.padding = nativeMapView->getInsets(); if (pitch != -1) { cameraOptions.pitch = pitch * M_PI / 180; } if (zoom != -1) { cameraOptions.zoom = zoom; } mbgl::AnimationOptions animationOptions; animationOptions.duration.emplace(mbgl::Duration(duration)); nativeMapView->getMap().flyTo(cameraOptions, animationOptions); } jni::jobject* nativeGetLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* layerId) { assert(env); assert(nativeMapViewPtr != 0); //Get the native map peer NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); //Find the layer mbgl::style::Layer* coreLayer = nativeMapView->getMap().getLayer(std_string_from_jstring(env, layerId)); if (!coreLayer) { mbgl::Log::Debug(mbgl::Event::JNI, "No layer found"); return jni::Object(); } //Create and return the layer's native peer return createJavaLayerPeer(*env, nativeMapView->getMap(), *coreLayer); } void nativeAddLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong nativeLayerPtr, jni::jstring* before) { assert(nativeMapViewPtr != 0); assert(nativeLayerPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); Layer *layer = reinterpret_cast(nativeLayerPtr); nativeMapView->getMap().addLayer( layer->releaseCoreLayer(), before ? mbgl::optional(std_string_from_jstring(env, before)) : mbgl::optional() ); } void nativeRemoveLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); try { nativeMapView->getMap().removeLayer(std_string_from_jstring(env, id)); } catch (const std::runtime_error& error) { jni::ThrowNew(*env, jni::FindClass(*env, "com/mapbox/mapboxsdk/style/layers/NoSuchLayerException"), error.what()); } } jni::jobject* nativeGetSource(JNIEnv *env, jni::jobject* obj, jni::jlong nativeMapViewPtr, jni::jstring* sourceId) { assert(env); assert(nativeMapViewPtr != 0); //Get the native map peer NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); //Find the source mbgl::style::Source* coreSource = nativeMapView->getMap().getSource(std_string_from_jstring(env, sourceId)); if (!coreSource) { mbgl::Log::Debug(mbgl::Event::JNI, "No source found"); return jni::Object(); } //Create and return the source's native peer return createJavaSourcePeer(*env, nativeMapView->getMap(), *coreSource); } void nativeAddSource(JNIEnv *env, jni::jobject* obj, jni::jlong nativeMapViewPtr, jni::jlong nativeSourcePtr) { assert(nativeMapViewPtr != 0); assert(nativeSourcePtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); Source *source = reinterpret_cast(nativeSourcePtr); nativeMapView->getMap().addSource(source->releaseCoreSource()); } void nativeRemoveSource(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); try { nativeMapView->getMap().removeSource(std_string_from_jstring(env, id)); } catch (const std::runtime_error& error) { jni::ThrowNew(*env, jni::FindClass(*env, "com/mapbox/mapboxsdk/style/sources/NoSuchSourceException"), error.what()); } } void nativeAddImage(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* name, jni::jint width, jni::jint height, jni::jfloat pixelRatio, jni::jarray* data) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); // Create Pre-multiplied image from byte[] NullCheck(*env, data); std::size_t size = jni::GetArrayLength(*env, *data); mbgl::PremultipliedImage premultipliedImage(width, height); if (premultipliedImage.size() != uint32_t(size)) { throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch"); } jni::GetArrayRegion(*env, *data, 0, size, reinterpret_cast(premultipliedImage.data.get())); //Wrap in a SpriteImage with the correct pixel ratio auto spriteImage = std::make_unique(std::move(premultipliedImage), float(pixelRatio)); nativeMapView->getMap().addImage(std_string_from_jstring(env, name), std::move(spriteImage)); } void nativeRemoveImage(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* name) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->getMap().removeImage(std_string_from_jstring(env, name)); } void nativeScheduleTakeSnapshot(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { assert(nativeMapViewPtr != 0); NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); nativeMapView->scheduleTakeSnapshot(); } // Offline calls begin jlong createDefaultFileSource(JNIEnv *env, jni::jobject* obj, jni::jstring* cachePath_, jni::jstring* assetRoot_, jlong maximumCacheSize) { std::string cachePath = std_string_from_jstring(env, cachePath_); std::string assetRoot = std_string_from_jstring(env, assetRoot_); mbgl::DefaultFileSource *defaultFileSource = new mbgl::DefaultFileSource(cachePath, assetRoot, maximumCacheSize); jlong defaultFileSourcePtr = reinterpret_cast(defaultFileSource); return defaultFileSourcePtr; } void setAccessToken(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourcePtr, jni::jstring* accessToken_) { assert(defaultFileSourcePtr != 0); std::string accessToken = std_string_from_jstring(env, accessToken_); mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); defaultFileSource->setAccessToken(accessToken); } jni::jstring* getAccessToken(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourcePtr) { assert(defaultFileSourcePtr != 0); mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); std::string accessToken = defaultFileSource->getAccessToken(); return std_string_to_jstring(env, accessToken); } void listOfflineRegions(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourcePtr, jni::jobject* listCallback) { // Checks assert(defaultFileSourcePtr != 0); NullCheck(*env, listCallback); // Makes sure the objects don't get GC'ed obj = jni::NewGlobalRef(*env, obj).release(); listCallback = jni::NewGlobalRef(*env, listCallback).release(); // Launch listCallback mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); defaultFileSource->listOfflineRegions([obj, defaultFileSourcePtr, listCallback](std::exception_ptr error, mbgl::optional> regions) mutable { // Reattach, the callback comes from a different thread JNIEnv *env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); if (renderDetach) { mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); } if (error) { std::string message = mbgl::util::toString(error); jni::CallMethod(*env2, listCallback, *listOnErrorMethodId, std_string_to_jstring(env2, message)); } else if (regions) { // Build jni::jarray* std::size_t index = 0; jni::jarray* jregions = &jni::NewObjectArray(*env2, regions->size(), *offlineRegionClass, NULL); for (auto& region : *regions) { // Create a new local reference frame (capacity 2 for the NewObject allocations below) // to avoid a local reference table overflow (#5629) jni::UniqueLocalFrame frame = jni::PushLocalFrame(*env2, 2); // Build the Region object jni::jobject* jregion = &jni::NewObject(*env2, *offlineRegionClass, *offlineRegionConstructorId); jni::SetField(*env2, jregion, *offlineRegionOfflineManagerId, obj); jni::SetField(*env2, jregion, *offlineRegionIdId, region.getID()); // Definition object mbgl::OfflineTilePyramidRegionDefinition definition = region.getDefinition(); jni::jobject* jdefinition = &jni::NewObject(*env2, *offlineRegionDefinitionClass, *offlineRegionDefinitionConstructorId); jni::SetField(*env2, jdefinition, *offlineRegionDefinitionStyleURLId, std_string_to_jstring(env2, definition.styleURL)); jni::SetField(*env2, jdefinition, *offlineRegionDefinitionBoundsId, latlngbounds_from_native(env2, definition.bounds)); jni::SetField(*env2, jdefinition, *offlineRegionDefinitionMinZoomId, definition.minZoom); jni::SetField(*env2, jdefinition, *offlineRegionDefinitionMaxZoomId, definition.maxZoom); jni::SetField(*env2, jdefinition, *offlineRegionDefinitionPixelRatioId, definition.pixelRatio); jni::SetField(*env2, jregion, *offlineRegionDefinitionId, jdefinition); // Metadata object jni::jarray* metadata = metadata_from_native(env2, region.getMetadata()); jni::SetField(*env2, jregion, *offlineRegionMetadataId, metadata); // Moves the region on the stack into a heap-allocated one jni::SetField(*env2, jregion, *offlineRegionPtrId, reinterpret_cast(new mbgl::OfflineRegion(std::move(region)))); jni::SetObjectArrayElement(*env2, *jregions, index, jregion); index++; } // Trigger callback jni::CallMethod(*env2, listCallback, *listOnListMethodId, jregions); } // Delete global refs and detach when we're done jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef(obj)); jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef(listCallback)); detach_jni_thread(theJVM, &env2, renderDetach); }); } void createOfflineRegion(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourcePtr, jni::jobject* definition_, jni::jarray* metadata_, jni::jobject* createCallback) { // Checks assert(defaultFileSourcePtr != 0); NullCheck(*env, createCallback); // Definition fields jni::jstring* jStyleURL = reinterpret_cast(jni::GetField(*env, definition_, *offlineRegionDefinitionStyleURLId)); std::string styleURL = std_string_from_jstring(env, jStyleURL); jni::jobject* jBounds = jni::GetField(*env, definition_, *offlineRegionDefinitionBoundsId); jdouble jMinZoom = jni::GetField(*env, definition_, *offlineRegionDefinitionMinZoomId); jdouble jMaxZoom = jni::GetField(*env, definition_, *offlineRegionDefinitionMaxZoomId); jfloat jPixelRatio = jni::GetField(*env, definition_, *offlineRegionDefinitionPixelRatioId); // Convert bounds fields to native mbgl::LatLngBounds bounds = latlngbounds_from_java(env, jBounds); // Definition mbgl::OfflineTilePyramidRegionDefinition definition(styleURL, bounds, jMinZoom, jMaxZoom, jPixelRatio); // Metadata mbgl::OfflineRegionMetadata metadata; if (metadata_ != nullptr) { metadata = metadata_from_java(env, *metadata_); } // Makes sure the objects don't get GC'ed obj = jni::NewGlobalRef(*env, obj).release(); createCallback = jni::NewGlobalRef(*env, createCallback).release(); // Launch createCallback mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); defaultFileSource->createOfflineRegion(definition, metadata, [obj, defaultFileSourcePtr, createCallback] (std::exception_ptr error, mbgl::optional region) mutable { // Reattach, the callback comes from a different thread JNIEnv *env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); if (renderDetach) { mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); } if (error) { std::string message = mbgl::util::toString(error); jni::CallMethod(*env2, createCallback, *createOnErrorMethodId, std_string_to_jstring(env2, message)); } else if (region) { // Build the Region object jni::jobject* jregion = &jni::NewObject(*env2, *offlineRegionClass, *offlineRegionConstructorId); jni::SetField(*env2, jregion, *offlineRegionOfflineManagerId, obj); jni::SetField(*env2, jregion, *offlineRegionIdId, region->getID()); // Metadata object jni::jarray* jmetadata = metadata_from_native(env2, region->getMetadata()); jni::SetField(*env2, jregion, *offlineRegionMetadataId, jmetadata); // Moves the region on the stack into a heap-allocated one jni::SetField(*env2, jregion, *offlineRegionPtrId, reinterpret_cast(new mbgl::OfflineRegion(std::move(*region)))); // Invoke Java callback jni::CallMethod(*env2, createCallback, *createOnCreateMethodId, jregion); } // Delete global refs and detach when we're done jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef(obj)); jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef(createCallback)); detach_jni_thread(theJVM, &env2, renderDetach); }); } void setOfflineMapboxTileCountLimit(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourcePtr, jlong limit) { // Checks assert(defaultFileSourcePtr != 0); assert(limit > 0); // Set limit mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); defaultFileSource->setOfflineMapboxTileCountLimit(limit); } mbgl::OfflineRegion* getOfflineRegionPeer(JNIEnv *env, jni::jobject* offlineRegion_) { jlong offlineRegionPtr = jni::GetField(*env, offlineRegion_, *offlineRegionPtrId); if (!offlineRegionPtr) { jni::ThrowNew(*env, jni::FindClass(*env, "java/lang/IllegalStateException"), "Use of OfflineRegion after OfflineRegion.delete"); } return reinterpret_cast(offlineRegionPtr); } void destroyOfflineRegion(JNIEnv *env, jni::jobject* offlineRegion_) { // Offline region jlong offlineRegionPtr = jni::GetField(*env, offlineRegion_, *offlineRegionPtrId); if (!offlineRegionPtr) { return; // Already deleted } // File source jni::jobject* jmanager = jni::GetField(*env, offlineRegion_, *offlineRegionOfflineManagerId); jlong defaultFileSourcePtr = jni::GetField(*env, jmanager, *offlineManagerClassPtrId); mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); // Release the observer and delete the region mbgl::OfflineRegion *offlineRegion = reinterpret_cast(offlineRegionPtr); defaultFileSource->setOfflineRegionObserver(*offlineRegion, nullptr); jni::SetField(*env, offlineRegion_, *offlineRegionPtrId, 0); delete offlineRegion; } void setOfflineRegionObserver(JNIEnv *env, jni::jobject* offlineRegion_, jni::jobject* observerCallback) { // Offline region mbgl::OfflineRegion* offlineRegion = getOfflineRegionPeer(env, offlineRegion_); // File source jni::jobject* jmanager = jni::GetField(*env, offlineRegion_, *offlineRegionOfflineManagerId); jlong defaultFileSourcePtr = jni::GetField(*env, jmanager, *offlineManagerClassPtrId); mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); // Define the observer class Observer : public mbgl::OfflineRegionObserver { public: Observer(jni::UniqueGlobalRef&& observerCallback_) : observerCallback(std::move(observerCallback_)) { } ~Observer() override { mbgl::Log::Debug(mbgl::Event::JNI, "~Observer()"); // Env JNIEnv* env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); jni::DeleteGlobalRef(*env2, std::move(observerCallback)); detach_jni_thread(theJVM, &env2, renderDetach); } void statusChanged(mbgl::OfflineRegionStatus status) override { // Env JNIEnv* env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); // Conver to jint jint downloadState; switch(status.downloadState) { case mbgl::OfflineRegionDownloadState::Inactive: downloadState = 0; break; case mbgl::OfflineRegionDownloadState::Active: downloadState = 1; break; } // Create a new local reference frame (capacity 1 for the NewObject allocation below) // to avoid a local reference table overflow (#4706) jni::UniqueLocalFrame frame = jni::PushLocalFrame(*env2, 1); // Stats object jni::jobject* jstatus = &jni::NewObject(*env2, *offlineRegionStatusClass, *offlineRegionStatusConstructorId); jni::SetField(*env2, jstatus, *offlineRegionStatusDownloadStateId, downloadState); jni::SetField(*env2, jstatus, *offlineRegionStatusCompletedResourceCountId, status.completedResourceCount); jni::SetField(*env2, jstatus, *offlineRegionStatusCompletedResourceSizeId, status.completedResourceSize); jni::SetField(*env2, jstatus, *offlineRegionStatusCompletedTileCountId, status.completedTileCount); jni::SetField(*env2, jstatus, *offlineRegionStatusCompletedTileSizeId, status.completedTileSize); jni::SetField(*env2, jstatus, *offlineRegionStatusRequiredResourceCountId, status.requiredResourceCount); jni::SetField(*env2, jstatus, *offlineRegionStatusRequiredResourceCountIsPreciseId, status.requiredResourceCountIsPrecise); jni::CallMethod(*env2, observerCallback.get(), *offlineRegionObserveronStatusChangedId, jstatus); // Detach when we're done detach_jni_thread(theJVM, &env2, renderDetach); } void responseError(mbgl::Response::Error error) override { // Env JNIEnv* env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); // Handle the value of reason independently of the underlying int value std::string errorReason; switch(error.reason) { case mbgl::Response::Error::Reason::Success: errorReason = "REASON_SUCCESS"; break; case mbgl::Response::Error::Reason::NotFound: errorReason = "REASON_NOT_FOUND"; break; case mbgl::Response::Error::Reason::Server: errorReason = "REASON_SERVER"; break; case mbgl::Response::Error::Reason::Connection: errorReason = "REASON_CONNECTION"; break; case mbgl::Response::Error::Reason::RateLimit: errorReason = "REASON_RATE_LIMIT"; break; case mbgl::Response::Error::Reason::Other: errorReason = "REASON_OTHER"; break; } // Error object jni::UniqueLocalFrame frame = jni::PushLocalFrame(*env2, 3); jni::jobject* jerror = &jni::NewObject(*env2, *offlineRegionErrorClass, *offlineRegionErrorConstructorId); jni::SetField(*env2, jerror, *offlineRegionErrorReasonId, std_string_to_jstring(env2, errorReason)); jni::SetField(*env2, jerror, *offlineRegionErrorMessageId, std_string_to_jstring(env2, error.message)); jni::CallMethod(*env2, observerCallback.get(), *offlineRegionObserveronErrorId, jerror); // Detach when we're done detach_jni_thread(theJVM, &env2, renderDetach); } void mapboxTileCountLimitExceeded(uint64_t limit) override { // Env JNIEnv* env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); // Send limit jni::CallMethod(*env2, observerCallback.get(), *offlineRegionObserveronLimitId, jlong(limit)); // Detach when we're done detach_jni_thread(theJVM, &env2, renderDetach); } jni::UniqueGlobalRef observerCallback; }; // Set the observer defaultFileSource->setOfflineRegionObserver(*offlineRegion, std::make_unique(jni::NewGlobalRef(*env, observerCallback))); } void setOfflineRegionDownloadState(JNIEnv *env, jni::jobject* offlineRegion_, jint offlineRegionDownloadState) { // State mbgl::OfflineRegionDownloadState state; if (offlineRegionDownloadState == 0) { state = mbgl::OfflineRegionDownloadState::Inactive; } else if (offlineRegionDownloadState == 1) { state = mbgl::OfflineRegionDownloadState::Active; } else { mbgl::Log::Error(mbgl::Event::JNI, "State can only be 0 (inactive) or 1 (active)."); return; } // Offline region mbgl::OfflineRegion* offlineRegion = getOfflineRegionPeer(env, offlineRegion_); // File source jni::jobject* jmanager = jni::GetField(*env, offlineRegion_, *offlineRegionOfflineManagerId); jlong defaultFileSourcePtr = jni::GetField(*env, jmanager, *offlineManagerClassPtrId); mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); // Set new state defaultFileSource->setOfflineRegionDownloadState(*offlineRegion, state); } void getOfflineRegionStatus(JNIEnv *env, jni::jobject* offlineRegion_, jni::jobject* statusCallback) { // Offline region mbgl::OfflineRegion* offlineRegion = getOfflineRegionPeer(env, offlineRegion_); // File source jni::jobject* jmanager = jni::GetField(*env, offlineRegion_, *offlineRegionOfflineManagerId); jlong defaultFileSourcePtr = jni::GetField(*env, jmanager, *offlineManagerClassPtrId); mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); // Makes sure the callback doesn't get GC'ed statusCallback = jni::NewGlobalRef(*env, statusCallback).release(); // Set new state defaultFileSource->getOfflineRegionStatus(*offlineRegion, [statusCallback](std::exception_ptr error, mbgl::optional status) mutable { // Reattach, the callback comes from a different thread JNIEnv *env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); if (renderDetach) { mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); } if (error) { std::string message = mbgl::util::toString(error); jni::CallMethod(*env2, statusCallback, *offlineRegionStatusOnErrorId, std_string_to_jstring(env2, message)); } else if (status) { // Conver to jint jint downloadState = -1; if (status->downloadState == mbgl::OfflineRegionDownloadState::Inactive) { downloadState = 0; } else if (status->downloadState == mbgl::OfflineRegionDownloadState::Active) { downloadState = 1; } else { mbgl::Log::Error(mbgl::Event::JNI, "Unsupported OfflineRegionDownloadState value."); return; } // Stats object jni::jobject* jstatus = &jni::NewObject(*env2, *offlineRegionStatusClass, *offlineRegionStatusConstructorId); jni::SetField(*env2, jstatus, *offlineRegionStatusDownloadStateId, downloadState); jni::SetField(*env2, jstatus, *offlineRegionStatusCompletedResourceCountId, status->completedResourceCount); jni::SetField(*env2, jstatus, *offlineRegionStatusCompletedResourceSizeId, status->completedResourceSize); jni::SetField(*env2, jstatus, *offlineRegionStatusRequiredResourceCountId, status->requiredResourceCount); jni::SetField(*env2, jstatus, *offlineRegionStatusRequiredResourceCountIsPreciseId, status->requiredResourceCountIsPrecise); jni::CallMethod(*env2, statusCallback, *offlineRegionStatusOnStatusId, jstatus); } // Delete global refs and detach when we're done jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef(statusCallback)); detach_jni_thread(theJVM, &env2, renderDetach); }); } void deleteOfflineRegion(JNIEnv *env, jni::jobject* offlineRegion_, jni::jobject* deleteCallback) { // Offline region mbgl::OfflineRegion* offlineRegion = getOfflineRegionPeer(env, offlineRegion_); // File source jni::jobject* jmanager = jni::GetField(*env, offlineRegion_, *offlineRegionOfflineManagerId); jlong defaultFileSourcePtr = jni::GetField(*env, jmanager, *offlineManagerClassPtrId); mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); // Makes sure the callback doesn't get GC'ed deleteCallback = jni::NewGlobalRef(*env, deleteCallback).release(); // Set new state jni::SetField(*env, offlineRegion_, *offlineRegionPtrId, 0); defaultFileSource->deleteOfflineRegion(std::move(*offlineRegion), [deleteCallback](std::exception_ptr error) mutable { // Reattach, the callback comes from a different thread JNIEnv *env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); if (renderDetach) { mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); } if (error) { std::string message = mbgl::util::toString(error); jni::CallMethod(*env2, deleteCallback, *offlineRegionDeleteOnErrorId, std_string_to_jstring(env2, message)); } else { jni::CallMethod(*env2, deleteCallback, *offlineRegionDeleteOnDeleteId); } // Delete global refs and detach when we're done jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef(deleteCallback)); detach_jni_thread(theJVM, &env2, renderDetach); }); } void updateOfflineRegionMetadata(JNIEnv *env, jni::jobject* offlineRegion_, jni::jarray* metadata_, jni::jobject* updateCallback) { // Offline region mbgl::OfflineRegion* offlineRegion = getOfflineRegionPeer(env, offlineRegion_); // File source jni::jobject* jmanager = jni::GetField(*env, offlineRegion_, *offlineRegionOfflineManagerId); jlong defaultFileSourcePtr = jni::GetField(*env, jmanager, *offlineManagerClassPtrId); mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast(defaultFileSourcePtr); // Id conversion int64_t id = offlineRegion->getID(); // Metadata mbgl::OfflineRegionMetadata metadata; if (metadata_ != nullptr) { metadata = metadata_from_java(env, *metadata_); } // Makes sure the objects don't get GC'ed updateCallback = jni::NewGlobalRef(*env, updateCallback).release(); // Launch updateCallback defaultFileSource->updateOfflineMetadata(id, metadata, [updateCallback] (std::exception_ptr error, mbgl::optional data) mutable { // Reattach, the callback comes from a different thread JNIEnv *env2; jboolean renderDetach = attach_jni_thread(theJVM, &env2, "Offline Thread"); if (renderDetach) { mbgl::Log::Debug(mbgl::Event::JNI, "Attached."); } if (error) { std::string message = mbgl::util::toString(error); jni::CallMethod(*env2, updateCallback, *updateMetadataOnErrorMethodId, std_string_to_jstring(env2, message)); } else if (data) { jni::jarray* jmetadata = metadata_from_native(env2, *data); jni::CallMethod(*env2, updateCallback, *updateMetadataOnUpdateMethodId, jmetadata); } // Delete global refs and detach when we're done jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef(updateCallback)); detach_jni_thread(theJVM, &env2, renderDetach); }); } // Offline calls end } extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { theJVM = vm; jni::JNIEnv& env = jni::GetEnv(*vm, jni::jni_version_1_6); static mbgl::util::RunLoop mainRunLoop; mbgl::android::RegisterNativeHTTPRequest(env); java::registerNatives(env); registerNativeLayers(env); registerNativeSources(env); ConnectivityListener::registerNative(env); latLngClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/geometry/LatLng"); latLngClass = jni::NewGlobalRef(env, latLngClass).release(); latLngConstructorId = &jni::GetMethodID(env, *latLngClass, "", "(DD)V"); latLngLatitudeId = &jni::GetFieldID(env, *latLngClass, "latitude", "D"); latLngLongitudeId = &jni::GetFieldID(env, *latLngClass, "longitude", "D"); latLngBoundsClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/geometry/LatLngBounds"); latLngBoundsClass = jni::NewGlobalRef(env, latLngBoundsClass).release(); latLngBoundsConstructorId = &jni::GetMethodID(env, *latLngBoundsClass, "", "(DDDD)V"); latLngBoundsLatNorthId = &jni::GetFieldID(env, *latLngBoundsClass, "mLatNorth", "D"); latLngBoundsLatSouthId = &jni::GetFieldID(env, *latLngBoundsClass, "mLatSouth", "D"); latLngBoundsLonEastId = &jni::GetFieldID(env, *latLngBoundsClass, "mLonEast", "D"); latLngBoundsLonWestId = &jni::GetFieldID(env, *latLngBoundsClass, "mLonWest", "D"); iconClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/annotations/Icon"); iconClass = jni::NewGlobalRef(env, iconClass).release(); iconIdId = &jni::GetFieldID(env, *iconClass, "mId", "Ljava/lang/String;"); markerClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/annotations/Marker"); markerClass = jni::NewGlobalRef(env, markerClass).release(); markerPositionId = &jni::GetFieldID(env, *markerClass, "position", "Lcom/mapbox/mapboxsdk/geometry/LatLng;"); markerIconId = &jni::GetFieldID(env, *markerClass, "icon", "Lcom/mapbox/mapboxsdk/annotations/Icon;"); markerIdId = &jni::GetFieldID(env, *markerClass, "id", "J"); polylineClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/annotations/Polyline"); polylineClass = jni::NewGlobalRef(env, polylineClass).release(); polylineAlphaId = &jni::GetFieldID(env, *polylineClass, "alpha", "F"); polylineColorId = &jni::GetFieldID(env, *polylineClass, "color", "I"); polylineWidthId = &jni::GetFieldID(env, *polylineClass, "width", "F"); polylinePointsId = &jni::GetFieldID(env, *polylineClass, "points", "Ljava/util/List;"); polygonClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/annotations/Polygon"); polygonClass = jni::NewGlobalRef(env, polygonClass).release(); polygonAlphaId = &jni::GetFieldID(env, *polygonClass, "alpha", "F"); polygonFillColorId = &jni::GetFieldID(env, *polygonClass, "fillColor", "I"); polygonStrokeColorId = &jni::GetFieldID(env, *polygonClass, "strokeColor", "I"); polygonPointsId = &jni::GetFieldID(env, *polygonClass, "points", "Ljava/util/List;"); jni::jclass* listClass = &jni::FindClass(env, "java/util/List"); listToArrayId = &jni::GetMethodID(env, *listClass, "toArray", "()[Ljava/lang/Object;"); arrayListClass = &jni::FindClass(env, "java/util/ArrayList"); arrayListClass = jni::NewGlobalRef(env, arrayListClass).release(); arrayListConstructorId = &jni::GetMethodID(env, *arrayListClass, "", "()V"); arrayListAddId = &jni::GetMethodID(env, *arrayListClass, "add", "(Ljava/lang/Object;)Z"); projectedMetersClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/geometry/ProjectedMeters"); projectedMetersClass = jni::NewGlobalRef(env, projectedMetersClass).release(); projectedMetersConstructorId = &jni::GetMethodID(env, *projectedMetersClass, "", "(DD)V"); projectedMetersNorthingId = &jni::GetFieldID(env, *projectedMetersClass, "northing", "D"); projectedMetersEastingId = &jni::GetFieldID(env, *projectedMetersClass, "easting", "D"); pointFClass = &jni::FindClass(env, "android/graphics/PointF"); pointFClass = jni::NewGlobalRef(env, pointFClass).release(); pointFConstructorId = &jni::GetMethodID(env, *pointFClass, "", "(FF)V"); pointFXId = &jni::GetFieldID(env, *pointFClass, "x", "F"); pointFYId = &jni::GetFieldID(env, *pointFClass, "y", "F"); rectFClass = &jni::FindClass(env, "android/graphics/RectF"); rectFClass = jni::NewGlobalRef(env, rectFClass).release(); rectFConstructorId = &jni::GetMethodID(env, *rectFClass, "", "()V"); rectFLeftId = &jni::GetFieldID(env, *rectFClass, "left", "F"); rectFRightId = &jni::GetFieldID(env, *rectFClass, "right", "F"); rectFTopId = &jni::GetFieldID(env, *rectFClass, "top", "F"); rectFBottomId = &jni::GetFieldID(env, *rectFClass, "bottom", "F"); jni::jclass& nativeMapViewClass = jni::FindClass(env, "com/mapbox/mapboxsdk/maps/NativeMapView"); onInvalidateId = &jni::GetMethodID(env, nativeMapViewClass, "onInvalidate", "()V"); onMapChangedId = &jni::GetMethodID(env, nativeMapViewClass, "onMapChanged", "(I)V"); onFpsChangedId = &jni::GetMethodID(env, nativeMapViewClass, "onFpsChanged", "(D)V"); onSnapshotReadyId = &jni::GetMethodID(env, nativeMapViewClass, "onSnapshotReady","([B)V"); #define MAKE_NATIVE_METHOD(name, sig) jni::MakeNativeMethod( #name, sig ) jni::RegisterNatives(env, nativeMapViewClass, MAKE_NATIVE_METHOD(nativeCreate, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;FIJ)J"), MAKE_NATIVE_METHOD(nativeDestroy, "(J)V"), MAKE_NATIVE_METHOD(nativeInitializeDisplay, "(J)V"), MAKE_NATIVE_METHOD(nativeTerminateDisplay, "(J)V"), MAKE_NATIVE_METHOD(nativeInitializeContext, "(J)V"), MAKE_NATIVE_METHOD(nativeTerminateContext, "(J)V"), MAKE_NATIVE_METHOD(nativeCreateSurface, "(JLandroid/view/Surface;)V"), MAKE_NATIVE_METHOD(nativeDestroySurface, "(J)V"), MAKE_NATIVE_METHOD(nativeUpdate, "(J)V"), MAKE_NATIVE_METHOD(nativeRender, "(J)V"), MAKE_NATIVE_METHOD(nativeViewResize, "(JII)V"), MAKE_NATIVE_METHOD(nativeFramebufferResize, "(JII)V"), MAKE_NATIVE_METHOD(nativeAddClass, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeRemoveClass, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeHasClass, "(JLjava/lang/String;)Z"), MAKE_NATIVE_METHOD(nativeSetClasses, "(JLjava/util/List;)V"), MAKE_NATIVE_METHOD(nativeGetClasses, "(J)Ljava/util/List;"), MAKE_NATIVE_METHOD(nativeSetStyleUrl, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeSetStyleJson, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeGetStyleJson, "(J)Ljava/lang/String;"), MAKE_NATIVE_METHOD(nativeSetAccessToken, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeGetAccessToken, "(J)Ljava/lang/String;"), MAKE_NATIVE_METHOD(nativeCancelTransitions, "(J)V"), MAKE_NATIVE_METHOD(nativeSetGestureInProgress, "(JZ)V"), MAKE_NATIVE_METHOD(nativeMoveBy, "(JDDJ)V"), MAKE_NATIVE_METHOD(nativeSetLatLng, "(JDDJ)V"), MAKE_NATIVE_METHOD(nativeGetLatLng, "(J)Lcom/mapbox/mapboxsdk/geometry/LatLng;"), MAKE_NATIVE_METHOD(nativeResetPosition, "(J)V"), MAKE_NATIVE_METHOD(nativeGetCameraValues, "(J)[D"), MAKE_NATIVE_METHOD(nativeGetPitch, "(J)D"), MAKE_NATIVE_METHOD(nativeSetPitch, "(JDJ)V"), MAKE_NATIVE_METHOD(nativeScaleBy, "(JDDDJ)V"), MAKE_NATIVE_METHOD(nativeSetScale, "(JDDDJ)V"), MAKE_NATIVE_METHOD(nativeGetScale, "(J)D"), MAKE_NATIVE_METHOD(nativeSetZoom, "(JDJ)V"), MAKE_NATIVE_METHOD(nativeGetZoom, "(J)D"), MAKE_NATIVE_METHOD(nativeResetZoom, "(J)V"), MAKE_NATIVE_METHOD(nativeGetMinZoom, "(J)D"), MAKE_NATIVE_METHOD(nativeSetMinZoom, "(JD)V"), MAKE_NATIVE_METHOD(nativeGetMaxZoom, "(J)D"), MAKE_NATIVE_METHOD(nativeSetMaxZoom, "(JD)V"), MAKE_NATIVE_METHOD(nativeRotateBy, "(JDDDDJ)V"), MAKE_NATIVE_METHOD(nativeSetBearing, "(JDJ)V"), MAKE_NATIVE_METHOD(nativeSetBearingXY, "(JDDD)V"), MAKE_NATIVE_METHOD(nativeGetBearing, "(J)D"), MAKE_NATIVE_METHOD(nativeResetNorth, "(J)V"), MAKE_NATIVE_METHOD(nativeAddMarkers, "(J[Lcom/mapbox/mapboxsdk/annotations/Marker;)[J"), MAKE_NATIVE_METHOD(nativeAddPolylines, "(J[Lcom/mapbox/mapboxsdk/annotations/Polyline;)[J"), MAKE_NATIVE_METHOD(nativeAddPolygons, "(J[Lcom/mapbox/mapboxsdk/annotations/Polygon;)[J"), MAKE_NATIVE_METHOD(nativeUpdateMarker, "(JJDDLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeUpdatePolygon, "(JJLcom/mapbox/mapboxsdk/annotations/Polygon;)V"), MAKE_NATIVE_METHOD(nativeUpdatePolyline, "(JJLcom/mapbox/mapboxsdk/annotations/Polyline;)V"), MAKE_NATIVE_METHOD(nativeRemoveAnnotations, "(J[J)V"), MAKE_NATIVE_METHOD(nativeQueryPointAnnotations, "(JLandroid/graphics/RectF;)[J"), MAKE_NATIVE_METHOD(nativeAddAnnotationIcon, "(JLjava/lang/String;IIF[B)V"), MAKE_NATIVE_METHOD(nativeSetVisibleCoordinateBounds, "(J[Lcom/mapbox/mapboxsdk/geometry/LatLng;Landroid/graphics/RectF;DJ)V"), MAKE_NATIVE_METHOD(nativeOnLowMemory, "(J)V"), MAKE_NATIVE_METHOD(nativeSetDebug, "(JZ)V"), MAKE_NATIVE_METHOD(nativeToggleDebug, "(J)V"), MAKE_NATIVE_METHOD(nativeGetDebug, "(J)Z"), MAKE_NATIVE_METHOD(nativeIsFullyLoaded, "(J)Z"), MAKE_NATIVE_METHOD(nativeSetReachability, "(JZ)V"), MAKE_NATIVE_METHOD(nativeGetMetersPerPixelAtLatitude, "(JDD)D"), MAKE_NATIVE_METHOD(nativeProjectedMetersForLatLng, "(JDD)Lcom/mapbox/mapboxsdk/geometry/ProjectedMeters;"), MAKE_NATIVE_METHOD(nativeLatLngForProjectedMeters, "(JDD)Lcom/mapbox/mapboxsdk/geometry/LatLng;"), MAKE_NATIVE_METHOD(nativePixelForLatLng, "(JDD)Landroid/graphics/PointF;"), MAKE_NATIVE_METHOD(nativeLatLngForPixel, "(JFF)Lcom/mapbox/mapboxsdk/geometry/LatLng;"), MAKE_NATIVE_METHOD(nativeGetTopOffsetPixelsForAnnotationSymbol, "(JLjava/lang/String;)D"), MAKE_NATIVE_METHOD(nativeJumpTo, "(JDDDDD)V"), MAKE_NATIVE_METHOD(nativeEaseTo, "(JDDDJDDZ)V"), MAKE_NATIVE_METHOD(nativeFlyTo, "(JDDDJDD)V"), MAKE_NATIVE_METHOD(nativeGetLayer, "(JLjava/lang/String;)Lcom/mapbox/mapboxsdk/style/layers/Layer;"), MAKE_NATIVE_METHOD(nativeAddLayer, "(JJLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeRemoveLayer, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeGetSource, "(JLjava/lang/String;)Lcom/mapbox/mapboxsdk/style/sources/Source;"), MAKE_NATIVE_METHOD(nativeAddSource, "(JJ)V"), MAKE_NATIVE_METHOD(nativeRemoveSource, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeAddImage, "(JLjava/lang/String;IIF[B)V"), MAKE_NATIVE_METHOD(nativeRemoveImage, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeSetContentPadding, "(JDDDD)V"), MAKE_NATIVE_METHOD(nativeScheduleTakeSnapshot, "(J)V"), MAKE_NATIVE_METHOD(nativeQueryRenderedFeaturesForPoint, "(JFF[Ljava/lang/String;)[Lcom/mapbox/services/commons/geojson/Feature;"), MAKE_NATIVE_METHOD(nativeQueryRenderedFeaturesForBox, "(JFFFF[Ljava/lang/String;)[Lcom/mapbox/services/commons/geojson/Feature;"), MAKE_NATIVE_METHOD(nativeSetAPIBaseURL, "(JLjava/lang/String;)V") ); // Offline begin struct OfflineManager { static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager"; } struct ListOfflineRegionsCallback { static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$ListOfflineRegionsCallback"; } }; struct CreateOfflineRegionsCallback { static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$CreateOfflineRegionCallback"; } }; }; struct OfflineRegion { static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion"; } }; jni::Class offlineManagerClass = jni::Class::Find(env); offlineManagerClassPtrId = &jni::GetFieldID(env, offlineManagerClass, "mDefaultFileSourcePtr", "J"); jni::RegisterNatives(env, offlineManagerClass, MAKE_NATIVE_METHOD(createDefaultFileSource, "(Ljava/lang/String;Ljava/lang/String;J)J"), MAKE_NATIVE_METHOD(setAccessToken, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(getAccessToken, "(J)Ljava/lang/String;"), MAKE_NATIVE_METHOD(listOfflineRegions, "(JLcom/mapbox/mapboxsdk/offline/OfflineManager$ListOfflineRegionsCallback;)V"), MAKE_NATIVE_METHOD(createOfflineRegion, "(JLcom/mapbox/mapboxsdk/offline/OfflineRegionDefinition;[BLcom/mapbox/mapboxsdk/offline/OfflineManager$CreateOfflineRegionCallback;)V"), MAKE_NATIVE_METHOD(setOfflineMapboxTileCountLimit, "(JJ)V") ); jni::Class listOfflineRegionsCallbackClass = jni::Class::Find(env); listOnListMethodId = &jni::GetMethodID(env, listOfflineRegionsCallbackClass, "onList", "([Lcom/mapbox/mapboxsdk/offline/OfflineRegion;)V"); listOnErrorMethodId = &jni::GetMethodID(env, listOfflineRegionsCallbackClass, "onError", "(Ljava/lang/String;)V"); jni::Class createOfflineRegionCallbackClass = jni::Class::Find(env); createOnCreateMethodId = &jni::GetMethodID(env, createOfflineRegionCallbackClass, "onCreate", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion;)V"); createOnErrorMethodId = &jni::GetMethodID(env, createOfflineRegionCallbackClass, "onError", "(Ljava/lang/String;)V"); offlineRegionClass = &jni::FindClass(env, OfflineRegion::Name()); offlineRegionClass = jni::NewGlobalRef(env, offlineRegionClass).release(); offlineRegionConstructorId = &jni::GetMethodID(env, *offlineRegionClass, "", "()V"); offlineRegionOfflineManagerId = &jni::GetFieldID(env, *offlineRegionClass, "offlineManager", "Lcom/mapbox/mapboxsdk/offline/OfflineManager;"); offlineRegionIdId = &jni::GetFieldID(env, *offlineRegionClass, "mId", "J"); offlineRegionDefinitionId = &jni::GetFieldID(env, *offlineRegionClass, "mDefinition", "Lcom/mapbox/mapboxsdk/offline/OfflineRegionDefinition;"); offlineRegionMetadataId = &jni::GetFieldID(env, *offlineRegionClass, "mMetadata", "[B"); offlineRegionPtrId = &jni::GetFieldID(env, *offlineRegionClass, "mOfflineRegionPtr", "J"); jni::RegisterNatives(env, *offlineRegionClass, MAKE_NATIVE_METHOD(destroyOfflineRegion, "()V"), MAKE_NATIVE_METHOD(setOfflineRegionObserver, "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionObserver;)V"), MAKE_NATIVE_METHOD(setOfflineRegionDownloadState, "(I)V"), MAKE_NATIVE_METHOD(getOfflineRegionStatus, "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionStatusCallback;)V"), MAKE_NATIVE_METHOD(deleteOfflineRegion, "(Lcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionDeleteCallback;)V"), MAKE_NATIVE_METHOD(updateOfflineRegionMetadata, "([BLcom/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionUpdateMetadataCallback;)V") ); // This needs to be updated once we support more than one type of region definition offlineRegionDefinitionClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition"); offlineRegionDefinitionClass = jni::NewGlobalRef(env, offlineRegionDefinitionClass).release(); offlineRegionDefinitionConstructorId = &jni::GetMethodID(env, *offlineRegionDefinitionClass, "", "()V"); offlineRegionDefinitionStyleURLId = &jni::GetFieldID(env, *offlineRegionDefinitionClass, "styleURL", "Ljava/lang/String;"); offlineRegionDefinitionBoundsId = &jni::GetFieldID(env, *offlineRegionDefinitionClass, "bounds", "Lcom/mapbox/mapboxsdk/geometry/LatLngBounds;"); offlineRegionDefinitionMinZoomId = &jni::GetFieldID(env, *offlineRegionDefinitionClass, "minZoom", "D"); offlineRegionDefinitionMaxZoomId = &jni::GetFieldID(env, *offlineRegionDefinitionClass, "maxZoom", "D"); offlineRegionDefinitionPixelRatioId = &jni::GetFieldID(env, *offlineRegionDefinitionClass, "pixelRatio", "F"); jni::jclass* offlineRegionObserverClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionObserver"); offlineRegionObserveronStatusChangedId = &jni::GetMethodID(env, *offlineRegionObserverClass, "onStatusChanged", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegionStatus;)V"); offlineRegionObserveronErrorId = &jni::GetMethodID(env, *offlineRegionObserverClass, "onError", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegionError;)V"); offlineRegionObserveronLimitId = &jni::GetMethodID(env, *offlineRegionObserverClass, "mapboxTileCountLimitExceeded", "(J)V"); offlineRegionStatusClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/offline/OfflineRegionStatus"); offlineRegionStatusClass = jni::NewGlobalRef(env, offlineRegionStatusClass).release(); offlineRegionStatusConstructorId = &jni::GetMethodID(env, *offlineRegionStatusClass, "", "()V"); offlineRegionStatusDownloadStateId = &jni::GetFieldID(env, *offlineRegionStatusClass, "downloadState", "I"); offlineRegionStatusCompletedResourceCountId = &jni::GetFieldID(env, *offlineRegionStatusClass, "completedResourceCount", "J"); offlineRegionStatusCompletedResourceSizeId = &jni::GetFieldID(env, *offlineRegionStatusClass, "completedResourceSize", "J"); offlineRegionStatusCompletedTileCountId = &jni::GetFieldID(env, *offlineRegionStatusClass, "completedTileCount", "J"); offlineRegionStatusCompletedTileSizeId = &jni::GetFieldID(env, *offlineRegionStatusClass, "completedTileSize", "J"); offlineRegionStatusRequiredResourceCountId = &jni::GetFieldID(env, *offlineRegionStatusClass, "requiredResourceCount", "J"); offlineRegionStatusRequiredResourceCountIsPreciseId = &jni::GetFieldID(env, *offlineRegionStatusClass, "requiredResourceCountIsPrecise", "Z"); offlineRegionErrorClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/offline/OfflineRegionError"); offlineRegionErrorClass = jni::NewGlobalRef(env, offlineRegionErrorClass).release(); offlineRegionErrorConstructorId = &jni::GetMethodID(env, *offlineRegionErrorClass, "", "()V"); offlineRegionErrorReasonId = &jni::GetFieldID(env, *offlineRegionErrorClass, "reason", "Ljava/lang/String;"); offlineRegionErrorMessageId = &jni::GetFieldID(env, *offlineRegionErrorClass, "message", "Ljava/lang/String;"); jni::jclass* offlineRegionStatusCallbackClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionStatusCallback"); offlineRegionStatusOnStatusId = &jni::GetMethodID(env, *offlineRegionStatusCallbackClass, "onStatus", "(Lcom/mapbox/mapboxsdk/offline/OfflineRegionStatus;)V"); offlineRegionStatusOnErrorId = &jni::GetMethodID(env, *offlineRegionStatusCallbackClass, "onError", "(Ljava/lang/String;)V"); jni::jclass* offlineRegionDeleteCallbackClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionDeleteCallback"); offlineRegionDeleteOnDeleteId = &jni::GetMethodID(env, *offlineRegionDeleteCallbackClass, "onDelete", "()V"); offlineRegionDeleteOnErrorId = &jni::GetMethodID(env, *offlineRegionDeleteCallbackClass, "onError", "(Ljava/lang/String;)V"); jni::jclass* offlineRegionUpdateMetadataCallbackClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionUpdateMetadataCallback"); updateMetadataOnUpdateMethodId = &jni::GetMethodID(env, *offlineRegionUpdateMetadataCallbackClass, "onUpdate", "([B)V"); updateMetadataOnErrorMethodId = &jni::GetMethodID(env, *offlineRegionUpdateMetadataCallbackClass, "onError", "(Ljava/lang/String;)V"); // Offline end char release[PROP_VALUE_MAX] = ""; __system_property_get("ro.build.version.release", release); androidRelease = std::string(release); return JNI_VERSION_1_6; }