diff options
Diffstat (limited to 'platform/android/src')
108 files changed, 4408 insertions, 2509 deletions
diff --git a/platform/android/src/annotation/marker.cpp b/platform/android/src/annotation/marker.cpp new file mode 100644 index 0000000000..a1fe436dbd --- /dev/null +++ b/platform/android/src/annotation/marker.cpp @@ -0,0 +1,31 @@ +#include "marker.hpp" + +namespace mbgl { +namespace android { + +jni::Class<Marker> Marker::javaClass; + +mbgl::Point<double> Marker::getPosition(jni::JNIEnv& env, jni::Object<Marker> marker) { + static auto positionField = Marker::javaClass.GetField<jni::Object<LatLng>>(env, "position"); + auto jPosition = marker.Get(env, positionField); + auto position = LatLng::getGeometry(env, jPosition); + jni::DeleteLocalRef(env, jPosition); + return position; +} + +std::string Marker::getIconId(jni::JNIEnv& env, jni::Object<Marker> marker) { + static auto iconIdField = Marker::javaClass.GetField<jni::String>(env, "iconId"); + auto jIconId = marker.Get(env, iconIdField); + auto iconId = jni::Make<std::string>(env, jIconId); + jni::DeleteLocalRef(env, jIconId); + return iconId; +} + +void Marker::registerNative(jni::JNIEnv& env) { + // Lookup the class + Marker::javaClass = *jni::Class<Marker>::Find(env).NewGlobalRef(env).release(); +} + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/annotation/marker.hpp b/platform/android/src/annotation/marker.hpp new file mode 100644 index 0000000000..b11a225245 --- /dev/null +++ b/platform/android/src/annotation/marker.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <jni/jni.hpp> + +#include <string> + +#include "../geometry/lat_lng.hpp" + +namespace mbgl { +namespace android { + +class Marker : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/annotations/Marker"; }; + + static jni::Class<Marker> javaClass; + + static mbgl::Point<double> getPosition(jni::JNIEnv&, jni::Object<Marker>); + + static std::string getIconId(jni::JNIEnv&, jni::Object<Marker>); + + static void registerNative(jni::JNIEnv&); + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/annotation/multi_point.hpp b/platform/android/src/annotation/multi_point.hpp new file mode 100644 index 0000000000..e1152dfd60 --- /dev/null +++ b/platform/android/src/annotation/multi_point.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <jni/jni.hpp> + +#include "../geometry/lat_lng.hpp" +#include "../java/util.hpp" + +namespace mbgl { +namespace android { + +class MultiPoint : protected mbgl::util::noncopyable { + +protected: + + template <class Geometry> + static Geometry toGeometry(JNIEnv& env, jni::Object<java::util::List> pointsList) { + NullCheck(env, &pointsList); + auto jarray = java::util::List::toArray<LatLng>(env, pointsList); + NullCheck(env, &jarray); + + std::size_t size = jarray.Length(env); + + Geometry geometry; + geometry.reserve(size); + + for (std::size_t i = 0; i < size; i++) { + auto latLng = jarray.Get(env, i); + NullCheck(env, &latLng); + + geometry.push_back(LatLng::getGeometry(env, latLng)); + + jni::DeleteLocalRef(env, latLng); + } + + jni::DeleteLocalRef(env, jarray); + return geometry; + } +}; + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/annotation/polygon.cpp b/platform/android/src/annotation/polygon.cpp new file mode 100644 index 0000000000..ba82fc34dc --- /dev/null +++ b/platform/android/src/annotation/polygon.cpp @@ -0,0 +1,49 @@ +#include "polygon.hpp" + +#include "../conversion/color.hpp" + +namespace mbgl { +namespace android { + +jni::Class<Polygon> Polygon::javaClass; + +mbgl::FillAnnotation Polygon::toAnnotation(jni::JNIEnv& env, jni::Object<Polygon> polygon) { + auto points = Polygon::getPoints(env, polygon); + + mbgl::FillAnnotation annotation { mbgl::Polygon<double> { MultiPoint::toGeometry<mbgl::LinearRing<double>>(env, points) } }; + annotation.opacity = { Polygon::getOpacity(env, polygon) }; + annotation.color = { Polygon::getFillColor(env, polygon) }; + annotation.outlineColor = { Polygon::getOutlineColor(env, polygon) }; + + jni::DeleteLocalRef(env, points); + + return annotation; +} + +jni::Object<java::util::List> Polygon::getPoints(jni::JNIEnv& env, jni::Object<Polygon> polygon) { + static auto field = Polygon::javaClass.GetField<jni::Object<java::util::List>>(env, "points"); + return polygon.Get(env, field); +} + +float Polygon::getOpacity(jni::JNIEnv& env, jni::Object<Polygon> polygon) { + static auto field = Polygon::javaClass.GetField<float>(env, "alpha"); + return polygon.Get(env, field); +} + +mbgl::Color Polygon::getFillColor(jni::JNIEnv& env, jni::Object<Polygon> polygon) { + static auto field = Polygon::javaClass.GetField<int>(env, "fillColor"); + return *conversion::convert<mbgl::Color, int>(env, polygon.Get(env, field)); +} + +mbgl::Color Polygon::getOutlineColor(jni::JNIEnv& env, jni::Object<Polygon> polygon) { + static auto field = Polygon::javaClass.GetField<int>(env, "strokeColor"); + return *conversion::convert<mbgl::Color, int>(env, polygon.Get(env, field)); +} + +void Polygon::registerNative(jni::JNIEnv& env) { + Polygon::javaClass = *jni::Class<Polygon>::Find(env).NewGlobalRef(env).release(); +} + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/annotation/polygon.hpp b/platform/android/src/annotation/polygon.hpp new file mode 100644 index 0000000000..658aa5344b --- /dev/null +++ b/platform/android/src/annotation/polygon.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <mbgl/annotation/annotation.hpp> +#include <mbgl/util/color.hpp> + +#include <string> + +#include "multi_point.hpp" + +#include "../geometry/lat_lng.hpp" +#include "../java/util.hpp" + +namespace mbgl { +namespace android { + +class Polygon : private MultiPoint { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/annotations/Polygon"; }; + + static jni::Class<Polygon> javaClass; + + static mbgl::FillAnnotation toAnnotation(jni::JNIEnv&, jni::Object<Polygon>); + + static void registerNative(jni::JNIEnv&); + +private: + + static jni::Object<java::util::List> getPoints(jni::JNIEnv&, jni::Object<Polygon>); + + static float getOpacity(jni::JNIEnv&, jni::Object<Polygon>); + + static mbgl::Color getFillColor(jni::JNIEnv&, jni::Object<Polygon>); + + static mbgl::Color getOutlineColor(jni::JNIEnv&, jni::Object<Polygon>); +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/annotation/polyline.cpp b/platform/android/src/annotation/polyline.cpp new file mode 100644 index 0000000000..3723dc1871 --- /dev/null +++ b/platform/android/src/annotation/polyline.cpp @@ -0,0 +1,49 @@ +#include "polyline.hpp" + +#include "../conversion/color.hpp" + +namespace mbgl { +namespace android { + +jni::Class<Polyline> Polyline::javaClass; + +mbgl::LineAnnotation Polyline::toAnnotation(jni::JNIEnv& env, jni::Object<Polyline> polyline) { + auto points = Polyline::getPoints(env, polyline); + + mbgl::LineAnnotation annotation { MultiPoint::toGeometry<mbgl::LineString<double>>(env, points) }; + annotation.opacity = { Polyline::getOpacity(env, polyline) }; + annotation.color = { Polyline::getColor(env, polyline) }; + annotation.width = { Polyline::getWidth(env, polyline) }; + + jni::DeleteLocalRef(env, points); + + return annotation; +} + +jni::Object<java::util::List> Polyline::getPoints(jni::JNIEnv& env, jni::Object<Polyline> polyline) { + static auto field = Polyline::javaClass.GetField<jni::Object<java::util::List>>(env, "points"); + return polyline.Get(env, field); +} + +float Polyline::getOpacity(jni::JNIEnv& env, jni::Object<Polyline> polyline) { + static auto field = Polyline::javaClass.GetField<float>(env, "alpha"); + return polyline.Get(env, field); +} + +mbgl::Color Polyline::getColor(jni::JNIEnv& env, jni::Object<Polyline> polyline) { + static auto field = Polyline::javaClass.GetField<int>(env, "color"); + return *conversion::convert<mbgl::Color, int>(env, polyline.Get(env, field)); +} + +float Polyline::getWidth(jni::JNIEnv& env, jni::Object<Polyline> polyline) { + static auto field = Polyline::javaClass.GetField<float>(env, "width"); + return polyline.Get(env, field); +} + +void Polyline::registerNative(jni::JNIEnv& env) { + Polyline::javaClass = *jni::Class<Polyline>::Find(env).NewGlobalRef(env).release(); +} + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/annotation/polyline.hpp b/platform/android/src/annotation/polyline.hpp new file mode 100644 index 0000000000..bcc616a5f7 --- /dev/null +++ b/platform/android/src/annotation/polyline.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <mbgl/annotation/annotation.hpp> +#include <mbgl/util/color.hpp> + +#include <string> + +#include "multi_point.hpp" + +#include "../geometry/lat_lng.hpp" +#include "../java/util.hpp" + +namespace mbgl { +namespace android { + +class Polyline : private MultiPoint { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/annotations/Polyline"; }; + + static jni::Class<Polyline> javaClass; + + static mbgl::LineAnnotation toAnnotation(jni::JNIEnv&, jni::Object<Polyline>); + + static void registerNative(jni::JNIEnv&); + +private: + + static jni::Object<java::util::List> getPoints(jni::JNIEnv&, jni::Object<Polyline>); + + static float getOpacity(jni::JNIEnv&, jni::Object<Polyline>); + + static mbgl::Color getColor(jni::JNIEnv&, jni::Object<Polyline>); + + static float getWidth(jni::JNIEnv&, jni::Object<Polyline>); +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/bitmap.cpp b/platform/android/src/bitmap.cpp new file mode 100644 index 0000000000..50088116f4 --- /dev/null +++ b/platform/android/src/bitmap.cpp @@ -0,0 +1,132 @@ +#include "bitmap.hpp" + +#include <android/bitmap.h> + +namespace mbgl { +namespace android { + +class PixelGuard { +public: + PixelGuard(jni::JNIEnv& env_, jni::Object<Bitmap> bitmap_) : env(env_), bitmap(bitmap_) { + const int result = AndroidBitmap_lockPixels(&env, jni::Unwrap(*bitmap), + reinterpret_cast<void**>(&address)); + if (result != ANDROID_BITMAP_RESULT_SUCCESS) { + throw std::runtime_error("bitmap decoding: could not lock pixels"); + } + } + ~PixelGuard() { + const int result = AndroidBitmap_unlockPixels(&env, jni::Unwrap(*bitmap)); + if (result != ANDROID_BITMAP_RESULT_SUCCESS) { + throw std::runtime_error("bitmap decoding: could not unlock pixels"); + } + } + + auto* get() { + return address; + } + + const auto* get() const { + return address; + } + +private: + jni::JNIEnv& env; + jni::Object<Bitmap> bitmap; + uint8_t* address; +}; + +void Bitmap::Config::registerNative(jni::JNIEnv& env) { + _class = *jni::Class<Config>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<Bitmap::Config> Bitmap::Config::_class; + +jni::Object<Bitmap::Config> Bitmap::Config::Create(jni::JNIEnv& env, Value value) { + switch (value) { + case ALPHA_8: + return _class.Get(env, + jni::StaticField<Config, jni::Object<Config>>(env, _class, "ALPHA_8")); + case ARGB_4444: + return _class.Get(env, + jni::StaticField<Config, jni::Object<Config>>(env, _class, "ARGB_4444")); + case ARGB_8888: + return _class.Get(env, + jni::StaticField<Config, jni::Object<Config>>(env, _class, "ARGB_8888")); + case RGB_565: + return _class.Get(env, + jni::StaticField<Config, jni::Object<Config>>(env, _class, "RGB_565")); + default: + throw std::runtime_error("invalid enum value for Bitmap.Config"); + } +} + +void Bitmap::registerNative(jni::JNIEnv& env) { + _class = *jni::Class<Bitmap>::Find(env).NewGlobalRef(env).release(); + Config::registerNative(env); +} + +jni::Class<Bitmap> Bitmap::_class; + +jni::Object<Bitmap> Bitmap::CreateBitmap(jni::JNIEnv& env, + jni::jint width, + jni::jint height, + jni::Object<Config> config) { + using Signature = jni::Object<Bitmap>(jni::jint, jni::jint, jni::Object<Config>); + auto method = _class.GetStaticMethod<Signature>(env, "createBitmap"); + return _class.Call(env, method, width, height, config); +} + +jni::Object<Bitmap> Bitmap::CreateBitmap(jni::JNIEnv& env, const PremultipliedImage& image) { + auto bitmap = CreateBitmap(env, image.size.width, image.size.height, Config::ARGB_8888); + + AndroidBitmapInfo info; + const int result = AndroidBitmap_getInfo(&env, jni::Unwrap(*bitmap), &info); + if (result != ANDROID_BITMAP_RESULT_SUCCESS) { + // TODO: more specific information + throw std::runtime_error("bitmap creation: couldn't get bitmap info"); + } + + assert(info.width == image.size.width); + assert(info.height == image.size.height); + assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888); + + PixelGuard guard(env, bitmap); + + // Copy the PremultipliedImage into the Android Bitmap + for (uint32_t y = 0; y < image.size.height; y++) { + auto begin = image.data.get() + y * image.stride(); + std::copy(begin, begin + image.stride(), guard.get() + y * info.stride); + } + + return bitmap; +} + +PremultipliedImage Bitmap::GetImage(jni::JNIEnv& env, jni::Object<Bitmap> bitmap) { + AndroidBitmapInfo info; + const int result = AndroidBitmap_getInfo(&env, jni::Unwrap(*bitmap), &info); + if (result != ANDROID_BITMAP_RESULT_SUCCESS) { + // TODO: more specific information + throw std::runtime_error("bitmap decoding: couldn't get bitmap info"); + } + + if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { + // TODO: convert + throw std::runtime_error("bitmap decoding: bitmap format invalid"); + } + + const PixelGuard guard(env, bitmap); + + // Copy the Android Bitmap into the PremultipliedImage. + auto pixels = + std::make_unique<uint8_t[]>(info.width * info.height * PremultipliedImage::channels); + for (uint32_t y = 0; y < info.height; y++) { + auto begin = guard.get() + y * info.stride; + std::copy(begin, begin + info.width * PremultipliedImage::channels, + pixels.get() + y * info.width * PremultipliedImage::channels); + } + + return { Size{ info.width, info.height }, std::move(pixels) }; +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/bitmap.hpp b/platform/android/src/bitmap.hpp new file mode 100644 index 0000000000..f64f42ae87 --- /dev/null +++ b/platform/android/src/bitmap.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include <mbgl/util/image.hpp> + +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class Bitmap { +public: + class Config { + public: + static constexpr auto Name() { + return "android/graphics/Bitmap$Config"; + }; + static void registerNative(jni::JNIEnv&); + + enum Value { + ALPHA_8, + ARGB_4444, + ARGB_8888, + RGB_565, + }; + + static jni::Object<Config> Create(jni::JNIEnv&, Value); + + private: + static jni::Class<Config> _class; + }; + + static constexpr auto Name() { + return "android/graphics/Bitmap"; + }; + static void registerNative(jni::JNIEnv&); + + static jni::Object<Bitmap> + CreateBitmap(jni::JNIEnv&, jni::jint width, jni::jint height, jni::Object<Config>); + static jni::Object<Bitmap> + CreateBitmap(jni::JNIEnv& env, jni::jint width, jni::jint height, Config::Value config) { + return CreateBitmap(env, width, height, Config::Create(env, config)); + } + + static PremultipliedImage GetImage(jni::JNIEnv&, jni::Object<Bitmap>); + static jni::Object<Bitmap> CreateBitmap(jni::JNIEnv&, const PremultipliedImage&); + +private: + static jni::Class<Bitmap> _class; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/bitmap_factory.cpp b/platform/android/src/bitmap_factory.cpp new file mode 100644 index 0000000000..7d9b068b20 --- /dev/null +++ b/platform/android/src/bitmap_factory.cpp @@ -0,0 +1,25 @@ +#include "bitmap_factory.hpp" + +namespace mbgl { +namespace android { + +void BitmapFactory::registerNative(jni::JNIEnv& env) { + _class = *jni::Class<BitmapFactory>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<BitmapFactory> BitmapFactory::_class; + +jni::Object<Bitmap> BitmapFactory::DecodeByteArray(jni::JNIEnv& env, + jni::Array<jni::jbyte> data, + jni::jint offset, + jni::jint length) { + + // Images are loaded with ARGB_8888 config, and premultiplied by default, which is exactly + // what we want, so we're not providing a BitmapFactory.Options object. + using Signature = jni::Object<Bitmap>(jni::Array<jni::jbyte>, jni::jint, jni::jint); + auto method = _class.GetStaticMethod<Signature>(env, "decodeByteArray"); + return _class.Call(env, method, data, offset, length); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/bitmap_factory.hpp b/platform/android/src/bitmap_factory.hpp new file mode 100644 index 0000000000..b0e7198260 --- /dev/null +++ b/platform/android/src/bitmap_factory.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include <jni/jni.hpp> + +#include "bitmap.hpp" + +namespace mbgl { +namespace android { + +class BitmapFactory { +public: + static constexpr auto Name() { + return "android/graphics/BitmapFactory"; + }; + static void registerNative(jni::JNIEnv&); + + static jni::Object<Bitmap> + DecodeByteArray(jni::JNIEnv&, jni::Array<jni::jbyte> data, jni::jint offset, jni::jint length); + +private: + static jni::Class<BitmapFactory> _class; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/connectivity_listener.cpp b/platform/android/src/connectivity_listener.cpp index cc2f0a4a81..5b1c0a86e4 100644 --- a/platform/android/src/connectivity_listener.cpp +++ b/platform/android/src/connectivity_listener.cpp @@ -22,12 +22,12 @@ namespace android { jni::Class<ConnectivityListener> ConnectivityListener::javaClass; void ConnectivityListener::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class ConnectivityListener::javaClass = *jni::Class<ConnectivityListener>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<ConnectivityListener>( env, ConnectivityListener::javaClass, @@ -39,5 +39,5 @@ namespace android { ); } -} //android -} //mbgl
\ No newline at end of file +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/connectivity_listener.hpp b/platform/android/src/connectivity_listener.hpp index d457dcfd5a..b0d655d027 100644 --- a/platform/android/src/connectivity_listener.hpp +++ b/platform/android/src/connectivity_listener.hpp @@ -27,9 +27,5 @@ public: }; -} //android -} //mbgl - - - - +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/conversion/collection.hpp b/platform/android/src/conversion/collection.hpp index 4256d5f969..da5eed64d2 100644 --- a/platform/android/src/conversion/collection.hpp +++ b/platform/android/src/conversion/collection.hpp @@ -32,6 +32,20 @@ inline std::vector<std::string> toVector(JNIEnv& env, jni::jarray<jni::jobject>& for (std::size_t i = 0; i < len; i++) { jni::jstring* jstr = reinterpret_cast<jni::jstring*>(jni::GetObjectArrayElement(env, array, i)); vector.push_back(*convert<std::string, jni::String>(env, jni::String(jstr))); + jni::DeleteLocalRef(env, jstr); + } + + return vector; +} + +inline std::vector<std::string> toVector(JNIEnv& env, jni::Array<jni::String> array) { + std::vector<std::string> vector; + std::size_t len = array.Length(env); + + for (std::size_t i = 0; i < len; i++) { + jni::String jstr = array.Get(env, i); + vector.push_back(*convert<std::string, jni::String>(env, jstr)); + jni::DeleteLocalRef(env, jstr); } return vector; diff --git a/platform/android/src/conversion/color.hpp b/platform/android/src/conversion/color.hpp new file mode 100644 index 0000000000..40aa68d4a9 --- /dev/null +++ b/platform/android/src/conversion/color.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "conversion.hpp" + +#include <mbgl/util/color.hpp> + +namespace mbgl { +namespace android { +namespace conversion { + +template <> +struct Converter<mbgl::Color, int> { + Result<mbgl::Color> operator()(jni::JNIEnv&, const int& color) const { + float r = (color >> 16) & 0xFF; + float g = (color >> 8) & 0xFF; + float b = (color) & 0xFF; + float a = (color >> 24) & 0xFF; + return { mbgl::Color( r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f ) }; + } +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/platform/android/src/conversion/constant.hpp b/platform/android/src/conversion/constant.hpp index f1a8171b99..2a0b710f73 100644 --- a/platform/android/src/conversion/constant.hpp +++ b/platform/android/src/conversion/constant.hpp @@ -76,7 +76,7 @@ struct Converter<jni::jobject*, T, typename std::enable_if<std::is_integral<T>:: } }; -//TODO: convert integral types to primitive jni types +// TODO: convert integral types to primitive jni types template <> struct Converter<jni::jobject*, std::string> { diff --git a/platform/android/src/conversion/conversion.hpp b/platform/android/src/conversion/conversion.hpp index 1277f3f67e..d1766f9755 100644 --- a/platform/android/src/conversion/conversion.hpp +++ b/platform/android/src/conversion/conversion.hpp @@ -47,4 +47,4 @@ Result<T> convert(jni::JNIEnv& env, const V& value, Args&&...args) { } // namespace conversion } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp new file mode 100644 index 0000000000..30e1ff50fe --- /dev/null +++ b/platform/android/src/file_source.cpp @@ -0,0 +1,106 @@ +#include "file_source.hpp" + +#include <mbgl/util/logging.hpp> + +#include <string> + +#include "jni/generic_global_ref_deleter.hpp" + + +namespace mbgl { +namespace android { + +// FileSource // + +FileSource::FileSource(jni::JNIEnv& _env, jni::String accessToken, jni::String _cachePath, jni::String _apkPath) { + // Create a core default file source + fileSource = std::make_unique<mbgl::DefaultFileSource>( + jni::Make<std::string>(_env, _cachePath) + "/mbgl-offline.db", + jni::Make<std::string>(_env, _apkPath)); + + // Set access token + fileSource->setAccessToken(jni::Make<std::string>(_env, accessToken)); +} + +FileSource::~FileSource() { +} + +jni::String FileSource::getAccessToken(jni::JNIEnv& env) { + return jni::Make<jni::String>(env, fileSource->getAccessToken()); +} + +void FileSource::setAccessToken(jni::JNIEnv& env, jni::String token) { + fileSource->setAccessToken(jni::Make<std::string>(env, token)); +} + +void FileSource::setAPIBaseUrl(jni::JNIEnv& env, jni::String url) { + fileSource->setAPIBaseURL(jni::Make<std::string>(env, url)); +} + +void FileSource::setResourceTransform(jni::JNIEnv& env, jni::Object<FileSource::ResourceTransformCallback> transformCallback) { + if (transformCallback) { + // Launch transformCallback + fileSource->setResourceTransform([ + // Capture the ResourceTransformCallback object as a managed global into + // the lambda. It is released automatically when we're setting a new ResourceTransform in + // a subsequent call. + // Note: we're converting it to shared_ptr because this lambda is converted to a std::function, + // which requires copyability of its captured variables. + callback = std::shared_ptr<jni::jobject>(transformCallback.NewGlobalRef(env).release()->Get(), GenericGlobalRefDeleter()), + env + ](mbgl::Resource::Kind kind, std::string&& url_) { + return FileSource::ResourceTransformCallback::onURL(const_cast<jni::JNIEnv&>(env), jni::Object<FileSource::ResourceTransformCallback>(*callback), int(kind), url_); + }); + } else { + // Reset the callback + fileSource->setResourceTransform(nullptr); + } +} + +jni::Class<FileSource> FileSource::javaClass; + +FileSource* FileSource::getNativePeer(jni::JNIEnv& env, jni::Object<FileSource> jFileSource) { + static auto field = FileSource::javaClass.GetField<jlong>(env, "nativePtr"); + return reinterpret_cast<FileSource *>(jFileSource.Get(env, field)); +} + +mbgl::DefaultFileSource& FileSource::getDefaultFileSource(jni::JNIEnv& env, jni::Object<FileSource> jFileSource) { + FileSource* fileSource = FileSource::getNativePeer(env, jFileSource); + assert(fileSource != nullptr); + return *fileSource->fileSource; +} + +void FileSource::registerNative(jni::JNIEnv& env) { + //Register classes + FileSource::javaClass = *jni::Class<FileSource>::Find(env).NewGlobalRef(env).release(); + FileSource::ResourceTransformCallback::javaClass = *jni::Class<FileSource::ResourceTransformCallback>::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<FileSource>( + env, FileSource::javaClass, "nativePtr", + std::make_unique<FileSource, JNIEnv&, jni::String, jni::String, jni::String>, + "initialize", + "finalize", + METHOD(&FileSource::getAccessToken, "getAccessToken"), + METHOD(&FileSource::setAccessToken, "setAccessToken"), + METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"), + METHOD(&FileSource::setResourceTransform, "setResourceTransform") + ); +} + + +// FileSource::ResourceTransformCallback // + +jni::Class<FileSource::ResourceTransformCallback> FileSource::ResourceTransformCallback::javaClass; + +std::string FileSource::ResourceTransformCallback::onURL(jni::JNIEnv& env, jni::Object<FileSource::ResourceTransformCallback> callback, int kind, std::string url_) { + static auto method = FileSource::ResourceTransformCallback::javaClass.GetMethod<jni::String (jni::jint, jni::String)>(env, "onURL"); + auto url = jni::Make<jni::String>(env, url_); + url = callback.Call(env, method, kind, url); + return jni::Make<std::string>(env, url); +} + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/file_source.hpp b/platform/android/src/file_source.hpp new file mode 100644 index 0000000000..073e393e05 --- /dev/null +++ b/platform/android/src/file_source.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include <mbgl/storage/default_file_source.hpp> + +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +/** + * Peer class for the Android FileSource holder. Ensures that a single DefaultFileSource is used + */ +class FileSource { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/storage/FileSource"; }; + + struct ResourceTransformCallback { + static constexpr auto Name() { return "com/mapbox/mapboxsdk/storage/FileSource$ResourceTransformCallback"; } + + static std::string onURL(jni::JNIEnv&, jni::Object<FileSource::ResourceTransformCallback>, int, std::string); + + static jni::Class<ResourceTransformCallback> javaClass; + }; + + FileSource(jni::JNIEnv&, jni::String, jni::String, jni::String); + + ~FileSource(); + + jni::String getAccessToken(jni::JNIEnv&); + + void setAccessToken(jni::JNIEnv&, jni::String); + + void setAPIBaseUrl(jni::JNIEnv&, jni::String); + + void setResourceTransform(jni::JNIEnv&, jni::Object<FileSource::ResourceTransformCallback>); + + static jni::Class<FileSource> javaClass; + + static FileSource* getNativePeer(jni::JNIEnv&, jni::Object<FileSource>); + + static mbgl::DefaultFileSource& getDefaultFileSource(jni::JNIEnv&, jni::Object<FileSource>); + + static void registerNative(jni::JNIEnv&); + +private: + + std::unique_ptr<mbgl::DefaultFileSource> fileSource; +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/conversion/feature.hpp b/platform/android/src/geometry/conversion/feature.hpp index f0c77c3389..921138e859 100644 --- a/platform/android/src/geometry/conversion/feature.hpp +++ b/platform/android/src/geometry/conversion/feature.hpp @@ -3,6 +3,7 @@ #include "../../conversion/constant.hpp" #include "../../conversion/conversion.hpp" #include "geometry.hpp" +#include "../../gson/json_object.hpp" #include <mbgl/util/feature.hpp> #include <mapbox/variant.hpp> @@ -10,6 +11,7 @@ #include <jni/jni.hpp> #include "../../jni/local_object.hpp" +#include "../feature.hpp" #include <string> #include <array> @@ -64,7 +66,7 @@ public: static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release(); static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/Boolean;)V"); - //Create JsonPrimitive + // Create JsonPrimitive jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, bool>(env, value)); jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, *converted); @@ -78,7 +80,7 @@ public: static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release(); static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/String;)V"); - //Create JsonPrimitive + // Create JsonPrimitive jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, value)); jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, converted.get()); @@ -93,7 +95,7 @@ public: static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release(); static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/Number;)V"); - //Create JsonPrimitive + // Create JsonPrimitive jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, Number>(env, value)); jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, converted.get()); @@ -109,10 +111,10 @@ public: static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");; static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Lcom/google/gson/JsonElement;)V"); - //Create json array + // Create json array jni::jobject* jarray = &jni::NewObject(env, *javaClass, *constructor); - //Add values + // Add values for (const auto &v : values) { jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(v, *this)); jni::CallMethod<void>(env, jarray, *add, converted.get()); @@ -125,15 +127,15 @@ public: * Json Object */ jni::jobject* operator()(const std::unordered_map<std::string, mbgl::Value> &value) const { - //TODO: clean up duplication here + // TODO: clean up duplication here static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonObject")).release(); static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");; static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Ljava/lang/String;Lcom/google/gson/JsonElement;)V"); - //Create json object + // Create json object jni::jobject* jsonObject = &jni::NewObject(env, *javaClass, *constructor); - //Add items + // Add items for (auto &item : value) { jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(item.second, *this)); jni::LocalObject<jni::jobject> key = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, item.first)); @@ -151,10 +153,10 @@ struct Converter<jni::jobject*, std::unordered_map<std::string, mbgl::Value>> { static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");; static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Ljava/lang/String;Lcom/google/gson/JsonElement;)V"); - //Create json object + // Create json object jni::jobject* jsonObject = &jni::NewObject(env, *javaClass, *constructor); - //Add items + // Add items PropertyValueEvaluator evaluator {env}; for (auto &item : value) { jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(item.second, evaluator)); @@ -168,39 +170,45 @@ struct Converter<jni::jobject*, std::unordered_map<std::string, mbgl::Value>> { template <> -struct Converter<jni::jobject*, mbgl::Feature> { - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::Feature& value) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Feature")).release(); - static jni::jmethodID* fromGeometry = &jni::GetStaticMethodID(env, *javaClass, "fromGeometry", "(Lcom/mapbox/services/commons/geojson/Geometry;Lcom/google/gson/JsonObject;Ljava/lang/String;)Lcom/mapbox/services/commons/geojson/Feature;"); +struct Converter<jni::Object<Feature>, mbgl::Feature> { + Result<jni::Object<Feature>> operator()(jni::JNIEnv& env, const mbgl::Feature& value) const { - //Convert Id + // Convert Id FeatureIdVisitor idEvaluator; std::string id = (value.id) ? mapbox::geometry::identifier::visit(value.id.value(), idEvaluator) : ""; - jni::LocalObject<jni::jobject> jid = jni::NewLocalObject(env, *convert<jni::jobject*>(env, id)); + auto jid = jni::Make<jni::String>(env, id); - //Convert properties - jni::LocalObject<jni::jobject> properties = jni::NewLocalObject(env, *convert<jni::jobject*>(env, value.properties)); + // Convert properties + auto properties = jni::Object<JsonObject>(*convert<jni::jobject*>(env, value.properties)); - //Convert geometry - jni::LocalObject<jni::jobject> geometry = jni::NewLocalObject(env, *convert<jni::jobject*>(env, value.geometry)); + // Convert geometry + auto geometry = jni::Object<Geometry>(*convert<jni::jobject*>(env, value.geometry)); - //Create feature - return {reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometry, geometry.get(), properties.get(), jid.get()))}; + // Create feature + auto feature = Feature::fromGeometry(env, geometry, properties, jid); + + //Cleanup + jni::DeleteLocalRef(env, jid); + jni::DeleteLocalRef(env, geometry); + jni::DeleteLocalRef(env, properties); + + return feature; } }; template <> -struct Converter<jni::jarray<jni::jobject>*, std::vector<mbgl::Feature>> { - Result<jni::jarray<jni::jobject>*> operator()(jni::JNIEnv& env, const std::vector<mbgl::Feature>& value) const { - static jni::jclass* featureClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Feature")).release(); - jni::jarray<jni::jobject>& jarray = jni::NewObjectArray(env, value.size(), *featureClass); +struct Converter<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>> { + Result<jni::Array<jni::Object<Feature>>> operator()(jni::JNIEnv& env, const std::vector<mbgl::Feature>& value) const { + + auto features = jni::Array<jni::Object<Feature>>::New(env, value.size(), Feature::javaClass); for(size_t i = 0; i < value.size(); i = i + 1) { - jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, mbgl::Feature>(env, value.at(i))); - jni::SetObjectArrayElement(env, jarray, i, converted.get()); + auto converted = *convert<jni::Object<Feature>, mbgl::Feature>(env, value.at(i)); + features.Set(env, i, converted); + jni::DeleteLocalRef(env, converted); } - return {&jarray}; + return {features}; } }; diff --git a/platform/android/src/geometry/conversion/geometry.hpp b/platform/android/src/geometry/conversion/geometry.hpp index 385ba9034e..2ca63e2c11 100644 --- a/platform/android/src/geometry/conversion/geometry.hpp +++ b/platform/android/src/geometry/conversion/geometry.hpp @@ -27,7 +27,7 @@ public: static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Point")).release(); static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([D)Lcom/mapbox/services/commons/geojson/Point;"); - //Create Point + // Create Point jni::LocalObject<jni::jarray<jni::jdouble>> position = jni::NewLocalObject(env, toGeoJsonPosition(env, geometry.x, geometry.y)); return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, position.get())); } @@ -39,7 +39,7 @@ public: static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/LineString")).release(); static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[D)Lcom/mapbox/services/commons/geojson/LineString;"); - //Create + // Create jni::LocalObject<jni::jarray<jni::jobject>> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry)); return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, coordinates.get())); } @@ -51,7 +51,7 @@ public: static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiPoint")).release(); static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[D)Lcom/mapbox/services/commons/geojson/MultiPoint;"); - //Create + // Create jni::LocalObject<jni::jarray<jni::jobject>> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry)); return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, coordinates.get())); } @@ -63,7 +63,7 @@ public: static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Polygon")).release(); static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/geojson/Polygon;"); - //Create + // Create jni::LocalObject<jni::jarray<jni::jobject>> shape = jni::NewLocalObject(env, toShape<>(env, geometry)); return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, shape.get())); } @@ -75,7 +75,7 @@ public: static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiLineString")).release(); static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/geojson/MultiLineString;"); - //Create + // Create jni::LocalObject<jni::jarray<jni::jobject>> shape = jni::NewLocalObject(env, toShape<>(env, geometry)); return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, shape.get())); } @@ -92,7 +92,7 @@ public: jni::SetObjectArrayElement(env, *jarray, i, shape.get()); } - //Create the MultiPolygon + // Create the MultiPolygon static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiPolygon")).release(); static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[[D)Lcom/mapbox/services/commons/geojson/MultiPolygon;"); return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometries, jarray.get())); @@ -111,7 +111,7 @@ public: jni::SetObjectArrayElement(env, *jarray, i, converted.get()); } - //Turn into array list and create the GeometryCollection + // Turn into array list and create the GeometryCollection static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/GeometryCollection")).release(); static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromGeometries", "(Ljava/util/List;)Lcom/mapbox/services/commons/geojson/GeometryCollection;"); diff --git a/platform/android/src/geometry/feature.cpp b/platform/android/src/geometry/feature.cpp new file mode 100644 index 0000000000..5355d50ab7 --- /dev/null +++ b/platform/android/src/geometry/feature.cpp @@ -0,0 +1,20 @@ +#include "feature.hpp" + +namespace mbgl { +namespace android { + +jni::Object<Feature> Feature::fromGeometry(jni::JNIEnv& env, jni::Object<Geometry> geometry, jni::Object<JsonObject> properties, jni::String id) { + static auto method = Feature::javaClass.GetStaticMethod<jni::Object<Feature> (jni::Object<Geometry>, jni::Object<JsonObject>, jni::String)>(env, "fromGeometry"); + return Feature::javaClass.Call(env, method, geometry, properties, id); +} + +void Feature::registerNative(jni::JNIEnv& env) { + // Lookup the class + Feature::javaClass = *jni::Class<Feature>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<Feature> Feature::javaClass; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/feature.hpp b/platform/android/src/geometry/feature.hpp new file mode 100644 index 0000000000..7f2733430c --- /dev/null +++ b/platform/android/src/geometry/feature.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/geometry.hpp> + +#include <jni/jni.hpp> + +#include "geometry.hpp" +#include "../gson/json_object.hpp" + +namespace mbgl { +namespace android { + +class Feature : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Feature"; }; + + static jni::Object<Feature> fromGeometry(jni::JNIEnv&, jni::Object<Geometry>, jni::Object<JsonObject>, jni::String); + + static jni::Class<Feature> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/geometry.hpp b/platform/android/src/geometry/geometry.hpp new file mode 100644 index 0000000000..5c8ae39181 --- /dev/null +++ b/platform/android/src/geometry/geometry.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> + +namespace mbgl { +namespace android { + +class Geometry : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Geometry"; }; + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/lat_lng.cpp b/platform/android/src/geometry/lat_lng.cpp new file mode 100644 index 0000000000..9cf3630107 --- /dev/null +++ b/platform/android/src/geometry/lat_lng.cpp @@ -0,0 +1,31 @@ +#include "lat_lng.hpp" + +namespace mbgl { +namespace android { + +jni::Object<LatLng> LatLng::New(jni::JNIEnv& env, double latitude, double longitude) { + static auto constructor = LatLng::javaClass.GetConstructor<double, double>(env); + return LatLng::javaClass.New(env, constructor, latitude, longitude); +} + +mbgl::Point<double> LatLng::getGeometry(jni::JNIEnv& env, jni::Object<LatLng> latLng) { + static auto latitudeField = LatLng::javaClass.GetField<jni::jdouble>(env, "latitude"); + static auto longitudeField = LatLng::javaClass.GetField<jni::jdouble>(env, "longitude"); + return mbgl::Point<double>(latLng.Get(env, longitudeField), latLng.Get(env, latitudeField)); +} + +mbgl::LatLng LatLng::getLatLng(jni::JNIEnv& env, jni::Object<LatLng> latLng) { + auto point = LatLng::getGeometry(env, latLng); + return mbgl::LatLng(point.y, point.x); +} + +void LatLng::registerNative(jni::JNIEnv& env) { + // Lookup the class + LatLng::javaClass = *jni::Class<LatLng>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<LatLng> LatLng::javaClass; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/lat_lng.hpp b/platform/android/src/geometry/lat_lng.hpp new file mode 100644 index 0000000000..1ac32ae32e --- /dev/null +++ b/platform/android/src/geometry/lat_lng.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/geometry.hpp> + +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class LatLng : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/LatLng"; }; + + static jni::Object<LatLng> New(jni::JNIEnv&, double, double); + + static mbgl::Point<double> getGeometry(jni::JNIEnv&, jni::Object<LatLng>); + + static mbgl::LatLng getLatLng(jni::JNIEnv&, jni::Object<LatLng>); + + static jni::Class<LatLng> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/lat_lng_bounds.cpp b/platform/android/src/geometry/lat_lng_bounds.cpp new file mode 100644 index 0000000000..9efacde120 --- /dev/null +++ b/platform/android/src/geometry/lat_lng_bounds.cpp @@ -0,0 +1,31 @@ +#include "lat_lng_bounds.hpp" + +namespace mbgl { +namespace android { + +jni::Object<LatLngBounds> LatLngBounds::New(jni::JNIEnv& env, mbgl::LatLngBounds bounds) { + static auto constructor = LatLngBounds::javaClass.GetConstructor<double, double, double, double>(env); + return LatLngBounds::javaClass.New(env, constructor, bounds.north(), bounds.east(), bounds.south(), bounds.west()); +} + +mbgl::LatLngBounds LatLngBounds::getLatLngBounds(jni::JNIEnv& env, jni::Object<LatLngBounds> bounds) { + static auto swLat = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLatSouth"); + static auto swLon = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLonWest"); + static auto neLat = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLatNorth"); + static auto neLon = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "mLonEast"); + return mbgl::LatLngBounds::hull( + { bounds.Get(env, swLat), bounds.Get(env, swLon) }, + { bounds.Get(env, neLat), bounds.Get(env, neLon) } + ); +} + +void LatLngBounds::registerNative(jni::JNIEnv& env) { + // Lookup the class + LatLngBounds::javaClass = *jni::Class<LatLngBounds>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<LatLngBounds> LatLngBounds::javaClass; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/lat_lng_bounds.hpp b/platform/android/src/geometry/lat_lng_bounds.hpp new file mode 100644 index 0000000000..1c853e4b67 --- /dev/null +++ b/platform/android/src/geometry/lat_lng_bounds.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/geometry.hpp> + +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class LatLngBounds : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/LatLngBounds"; }; + + static jni::Object<LatLngBounds> New(jni::JNIEnv&, mbgl::LatLngBounds); + + static mbgl::LatLngBounds getLatLngBounds(jni::JNIEnv&, jni::Object<LatLngBounds>); + + static jni::Class<LatLngBounds> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/projected_meters.cpp b/platform/android/src/geometry/projected_meters.cpp new file mode 100644 index 0000000000..f3d9d1b0ef --- /dev/null +++ b/platform/android/src/geometry/projected_meters.cpp @@ -0,0 +1,20 @@ +#include "projected_meters.hpp" + +namespace mbgl { +namespace android { + +jni::Object<ProjectedMeters> ProjectedMeters::New(jni::JNIEnv& env, double northing, double easting) { + static auto constructor = ProjectedMeters::javaClass.GetConstructor<double, double>(env); + return ProjectedMeters::javaClass.New(env, constructor, northing, easting); +} + +void ProjectedMeters::registerNative(jni::JNIEnv& env) { + // Lookup the class + ProjectedMeters::javaClass = *jni::Class<ProjectedMeters>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<ProjectedMeters> ProjectedMeters::javaClass; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/geometry/projected_meters.hpp b/platform/android/src/geometry/projected_meters.hpp new file mode 100644 index 0000000000..9b70967b5d --- /dev/null +++ b/platform/android/src/geometry/projected_meters.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/geometry.hpp> + +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class ProjectedMeters : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/ProjectedMeters"; }; + + static jni::Object<ProjectedMeters> New(jni::JNIEnv&, double, double); + + static jni::Class<ProjectedMeters> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/graphics/pointf.cpp b/platform/android/src/graphics/pointf.cpp new file mode 100644 index 0000000000..6e91b81416 --- /dev/null +++ b/platform/android/src/graphics/pointf.cpp @@ -0,0 +1,20 @@ +#include "pointf.hpp" + +namespace mbgl { +namespace android { + +jni::Object<PointF> PointF::New(jni::JNIEnv& env, float x, float y) { + static auto constructor = PointF::javaClass.GetConstructor<float, float>(env); + return PointF::javaClass.New(env, constructor, x, y); +} + +void PointF::registerNative(jni::JNIEnv& env) { + // Lookup the class + PointF::javaClass = *jni::Class<PointF>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<PointF> PointF::javaClass; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/graphics/pointf.hpp b/platform/android/src/graphics/pointf.hpp new file mode 100644 index 0000000000..ea25ad2b40 --- /dev/null +++ b/platform/android/src/graphics/pointf.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> + +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class PointF : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "android/graphics/PointF"; }; + + static jni::Object<PointF> New(jni::JNIEnv&, float, float); + + static jni::Class<PointF> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/graphics/rectf.cpp b/platform/android/src/graphics/rectf.cpp new file mode 100644 index 0000000000..1b375dad18 --- /dev/null +++ b/platform/android/src/graphics/rectf.cpp @@ -0,0 +1,35 @@ +#include "rectf.hpp" + +namespace mbgl { +namespace android { + +float RectF::getLeft(jni::JNIEnv& env, jni::Object<RectF> rectf) { + static auto field = RectF::javaClass.GetField<float>(env, "left"); + return rectf.Get(env, field); +} + +float RectF::getTop(jni::JNIEnv& env, jni::Object<RectF> rectf) { + static auto field = RectF::javaClass.GetField<float>(env, "top"); + return rectf.Get(env, field); +} + +float RectF::getRight(jni::JNIEnv& env, jni::Object<RectF> rectf) { + static auto field = RectF::javaClass.GetField<float>(env, "right"); + return rectf.Get(env, field); +} + +float RectF::getBottom(jni::JNIEnv& env, jni::Object<RectF> rectf) { + static auto field = RectF::javaClass.GetField<float>(env, "bottom"); + return rectf.Get(env, field); +} + +void RectF::registerNative(jni::JNIEnv& env) { + // Lookup the class + RectF::javaClass = *jni::Class<RectF>::Find(env).NewGlobalRef(env).release(); +} + +jni::Class<RectF> RectF::javaClass; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/graphics/rectf.hpp b/platform/android/src/graphics/rectf.hpp new file mode 100644 index 0000000000..0f3a7756d5 --- /dev/null +++ b/platform/android/src/graphics/rectf.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> + +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class RectF : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "android/graphics/RectF"; }; + + static float getLeft(jni::JNIEnv&, jni::Object<RectF>); + + static float getTop(jni::JNIEnv&, jni::Object<RectF>); + + static float getRight(jni::JNIEnv&, jni::Object<RectF>); + + static float getBottom(jni::JNIEnv&, jni::Object<RectF>); + + static jni::Class<RectF> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/gson/json_object.hpp b/platform/android/src/gson/json_object.hpp new file mode 100644 index 0000000000..a7de0b1978 --- /dev/null +++ b/platform/android/src/gson/json_object.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> + +namespace mbgl { +namespace android { + +class JsonObject : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/google/gson/JsonObject"; }; + +}; + + +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/image.cpp b/platform/android/src/image.cpp new file mode 100644 index 0000000000..2a33944b18 --- /dev/null +++ b/platform/android/src/image.cpp @@ -0,0 +1,22 @@ +#include <mbgl/util/image.hpp> +#include <mbgl/util/string.hpp> + +#include <string> + +#include "attach_env.hpp" +#include "bitmap_factory.hpp" + +namespace mbgl { + +PremultipliedImage decodeImage(const std::string& string) { + auto env{ android::AttachEnv() }; + + auto array = jni::Array<jni::jbyte>::New(*env, string.size()); + jni::SetArrayRegion(*env, *array, 0, string.size(), + reinterpret_cast<const signed char*>(string.data())); + + auto bitmap = android::BitmapFactory::DecodeByteArray(*env, array, 0, string.size()); + return android::Bitmap::GetImage(*env, bitmap); +} + +} // namespace mbgl diff --git a/platform/android/src/java/lang.hpp b/platform/android/src/java/lang.hpp new file mode 100644 index 0000000000..dcf81a9d0c --- /dev/null +++ b/platform/android/src/java/lang.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace mbgl { +namespace android { +namespace java { +namespace lang { + +class Float { +public: + static constexpr auto Name() { return "java/lang/Float"; }; +}; + +class Number { +public: + static constexpr auto Name() { return "java/lang/Number"; }; +}; + +} // namespace lang +} // namespace java +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/java/util.cpp b/platform/android/src/java/util.cpp new file mode 100644 index 0000000000..c630e403d9 --- /dev/null +++ b/platform/android/src/java/util.cpp @@ -0,0 +1,18 @@ +#include "util.hpp" + +namespace mbgl { +namespace android { +namespace java { +namespace util { + +jni::Class<List> List::javaClass; + +void registerNative(jni::JNIEnv& env) { + List::javaClass = *jni::Class<List>::Find(env).NewGlobalRef(env).release(); +} + + +} // namespace util +} // namespace java +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/java/util.hpp b/platform/android/src/java/util.hpp new file mode 100644 index 0000000000..1a552c7124 --- /dev/null +++ b/platform/android/src/java/util.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> + +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { +namespace java { +namespace util { + +class List : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "java/util/List"; }; + + template<class T> + static jni::Array<jni::Object<T>> toArray(jni::JNIEnv& env, jni::Object<List> list) { + static auto toArray = List::javaClass.GetMethod<jni::Array<jni::Object<>> ()>(env, "toArray"); + return (jni::Array<jni::Object<T>>) list.Call(env, toArray); + }; + + static jni::Class<List> javaClass; + +}; + +void registerNative(jni::JNIEnv&); + + +} // namespace util +} // namespace java +} // namespace android +} // namespace mbgl
\ No newline at end of file diff --git a/platform/android/src/java_types.hpp b/platform/android/src/java_types.hpp index 2e2c5fc2d6..b416a75b91 100644 --- a/platform/android/src/java_types.hpp +++ b/platform/android/src/java_types.hpp @@ -32,4 +32,4 @@ namespace java { void registerNatives(JNIEnv&); } } -}
\ No newline at end of file +} diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 1ef7fec8fb..bd12cff3fa 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -1,44 +1,37 @@ -#include <cstdint> -#include <cinttypes> -#include <cassert> -#include <string> -#include <array> -#include <vector> +#include "jni.hpp" -#include <android/native_window_jni.h> -#include <sys/system_properties.h> +#include <mbgl/util/logging.hpp> -#include "jni.hpp" +#include "annotation/marker.hpp" +#include "annotation/polygon.hpp" +#include "annotation/polyline.hpp" +#include "bitmap.hpp" +#include "bitmap_factory.hpp" +#include "connectivity_listener.hpp" +#include "conversion/conversion.hpp" +#include "conversion/collection.hpp" +#include "file_source.hpp" +#include "geometry/feature.hpp" +#include "geometry/lat_lng.hpp" +#include "geometry/lat_lng_bounds.hpp" +#include "geometry/projected_meters.hpp" +#include "graphics/pointf.hpp" +#include "graphics/rectf.hpp" #include "java_types.hpp" #include "native_map_view.hpp" -#include "connectivity_listener.hpp" +#include "offline/offline_manager.hpp" +#include "offline/offline_region.hpp" +#include "offline/offline_region_definition.hpp" +#include "offline/offline_region_error.hpp" +#include "offline/offline_region_status.hpp" +#include "style/functions/categorical_stops.hpp" +#include "style/functions/exponential_stops.hpp" +#include "style/functions/identity_stops.hpp" +#include "style/functions/interval_stops.hpp" +#include "style/functions/stop.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 <mbgl/map/map.hpp> -#include <mbgl/map/camera.hpp> -#include <mbgl/annotation/annotation.hpp> -#include <mbgl/style/layer.hpp> -#include <mbgl/style/source.hpp> -#include <mbgl/sprite/sprite_image.hpp> -#include <mbgl/util/event.hpp> -#include <mbgl/util/logging.hpp> -#include <mbgl/storage/network_status.hpp> -#include <mbgl/util/exception.hpp> -#include <mbgl/util/optional.hpp> -#include <mbgl/util/string.hpp> -#include <mbgl/util/run_loop.hpp> - -#include <mapbox/geometry.hpp> - -#include <jni/jni.hpp> - -#pragma clang diagnostic ignored "-Wunused-parameter" - namespace mbgl { namespace android { @@ -46,127 +39,7 @@ 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 - +//TODO: remove bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName) { assert(vm != nullptr); assert(env != nullptr); @@ -194,6 +67,7 @@ bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName) { return detach; } +//TODO: remove void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach) { if (detach) { assert(vm != nullptr); @@ -208,1843 +82,62 @@ void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach) { *env = nullptr; } -std::string std_string_from_jstring(JNIEnv *env, jni::jstring* jstr) { - return jni::Make<std::string>(*env, jni::String(jstr)); -} - -jni::jstring* std_string_to_jstring(JNIEnv *env, std::string str) { - return jni::Make<jni::String>(*env, str).Get(); -} - -std::vector<std::string> std_vector_string_from_jobject(JNIEnv *env, jni::jobject* jlist) { - std::vector<std::string> vector; - - jni::NullCheck(*env, jlist); - jni::jarray<jni::jobject>* jarray = - reinterpret_cast<jni::jarray<jni::jobject>*>(jni::CallMethod<jni::jobject*>(*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::jstring*>(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<std::string> vector) { - jni::jobject* jlist = &jni::NewObject(*env, *arrayListClass, *arrayListConstructorId); - - for (const auto& str : vector) { - jni::CallMethod<jboolean>(*env, jlist, *arrayListAddId, std_string_to_jstring(env, str)); - } - - return jlist; -} - -jni::jarray<jlong>* std_vector_uint_to_jobject(JNIEnv *env, const std::vector<uint32_t>& vector) { - jni::jarray<jlong>& jarray = jni::NewArray<jlong>(*env, vector.size()); - - std::vector<jlong> 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<uint8_t> metadata_from_java(JNIEnv* env, jni::jarray<jbyte>& j) { - std::size_t length = jni::GetArrayLength(*env, j); - std::vector<uint8_t> c; - c.resize(length); - jni::GetArrayRegion(*env, j, 0, length, reinterpret_cast<jbyte*>(c.data())); - return c; -} - -static jni::jarray<jbyte>* metadata_from_native(JNIEnv* env, const std::vector<uint8_t>& c) { - std::size_t length = static_cast<std::size_t>(c.size()); - jni::jarray<jbyte>& j = jni::NewArray<jbyte>(*env, length); - jni::SetArrayRegion(*env, j, 0, c.size(), reinterpret_cast<const jbyte*>(c.data())); - return &j; -} - -static mbgl::LatLngBounds latlngbounds_from_java(JNIEnv *env, jni::jobject* latLngBounds) { - jdouble swLat = jni::GetField<jdouble>(*env, latLngBounds, *latLngBoundsLatSouthId); - jdouble swLon = jni::GetField<jdouble>(*env, latLngBounds, *latLngBoundsLonWestId); - jdouble neLat = jni::GetField<jdouble>(*env, latLngBounds, *latLngBoundsLatNorthId); - jdouble neLon = jni::GetField<jdouble>(*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<jlong>(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<NativeMapView *>(nativeMapViewPtr); -} - -void nativeInitializeDisplay(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeDisplay"); - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(nativeMapViewPtr); - nativeMapView->destroySurface(); -} - -void nativeUpdate(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->invalidate(); -} - -void nativeRender(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(nativeMapViewPtr); - nativeMapView->resizeFramebuffer(fbWidth, fbHeight); -} - -void nativeRemoveClass(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* clazz) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().setStyleURL(std_string_from_jstring(env, url)); -} - -jni::jstring* nativeGetStyleUrl(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr){ - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - return std_string_to_jstring(env, nativeMapView->getMap().getStyleURL()); -} - -void nativeSetStyleJson(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* newStyleJson) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().cancelTransitions(); -} - -void nativeSetGestureInProgress(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jboolean inProgress) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().moveBy({dx, dy}, 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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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(); - 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<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().resetPosition(); -} - -jdouble nativeGetPitch(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().setZoom(zoom, mbgl::Milliseconds(duration)); -} - -jdouble nativeGetZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - return nativeMapView->getMap().getZoom(); -} - -void nativeResetZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().resetZoom(); -} - -void nativeSetMinZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble zoom) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().setMinZoom(zoom); -} - -jdouble nativeGetMinZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - return nativeMapView->getMap().getMinZoom(); -} - -void nativeSetMaxZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jdouble zoom) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().setMaxZoom(zoom); -} - -jdouble nativeGetMaxZoom(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(nativeMapViewPtr); - return nativeMapView->getMap().getBearing(); -} - -void nativeResetNorth(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(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<double>(lon, lat), iconId }); -} - -jni::jarray<jlong>* nativeAddMarkers(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray<jni::jobject>* jarray) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<jni::jobject*>(*env, marker, *markerPositionId); - jni::jobject* icon = jni::GetField<jni::jobject*>(*env, marker, *markerIconId); - jni::jstring* jid = reinterpret_cast<jni::jstring*>(jni::GetField<jni::jobject*>(*env, icon, *iconIdId)); - - jdouble latitude = jni::GetField<jdouble>(*env, position, *latLngLatitudeId); - jdouble longitude = jni::GetField<jdouble>(*env, position, *latLngLongitudeId); - - ids.push_back(nativeMapView->getMap().addAnnotation(mbgl::SymbolAnnotation { - mbgl::Point<double>(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 <class Geometry> -Geometry toGeometry(JNIEnv *env, jni::jobject* jlist) { - NullCheck(*env, jlist); - jni::jarray<jni::jobject>* jarray = - reinterpret_cast<jni::jarray<jni::jobject>*>(jni::CallMethod<jni::jobject*>(*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::jobject*>(jni::GetObjectArrayElement(*env, *jarray, i)); - NullCheck(*env, latLng); - - geometry.push_back(mbgl::Point<double>( - jni::GetField<jdouble>(*env, latLng, *latLngLongitudeId), - jni::GetField<jdouble>(*env, latLng, *latLngLatitudeId))); - - jni::DeleteLocalRef(*env, latLng); - } - - jni::DeleteLocalRef(*env, jarray); - jni::DeleteLocalRef(*env, jlist); - - return geometry; -} - -jni::jarray<jlong>* nativeAddPolylines(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray<jni::jobject>* jarray) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<jni::jobject*>(*env, polyline, *polylinePointsId); - - mbgl::LineAnnotation annotation { toGeometry<mbgl::LineString<double>>(env, points) }; - annotation.opacity = { jni::GetField<jfloat>(*env, polyline, *polylineAlphaId) }; - annotation.color = { toColor(jni::GetField<jint>(*env, polyline, *polylineColorId)) }; - annotation.width = { jni::GetField<jfloat>(*env, polyline, *polylineWidthId) }; - ids.push_back(nativeMapView->getMap().addAnnotation(annotation)); - - jni::DeleteLocalRef(*env, polyline); - } - - return std_vector_uint_to_jobject(env, ids); -} - -jni::jarray<jlong>* nativeAddPolygons(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray<jni::jobject>* jarray) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<jni::jobject*>(*env, polygon, *polygonPointsId); - - mbgl::FillAnnotation annotation { mbgl::Polygon<double> { toGeometry<mbgl::LinearRing<double>>(env, points) } }; - annotation.opacity = { jni::GetField<jfloat>(*env, polygon, *polygonAlphaId) }; - annotation.outlineColor = { toColor(jni::GetField<jint>(*env, polygon, *polygonStrokeColorId)) }; - annotation.color = { toColor(jni::GetField<jint>(*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<NativeMapView *>(nativeMapViewPtr); - jni::jobject* points = jni::GetField<jni::jobject*>(*env, polygon, *polygonPointsId); - - mbgl::FillAnnotation annotation { mbgl::Polygon<double> { toGeometry<mbgl::LinearRing<double>>(env, points) } }; - annotation.opacity = { jni::GetField<jfloat>(*env, polygon, *polygonAlphaId) }; - annotation.outlineColor = { toColor(jni::GetField<jint>(*env, polygon, *polygonStrokeColorId)) }; - annotation.color = { toColor(jni::GetField<jint>(*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<NativeMapView *>(nativeMapViewPtr); - jni::jobject* points = jni::GetField<jni::jobject*>(*env, polyline, *polylinePointsId); - - mbgl::LineAnnotation annotation { toGeometry<mbgl::LineString<double>>(env, points) }; - annotation.opacity = { jni::GetField<jfloat>(*env, polyline, *polylineAlphaId) }; - annotation.color = { toColor(jni::GetField<jint>(*env, polyline, *polylineColorId)) }; - annotation.width = { jni::GetField<jfloat>(*env, polyline, *polylineWidthId) }; - nativeMapView->getMap().updateAnnotation(polylineId, annotation); -} - -void nativeRemoveAnnotations(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jarray<jlong>* jarray) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<jlong>* nativeQueryPointAnnotations(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jobject* rect) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - - // Conversion - jfloat left = jni::GetField<jfloat>(*env, rect, *rectFLeftId); - jfloat right = jni::GetField<jfloat>(*env, rect, *rectFRightId); - jfloat top = jni::GetField<jfloat>(*env, rect, *rectFTopId); - jfloat bottom = jni::GetField<jfloat>(*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<jbyte>* jpixels) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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( - { static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); - - if (premultipliedImage.bytes() != uint32_t(size)) { - throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch"); - } - - jni::GetArrayRegion(*env, *jpixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get())); - - auto iconImage = std::make_shared<mbgl::SpriteImage>( - std::move(premultipliedImage), - float(scale)); - - nativeMapView->getMap().addAnnotationIcon(symbolName, iconImage); -} - -void nativeSetVisibleCoordinateBounds(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, - jni::jarray<jni::jobject>* coordinates, jni::jobject* padding, jdouble direction, jlong duration) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - - jfloat left = jni::GetField<jfloat>(*env, padding, *rectFLeftId); - jfloat right = jni::GetField<jfloat>(*env, padding, *rectFRightId); - jfloat top = jni::GetField<jfloat>(*env, padding, *rectFTopId); - jfloat bottom = jni::GetField<jfloat>(*env, padding, *rectFBottomId); - - NullCheck(*env, coordinates); - std::size_t count = jni::GetArrayLength(*env, *coordinates); - - mbgl::EdgeInsets mbglInsets = {top, left, bottom, right}; - std::vector<mbgl::LatLng> 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<jdouble>(*env, latLng, *latLngLatitudeId); - jdouble longitude = jni::GetField<jdouble>(*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<jni::jobject>* nativeQueryRenderedFeaturesForPoint(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jfloat x, jni::jfloat y, jni::jarray<jni::jobject>* layerIds) { - using namespace mbgl::android::conversion; - using namespace mapbox::geometry; - - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - - mbgl::optional<std::vector<std::string>> layers; - if (layerIds != nullptr && jni::GetArrayLength(*env, *layerIds) > 0) { - layers = toVector(*env, *layerIds); - } - point<double> point = {x, y}; - - return *convert<jni::jarray<jni::jobject>*, std::vector<mbgl::Feature>>(*env, nativeMapView->getMap().queryRenderedFeatures(point, layers)); -} - -jni::jarray<jni::jobject>* nativeQueryRenderedFeaturesForBox(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jfloat left, jni::jfloat top, jni::jfloat right, jni::jfloat bottom, jni::jarray<jni::jobject>* layerIds) { - using namespace mbgl::android::conversion; - using namespace mapbox::geometry; - - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - - mbgl::optional<std::vector<std::string>> layers; - if (layerIds != nullptr && jni::GetArrayLength(*env, *layerIds) > 0) { - layers = toVector(*env, *layerIds); - } - box<double> box = { point<double>{ left, top}, point<double>{ right, bottom } }; - - return *convert<jni::jarray<jni::jobject>*, std::vector<mbgl::Feature>>(*env, nativeMapView->getMap().queryRenderedFeatures(box, layers)); -} - -void nativeOnLowMemory(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - nativeMapView->getMap().onLowMemory(); -} - -void nativeSetDebug(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jboolean debug) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(nativeMapViewPtr); - return nativeMapView->getMap().getDebug() != DebugOptions::NoDebug; -} - -jboolean nativeIsFullyLoaded(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(nativeMapViewPtr); - mbgl::ScreenCoordinate pixel = nativeMapView->getMap().pixelForLatLng(mbgl::LatLng(latitude, longitude)); - return &jni::NewObject(*env, *pointFClass, *pointFConstructorId, static_cast<jfloat>(pixel.x), static_cast<jfloat>(pixel.y)); -} - -jni::jobject* nativeLatLngForPixel(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jfloat x, jfloat y) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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<NativeMapView *>(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); -} - -jlong nativeGetTransitionDuration(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(env); - assert(nativeMapViewPtr != 0); - - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - - const auto transitionOptions = nativeMapView->getMap().getTransitionOptions(); - return transitionOptions.duration.value_or(mbgl::Duration::zero()).count(); -} - -void nativeSetTransitionDuration(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong duration) { - assert(env); - assert(nativeMapViewPtr != 0); - - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - - auto transitionOptions = nativeMapView->getMap().getTransitionOptions(); - transitionOptions.duration = std::chrono::duration_cast<mbgl::Duration>(std::chrono::duration<jlong>(duration)); - nativeMapView->getMap().setTransitionOptions(transitionOptions); -} - -jlong nativeGetTransitionDelay(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) { - assert(env); - assert(nativeMapViewPtr != 0); - - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - - const auto transitionOptions = nativeMapView->getMap().getTransitionOptions(); - return transitionOptions.delay.value_or(mbgl::Duration::zero()).count(); -} - -void nativeSetTransitionDelay(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong delay) { - assert(env); - assert(nativeMapViewPtr != 0); - - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - - auto transitionOptions = nativeMapView->getMap().getTransitionOptions(); - transitionOptions.delay = std::chrono::duration_cast<mbgl::Duration>(std::chrono::duration<jlong>(delay)); - nativeMapView->getMap().setTransitionOptions(transitionOptions); -} - -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<NativeMapView *>(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<Layer>(); - } - - //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<NativeMapView *>(nativeMapViewPtr); - - Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr); - try { - layer->addToMap(nativeMapView->getMap(), before ? mbgl::optional<std::string>(std_string_from_jstring(env, before)) : mbgl::optional<std::string>()); - } catch (const std::runtime_error& error) { - jni::ThrowNew(*env, jni::FindClass(*env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); - } -} - -/** - * Remove by layer id. Ownership is not transferred back - */ -void nativeRemoveLayerById(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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()); - } -} - -/** - * Remove with wrapper object id. Ownership is transferred back to the wrapper - */ -void nativeRemoveLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong layerPtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - mbgl::android::Layer *layer = reinterpret_cast<mbgl::android::Layer *>(layerPtr); - try { - std::unique_ptr<mbgl::style::Layer> coreLayer = nativeMapView->getMap().removeLayer(layer->get().getID()); - layer->setLayer(std::move(coreLayer)); - } 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<NativeMapView *>(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<Source>(); - } - - //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<NativeMapView *>(nativeMapViewPtr); - - Source *source = reinterpret_cast<Source *>(nativeSourcePtr); - try { - source->addToMap(nativeMapView->getMap()); - } catch (const std::runtime_error& error) { - jni::ThrowNew(*env, jni::FindClass(*env, "com/mapbox/mapboxsdk/style/sources/CannotAddSourceException"), error.what()); - } -} - -void nativeRemoveSourceById(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(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 nativeRemoveSource(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong sourcePtr) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - mbgl::android::Source *source = reinterpret_cast<mbgl::android::Source *>(sourcePtr); - try { - std::unique_ptr<mbgl::style::Source> coreSource = nativeMapView->getMap().removeSource(source->get().getID()); - source->setSource(std::move(coreSource)); - } 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<jbyte>* data) { - assert(nativeMapViewPtr != 0); - NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr); - - // Create Pre-multiplied image from byte[] - NullCheck(*env, data); - std::size_t size = jni::GetArrayLength(*env, *data); - mbgl::PremultipliedImage premultipliedImage( - { static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); - - if (premultipliedImage.bytes() != uint32_t(size)) { - throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch"); - } - - jni::GetArrayRegion(*env, *data, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get())); - - //Wrap in a SpriteImage with the correct pixel ratio - auto spriteImage = std::make_unique<mbgl::SpriteImage>(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<NativeMapView *>(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<NativeMapView *>(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<jlong>(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<mbgl::DefaultFileSource *>(defaultFileSourcePtr); - defaultFileSource->setAccessToken(accessToken); -} - -jni::jstring* getAccessToken(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourcePtr) { - assert(defaultFileSourcePtr != 0); - mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(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<mbgl::DefaultFileSource *>(defaultFileSourcePtr); - defaultFileSource->listOfflineRegions([obj, defaultFileSourcePtr, listCallback](std::exception_ptr error, mbgl::optional<std::vector<mbgl::OfflineRegion>> 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<void>(*env2, listCallback, *listOnErrorMethodId, std_string_to_jstring(env2, message)); - } else if (regions) { - // Build jni::jarray<jni::jobject>* - std::size_t index = 0; - jni::jarray<jni::jobject>* 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<jni::jobject*>(*env2, jregion, *offlineRegionOfflineManagerId, obj); - jni::SetField<jlong>(*env2, jregion, *offlineRegionIdId, region.getID()); - - // Definition object - mbgl::OfflineTilePyramidRegionDefinition definition = region.getDefinition(); - jni::jobject* jdefinition = &jni::NewObject(*env2, *offlineRegionDefinitionClass, *offlineRegionDefinitionConstructorId); - jni::SetField<jni::jobject*>(*env2, jdefinition, *offlineRegionDefinitionStyleURLId, std_string_to_jstring(env2, definition.styleURL)); - jni::SetField<jni::jobject*>(*env2, jdefinition, *offlineRegionDefinitionBoundsId, latlngbounds_from_native(env2, definition.bounds)); - jni::SetField<jdouble>(*env2, jdefinition, *offlineRegionDefinitionMinZoomId, definition.minZoom); - jni::SetField<jdouble>(*env2, jdefinition, *offlineRegionDefinitionMaxZoomId, definition.maxZoom); - jni::SetField<jfloat>(*env2, jdefinition, *offlineRegionDefinitionPixelRatioId, definition.pixelRatio); - jni::SetField<jni::jobject*>(*env2, jregion, *offlineRegionDefinitionId, jdefinition); - - // Metadata object - jni::jarray<jbyte>* metadata = metadata_from_native(env2, region.getMetadata()); - jni::SetField<jni::jobject*>(*env2, jregion, *offlineRegionMetadataId, metadata); - - // Moves the region on the stack into a heap-allocated one - jni::SetField<jlong>(*env2, jregion, *offlineRegionPtrId, - reinterpret_cast<jlong>(new mbgl::OfflineRegion(std::move(region)))); - - jni::SetObjectArrayElement(*env2, *jregions, index, jregion); - index++; - } - - // Trigger callback - jni::CallMethod<void>(*env2, listCallback, *listOnListMethodId, jregions); - } - - // Delete global refs and detach when we're done - jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef<jni::jobject>(obj)); - jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef<jni::jobject>(listCallback)); - detach_jni_thread(theJVM, &env2, renderDetach); - }); -} - -void createOfflineRegion(JNIEnv *env, jni::jobject* obj, jlong defaultFileSourcePtr, jni::jobject* definition_, jni::jarray<jbyte>* metadata_, jni::jobject* createCallback) { - // Checks - assert(defaultFileSourcePtr != 0); - NullCheck(*env, createCallback); - - // Definition fields - jni::jstring* jStyleURL = reinterpret_cast<jni::jstring*>(jni::GetField<jni::jobject*>(*env, definition_, *offlineRegionDefinitionStyleURLId)); - std::string styleURL = std_string_from_jstring(env, jStyleURL); - jni::jobject* jBounds = jni::GetField<jni::jobject*>(*env, definition_, *offlineRegionDefinitionBoundsId); - jdouble jMinZoom = jni::GetField<jdouble>(*env, definition_, *offlineRegionDefinitionMinZoomId); - jdouble jMaxZoom = jni::GetField<jdouble>(*env, definition_, *offlineRegionDefinitionMaxZoomId); - jfloat jPixelRatio = jni::GetField<jfloat>(*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<mbgl::DefaultFileSource *>(defaultFileSourcePtr); - defaultFileSource->createOfflineRegion(definition, metadata, [obj, defaultFileSourcePtr, createCallback] (std::exception_ptr error, mbgl::optional<mbgl::OfflineRegion> 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<void>(*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<jni::jobject*>(*env2, jregion, *offlineRegionOfflineManagerId, obj); - jni::SetField<jlong>(*env2, jregion, *offlineRegionIdId, region->getID()); - - // Metadata object - jni::jarray<jbyte>* jmetadata = metadata_from_native(env2, region->getMetadata()); - jni::SetField<jni::jobject*>(*env2, jregion, *offlineRegionMetadataId, jmetadata); - - // Moves the region on the stack into a heap-allocated one - jni::SetField<jlong>(*env2, jregion, *offlineRegionPtrId, - reinterpret_cast<jlong>(new mbgl::OfflineRegion(std::move(*region)))); - - // Invoke Java callback - jni::CallMethod<void>(*env2, createCallback, *createOnCreateMethodId, jregion); - } - - // Delete global refs and detach when we're done - jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef<jni::jobject>(obj)); - jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef<jni::jobject>(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<mbgl::DefaultFileSource *>(defaultFileSourcePtr); - defaultFileSource->setOfflineMapboxTileCountLimit(limit); -} - -mbgl::OfflineRegion* getOfflineRegionPeer(JNIEnv *env, jni::jobject* offlineRegion_) { - jlong offlineRegionPtr = jni::GetField<jlong>(*env, offlineRegion_, *offlineRegionPtrId); - if (!offlineRegionPtr) { - jni::ThrowNew(*env, jni::FindClass(*env, "java/lang/IllegalStateException"), - "Use of OfflineRegion after OfflineRegion.delete"); - } - return reinterpret_cast<mbgl::OfflineRegion *>(offlineRegionPtr); -} - -void destroyOfflineRegion(JNIEnv *env, jni::jobject* offlineRegion_) { - // Offline region - jlong offlineRegionPtr = jni::GetField<jlong>(*env, offlineRegion_, *offlineRegionPtrId); - if (!offlineRegionPtr) { - return; // Already deleted - } - - // File source - jni::jobject* jmanager = jni::GetField<jni::jobject*>(*env, offlineRegion_, *offlineRegionOfflineManagerId); - jlong defaultFileSourcePtr = jni::GetField<jlong>(*env, jmanager, *offlineManagerClassPtrId); - mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr); - - // Release the observer and delete the region - mbgl::OfflineRegion *offlineRegion = reinterpret_cast<mbgl::OfflineRegion *>(offlineRegionPtr); - defaultFileSource->setOfflineRegionObserver(*offlineRegion, nullptr); - jni::SetField<jlong>(*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<jni::jobject*>(*env, offlineRegion_, *offlineRegionOfflineManagerId); - jlong defaultFileSourcePtr = jni::GetField<jlong>(*env, jmanager, *offlineManagerClassPtrId); - mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr); - - // Define the observer - class Observer : public mbgl::OfflineRegionObserver { - public: - Observer(jni::UniqueGlobalRef<jni::jobject>&& 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<jint>(*env2, jstatus, *offlineRegionStatusDownloadStateId, downloadState); - jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusCompletedResourceCountId, status.completedResourceCount); - jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusCompletedResourceSizeId, status.completedResourceSize); - jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusCompletedTileCountId, status.completedTileCount); - jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusCompletedTileSizeId, status.completedTileSize); - jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusRequiredResourceCountId, status.requiredResourceCount); - jni::SetField<jboolean>(*env2, jstatus, *offlineRegionStatusRequiredResourceCountIsPreciseId, status.requiredResourceCountIsPrecise); - jni::CallMethod<void>(*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<jni::jobject*>(*env2, jerror, *offlineRegionErrorReasonId, std_string_to_jstring(env2, errorReason)); - jni::SetField<jni::jobject*>(*env2, jerror, *offlineRegionErrorMessageId, std_string_to_jstring(env2, error.message)); - jni::CallMethod<void>(*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<void>(*env2, observerCallback.get(), *offlineRegionObserveronLimitId, jlong(limit)); - - // Detach when we're done - detach_jni_thread(theJVM, &env2, renderDetach); - } - - jni::UniqueGlobalRef<jni::jobject> observerCallback; - }; - - // Set the observer - defaultFileSource->setOfflineRegionObserver(*offlineRegion, - std::make_unique<Observer>(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<jni::jobject*>(*env, offlineRegion_, *offlineRegionOfflineManagerId); - jlong defaultFileSourcePtr = jni::GetField<jlong>(*env, jmanager, *offlineManagerClassPtrId); - mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(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<jni::jobject*>(*env, offlineRegion_, *offlineRegionOfflineManagerId); - jlong defaultFileSourcePtr = jni::GetField<jlong>(*env, jmanager, *offlineManagerClassPtrId); - mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(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<mbgl::OfflineRegionStatus> 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<void>(*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<jint>(*env2, jstatus, *offlineRegionStatusDownloadStateId, downloadState); - jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusCompletedResourceCountId, status->completedResourceCount); - jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusCompletedResourceSizeId, status->completedResourceSize); - jni::SetField<jlong>(*env2, jstatus, *offlineRegionStatusRequiredResourceCountId, status->requiredResourceCount); - jni::SetField<jboolean>(*env2, jstatus, *offlineRegionStatusRequiredResourceCountIsPreciseId, status->requiredResourceCountIsPrecise); - jni::CallMethod<void>(*env2, statusCallback, *offlineRegionStatusOnStatusId, jstatus); - } - - // Delete global refs and detach when we're done - jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef<jni::jobject>(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<jni::jobject*>(*env, offlineRegion_, *offlineRegionOfflineManagerId); - jlong defaultFileSourcePtr = jni::GetField<jlong>(*env, jmanager, *offlineManagerClassPtrId); - mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(defaultFileSourcePtr); - - // Makes sure the callback doesn't get GC'ed - deleteCallback = jni::NewGlobalRef(*env, deleteCallback).release(); - - // Set new state - jni::SetField<jlong>(*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<void>(*env2, deleteCallback, *offlineRegionDeleteOnErrorId, std_string_to_jstring(env2, message)); - } else { - jni::CallMethod<void>(*env2, deleteCallback, *offlineRegionDeleteOnDeleteId); - } - - // Delete global refs and detach when we're done - jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef<jni::jobject>(deleteCallback)); - detach_jni_thread(theJVM, &env2, renderDetach); - }); -} - -void updateOfflineRegionMetadata(JNIEnv *env, jni::jobject* offlineRegion_, jni::jarray<jbyte>* metadata_, jni::jobject* updateCallback) { - // Offline region - mbgl::OfflineRegion* offlineRegion = getOfflineRegionPeer(env, offlineRegion_); - - // File source - jni::jobject* jmanager = jni::GetField<jni::jobject*>(*env, offlineRegion_, *offlineRegionOfflineManagerId); - jlong defaultFileSourcePtr = jni::GetField<jlong>(*env, jmanager, *offlineManagerClassPtrId); - mbgl::DefaultFileSource *defaultFileSource = reinterpret_cast<mbgl::DefaultFileSource *>(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<mbgl::OfflineRegionMetadata> 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<void>(*env2, updateCallback, *updateMetadataOnErrorMethodId, std_string_to_jstring(env2, message)); - } else if (data) { - jni::jarray<jbyte>* jmetadata = metadata_from_native(env2, *data); - jni::CallMethod<void>(*env2, updateCallback, *updateMetadataOnUpdateMethodId, jmetadata); - } - - // Delete global refs and detach when we're done - jni::DeleteGlobalRef(*env2, jni::UniqueGlobalRef<jni::jobject>(updateCallback)); - detach_jni_thread(theJVM, &env2, renderDetach); - }); -} - -// Offline calls end - -} // anonymous - -namespace mbgl { -namespace android { - void registerNatives(JavaVM *vm) { theJVM = vm; jni::JNIEnv& env = jni::GetEnv(*vm, jni::jni_version_1_6); + // For the DefaultFileSource static mbgl::util::RunLoop mainRunLoop; + FileSource::registerNative(env); - mbgl::android::RegisterNativeHTTPRequest(env); - + // Basic types 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, "<init>", "(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, "<init>", "(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, "<init>", "()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, "<init>", "(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, "<init>", "(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, "<init>", "()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<decltype(name), name>( #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(nativeGetStyleUrl, "(J)Ljava/lang/String;"), - 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(nativeGetTransitionDuration, "(J)J"), - MAKE_NATIVE_METHOD(nativeSetTransitionDuration, "(JJ)V"), - MAKE_NATIVE_METHOD(nativeGetTransitionDelay, "(J)J"), - MAKE_NATIVE_METHOD(nativeSetTransitionDelay, "(JJ)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(nativeRemoveLayerById, "(JLjava/lang/String;)V"), - MAKE_NATIVE_METHOD(nativeRemoveLayer, "(JJ)V"), - MAKE_NATIVE_METHOD(nativeGetSource, "(JLjava/lang/String;)Lcom/mapbox/mapboxsdk/style/sources/Source;"), - MAKE_NATIVE_METHOD(nativeAddSource, "(JJ)V"), - MAKE_NATIVE_METHOD(nativeRemoveSourceById, "(JLjava/lang/String;)V"), - MAKE_NATIVE_METHOD(nativeRemoveSource, "(JJ)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") - ); + java::util::registerNative(env); + PointF::registerNative(env); + RectF::registerNative(env); - // Offline begin + // Geometry + Feature::registerNative(env); + LatLng::registerNative(env); + LatLngBounds::registerNative(env); + ProjectedMeters::registerNative(env); - struct OfflineManager { - static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager"; } + //Annotation + Marker::registerNative(env); + Polygon::registerNative(env); + Polyline::registerNative(env); - struct ListOfflineRegionsCallback { - static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$ListOfflineRegionsCallback"; } - }; + // Map + NativeMapView::registerNative(env); - struct CreateOfflineRegionsCallback { - static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$CreateOfflineRegionCallback"; } - }; - }; + // Http + RegisterNativeHTTPRequest(env); - struct OfflineRegion { - static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion"; } - }; + // Bitmap + Bitmap::registerNative(env); + BitmapFactory::registerNative(env); - jni::Class<OfflineManager> offlineManagerClass = jni::Class<OfflineManager>::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<OfflineManager::ListOfflineRegionsCallback> listOfflineRegionsCallbackClass = jni::Class<OfflineManager::ListOfflineRegionsCallback>::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<OfflineManager::CreateOfflineRegionsCallback> createOfflineRegionCallbackClass = jni::Class<OfflineManager::CreateOfflineRegionsCallback>::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, "<init>", "()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, "<init>", "()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, "<init>", "()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, "<init>", "()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"); + // Style + registerNativeLayers(env); + registerNativeSources(env); + Stop::registerNative(env); + CategoricalStops::registerNative(env); + ExponentialStops::registerNative(env); + IdentityStops::registerNative(env); + IntervalStops::registerNative(env); - // Offline end + // Connectivity + ConnectivityListener::registerNative(env); - char release[PROP_VALUE_MAX] = ""; - __system_property_get("ro.build.version.release", release); - androidRelease = std::string(release); + // Offline + OfflineManager::registerNative(env); + OfflineRegion::registerNative(env); + OfflineRegionDefinition::registerNative(env); + OfflineTilePyramidRegionDefinition::registerNative(env); + OfflineRegionError::registerNative(env); + OfflineRegionStatus::registerNative(env); } - -} // android -} // mbgl + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/jni.hpp b/platform/android/src/jni.hpp index 90ec914cf2..f0c113f754 100644 --- a/platform/android/src/jni.hpp +++ b/platform/android/src/jni.hpp @@ -14,17 +14,11 @@ extern JavaVM* theJVM; extern std::string cachePath; extern std::string dataPath; extern std::string apkPath; -extern std::string androidRelease; -extern jmethodID onInvalidateId; -extern jmethodID onMapChangedId; -extern jmethodID onFpsChangedId; -extern jmethodID onSnapshotReadyId; +bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName); +void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach); -extern bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName); -extern void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach); - extern void registerNatives(JavaVM* vm); -} //android -} //mbgl +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/jni/generic_global_ref_deleter.hpp b/platform/android/src/jni/generic_global_ref_deleter.hpp new file mode 100644 index 0000000000..4e53e0a0ce --- /dev/null +++ b/platform/android/src/jni/generic_global_ref_deleter.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include <jni/jni.hpp> + +#include "../attach_env.hpp" + +namespace mbgl { +namespace android { + +// A deleter that doesn't retain an JNIEnv handle but instead tries to attach the JVM. This means +// it can be used on any thread to delete a global ref. +struct GenericGlobalRefDeleter { + void operator()(jni::jobject* p) const { + if (p) { + auto env = AttachEnv(); + env->DeleteGlobalRef(jni::Unwrap(p)); + } + } +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 9d467f6ede..1930d1854d 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -1,5 +1,4 @@ #include "native_map_view.hpp" -#include "jni.hpp" #include <cstdlib> #include <ctime> @@ -10,90 +9,91 @@ #include <sys/system_properties.h> -#include <mbgl/util/platform.hpp> -#include <mbgl/util/event.hpp> -#include <mbgl/util/logging.hpp> -#include <mbgl/gl/extension.hpp> +#include <EGL/egl.h> +#include <android/native_window_jni.h> + +#include <jni/jni.hpp> + #include <mbgl/gl/context.hpp> +#include <mbgl/gl/extension.hpp> +#include <mbgl/map/backend_scope.hpp> #include <mbgl/util/constants.hpp> +#include <mbgl/util/event.hpp> +#include <mbgl/util/exception.hpp> +#include <mbgl/util/geo.hpp> #include <mbgl/util/image.hpp> +#include <mbgl/util/shared_thread_pool.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/util/platform.hpp> +#include <mbgl/sprite/sprite_image.hpp> -namespace mbgl { -namespace android { - -NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio, int availableProcessors_, size_t totalMemory_) - : env(env_), - availableProcessors(availableProcessors_), - totalMemory(totalMemory_), - threadPool(4) { +#include "conversion/conversion.hpp" +#include "conversion/collection.hpp" +#include "geometry/conversion/feature.hpp" - assert(env_ != nullptr); - assert(obj_ != nullptr); +#include "jni.hpp" +#include "attach_env.hpp" +#include "bitmap.hpp" +#include "run_loop_impl.hpp" +#include "java/util.hpp" - if (env->GetJavaVM(&vm) < 0) { - env->ExceptionDescribe(); - return; - } +namespace mbgl { +namespace android { - obj = env->NewWeakGlobalRef(obj_); - if (obj == nullptr) { - env->ExceptionDescribe(); +NativeMapView::NativeMapView(jni::JNIEnv& _env, jni::Object<NativeMapView> _obj, jni::Object<FileSource> jFileSource, + jni::jfloat _pixelRatio, jni::jint _availableProcessors, jni::jlong _totalMemory) : + javaPeer(_obj.NewWeakGlobalRef(_env)), + pixelRatio(_pixelRatio), + availableProcessors(_availableProcessors), + totalMemory(_totalMemory), + threadPool(sharedThreadPool()) { + + // Get a reference to the JavaVM for callbacks + if (_env.GetJavaVM(&vm) < 0) { + _env.ExceptionDescribe(); return; } - fileSource = std::make_unique<mbgl::DefaultFileSource>( - mbgl::android::cachePath + "/mbgl-offline.db", - mbgl::android::apkPath); - + // Create the core map map = std::make_unique<mbgl::Map>( *this, mbgl::Size{ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }, - pixelRatio, *fileSource, threadPool, MapMode::Continuous); + pixelRatio, mbgl::android::FileSource::getDefaultFileSource(_env, jFileSource) + , *threadPool, MapMode::Continuous); + //Calculate a fitting cache size based on device parameters 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->getSize().width) / mbgl::util::tileSize) * - (static_cast<float>(map->getSize().height) / mbgl::util::tileSize); - - size_t cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f; + (static_cast<float>(map->getSize().height) / mbgl::util::tileSize); - map->setSourceTileCacheSize(cacheSize); + map->setSourceTileCacheSize(zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f); } +/** + * Called through NativeMapView#destroy() + */ NativeMapView::~NativeMapView() { - terminateContext(); - destroySurface(); - terminateDisplay(); - - assert(vm != nullptr); - assert(obj != nullptr); + _terminateContext(); + _destroySurface(); + _terminateDisplay(); map.reset(); - fileSource.reset(); - - env->DeleteWeakGlobalRef(obj); - obj = nullptr; - env = nullptr; vm = nullptr; } -mbgl::Size NativeMapView::getFramebufferSize() const { - return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) }; -} - -void NativeMapView::updateViewBinding() { - getContext().bindFramebuffer.setCurrentValue(0); - assert(mbgl::gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue()); - getContext().viewport.setCurrentValue({ 0, 0, getFramebufferSize() }); - assert(mbgl::gl::value::Viewport::Get() == getContext().viewport.getCurrentValue()); -} - +/** + * From mbgl::View + */ void NativeMapView::bind() { getContext().bindFramebuffer = 0; getContext().viewport = { 0, 0, getFramebufferSize() }; } +/** + * From mbgl::Backend. + */ void NativeMapView::activate() { if (active++) { return; @@ -122,6 +122,9 @@ void NativeMapView::activate() { } } +/** + * From mbgl::Backend. + */ void NativeMapView::deactivate() { if (--active) { return; @@ -146,17 +149,57 @@ void NativeMapView::deactivate() { } } +/** + * From mbgl::Backend. Callback to java NativeMapView#onInvalidate(). + * + * May be called from any thread + */ void NativeMapView::invalidate() { + android::UniqueEnv _env = android::AttachEnv(); + static auto onInvalidate = javaClass.GetMethod<void ()>(*_env, "onInvalidate"); + javaPeer->Call(*_env, onInvalidate); +} + +/** + * From mbgl::Backend. Callback to java NativeMapView#onMapChanged(int). + * + * May be called from any thread + */ +void NativeMapView::notifyMapChange(mbgl::MapChange change) { assert(vm != nullptr); - assert(obj != nullptr); - env->CallVoidMethod(obj, onInvalidateId); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - } + android::UniqueEnv _env = android::AttachEnv(); + static auto onMapChanged = javaClass.GetMethod<void (int)>(*_env, "onMapChanged"); + javaPeer->Call(*_env, onMapChanged, (int) change); +} + +// JNI Methods // + +void NativeMapView::initializeDisplay(jni::JNIEnv&) { + _initializeDisplay(); } -void NativeMapView::render() { +void NativeMapView::terminateDisplay(jni::JNIEnv&) { + _terminateDisplay(); +} + +void NativeMapView::initializeContext(jni::JNIEnv&) { + _initializeContext(); +} + +void NativeMapView::terminateContext(jni::JNIEnv&) { + _terminateContext(); +} + +void NativeMapView::createSurface(jni::JNIEnv& env, jni::Object<> _surface) { + _createSurface(ANativeWindow_fromSurface(&env, jni::Unwrap(*_surface))); +} + +void NativeMapView::destroySurface(jni::JNIEnv&) { + _destroySurface(); +} + +void NativeMapView::render(jni::JNIEnv& env) { BackendScope guard(*this); if (framebufferSizeChanged) { @@ -172,17 +215,12 @@ void NativeMapView::render() { // take snapshot auto image = getContext().readFramebuffer<mbgl::PremultipliedImage>(getFramebufferSize()); - - // encode and convert to jbytes - std::string string = encodePNG(image); - jbyteArray arr = env->NewByteArray(string.length()); - env->SetByteArrayRegion(arr,0,string.length(),(jbyte*)string.c_str()); + auto bitmap = Bitmap::CreateBitmap(env, std::move(image)); // invoke Mapview#OnSnapshotReady - env->CallVoidMethod(obj, onSnapshotReadyId, arr); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - } + android::UniqueEnv _env = android::AttachEnv(); + static auto onSnapshotReady = javaClass.GetMethod<void (jni::Object<Bitmap>)>(*_env, "onSnapshotReady"); + javaPeer->Call(*_env, onSnapshotReady, bitmap); } if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) { @@ -198,11 +236,736 @@ void NativeMapView::render() { } } -mbgl::Map &NativeMapView::getMap() { return *map; } +void NativeMapView::update(jni::JNIEnv&) { + invalidate(); +} + +void NativeMapView::resizeView(jni::JNIEnv&, int w, int h) { + width = w; + height = h; + map->setSize({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); +} + +void NativeMapView::resizeFramebuffer(jni::JNIEnv&, int w, int h) { + fbWidth = w; + fbHeight = h; + framebufferSizeChanged = true; + invalidate(); +} + +jni::String NativeMapView::getStyleUrl(jni::JNIEnv& env) { + return jni::Make<jni::String>(env, map->getStyleURL()); +} + +void NativeMapView::setStyleUrl(jni::JNIEnv& env, jni::String url) { + map->setStyleURL(jni::Make<std::string>(env, url)); +} + +jni::String NativeMapView::getStyleJson(jni::JNIEnv& env) { + return jni::Make<jni::String>(env, map->getStyleJSON()); +} + +void NativeMapView::setStyleJson(jni::JNIEnv& env, jni::String json) { + map->setStyleJSON(jni::Make<std::string>(env, json)); +} + +void NativeMapView::cancelTransitions(jni::JNIEnv&) { + map->cancelTransitions(); +} + +void NativeMapView::setGestureInProgress(jni::JNIEnv&, jni::jboolean inProgress) { + map->setGestureInProgress(inProgress); +} + +void NativeMapView::moveBy(jni::JNIEnv&, jni::jdouble dx, jni::jdouble dy, jni::jlong duration) { + mbgl::AnimationOptions animationOptions; + if (duration > 0) { + animationOptions.duration.emplace(mbgl::Milliseconds(duration)); + animationOptions.easing.emplace(mbgl::util::UnitBezier { 0, 0.3, 0.6, 1.0 }); + } + map->moveBy({dx, dy}, animationOptions); +} + +void NativeMapView::jumpTo(jni::JNIEnv&, jni::jdouble angle, jni::jdouble latitude, jni::jdouble longitude, jni::jdouble pitch, jni::jdouble zoom) { + mbgl::CameraOptions options; + if (angle != -1) { + options.angle = (-angle * M_PI) / 180; + } + options.center = mbgl::LatLng(latitude, longitude); + options.padding = insets; + if (pitch != -1) { + options.pitch = pitch * M_PI / 180; + } + if (zoom != -1) { + options.zoom = zoom; + } + + map->jumpTo(options); +} + +void NativeMapView::easeTo(jni::JNIEnv&, jni::jdouble angle, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom, jni::jboolean easing) { + mbgl::CameraOptions cameraOptions; + if (angle != -1) { + cameraOptions.angle = (-angle * M_PI) / 180; + } + cameraOptions.center = mbgl::LatLng(latitude, longitude); + cameraOptions.padding = insets; + 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 }); + } + + map->easeTo(cameraOptions, animationOptions); +} + +void NativeMapView::flyTo(jni::JNIEnv&, jni::jdouble angle, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom) { + mbgl::CameraOptions cameraOptions; + if (angle != -1) { + cameraOptions.angle = (-angle * M_PI) / 180 ; + } + cameraOptions.center = mbgl::LatLng(latitude, longitude); + cameraOptions.padding = insets; + if (pitch != -1) { + cameraOptions.pitch = pitch * M_PI / 180; + } + if (zoom != -1) { + cameraOptions.zoom = zoom; + } + + mbgl::AnimationOptions animationOptions; + animationOptions.duration.emplace(mbgl::Duration(duration)); + map->flyTo(cameraOptions, animationOptions); +} + +jni::Object<LatLng> NativeMapView::getLatLng(JNIEnv& env) { + mbgl::LatLng latLng = map->getLatLng(insets); + return LatLng::New(env, latLng.latitude, latLng.longitude); +} + +void NativeMapView::setLatLng(jni::JNIEnv&, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration) { + map->setLatLng(mbgl::LatLng(latitude, longitude), insets, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +void NativeMapView::setReachability(jni::JNIEnv&, jni::jboolean reachable) { + if (reachable) { + mbgl::NetworkStatus::Reachable(); + } +} + +void NativeMapView::resetPosition(jni::JNIEnv&) { + map->resetPosition(); +} + +jni::jdouble NativeMapView::getPitch(jni::JNIEnv&) { + return map->getPitch(); +} + +void NativeMapView::setPitch(jni::JNIEnv&, jni::jdouble pitch, jni::jlong duration) { + map->setPitch(pitch, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +void NativeMapView::scaleBy(jni::JNIEnv&, jni::jdouble ds, jni::jdouble cx, jni::jdouble cy, jni::jlong duration) { + mbgl::ScreenCoordinate center(cx, cy); + map->scaleBy(ds, center, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +void NativeMapView::setScale(jni::JNIEnv&, jni::jdouble scale, jni::jdouble cx, jni::jdouble cy, jni::jlong duration) { + mbgl::ScreenCoordinate center(cx, cy); + map->setScale(scale, center, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +jni::jdouble NativeMapView::getScale(jni::JNIEnv&) { + return map->getScale(); +} + +void NativeMapView::setZoom(jni::JNIEnv&, jni::jdouble zoom, jni::jlong duration) { + map->setZoom(zoom, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +jni::jdouble NativeMapView::getZoom(jni::JNIEnv&) { + return map->getZoom(); +} + +void NativeMapView::resetZoom(jni::JNIEnv&) { + map->resetZoom(); +} + +void NativeMapView::setMinZoom(jni::JNIEnv&, jni::jdouble zoom) { + map->setMinZoom(zoom); +} + +jni::jdouble NativeMapView::getMinZoom(jni::JNIEnv&) { + return map->getMinZoom(); +} + +void NativeMapView::setMaxZoom(jni::JNIEnv&, jni::jdouble zoom) { + map->setMaxZoom(zoom); +} + +jni::jdouble NativeMapView::getMaxZoom(jni::JNIEnv&) { + return map->getMaxZoom(); +} + +void NativeMapView::rotateBy(jni::JNIEnv&, jni::jdouble sx, jni::jdouble sy, jni::jdouble ex, jni::jdouble ey, jni::jlong duration) { + mbgl::ScreenCoordinate first(sx, sy); + mbgl::ScreenCoordinate second(ex, ey); + map->rotateBy(first, second, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +void NativeMapView::setBearing(jni::JNIEnv&, jni::jdouble degrees, jni::jlong duration) { + map->setBearing(degrees, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +void NativeMapView::setBearingXY(jni::JNIEnv&, jni::jdouble degrees, jni::jdouble cx, jni::jdouble cy, jni::jlong duration) { + mbgl::ScreenCoordinate center(cx, cy); + map->setBearing(degrees, center, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +jni::jdouble NativeMapView::getBearing(jni::JNIEnv&) { + return map->getBearing(); +} + +void NativeMapView::resetNorth(jni::JNIEnv&) { + map->resetNorth(); +} + +void NativeMapView::setVisibleCoordinateBounds(JNIEnv& env, jni::Array<jni::Object<LatLng>> coordinates, jni::Object<RectF> padding, jdouble direction, jni::jlong duration) { + NullCheck(env, &coordinates); + std::size_t count = coordinates.Length(env); + + std::vector<mbgl::LatLng> latLngs; + latLngs.reserve(count); + + for (std::size_t i = 0; i < count; i++) { + auto latLng = coordinates.Get(env, i); + latLngs.push_back(LatLng::getLatLng(env, latLng)); + jni::DeleteLocalRef(env, latLng); + } + + mbgl::EdgeInsets mbglInsets = { RectF::getTop(env, padding), RectF::getLeft(env, padding), RectF::getBottom(env, padding), RectF::getRight(env, padding) }; + mbgl::CameraOptions cameraOptions = map->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 }); + } + + map->easeTo(cameraOptions, animationOptions); +} + +void NativeMapView::setContentPadding(JNIEnv&, double top, double left, double bottom, double right) { + insets = {top, left, bottom, right}; +} + +void NativeMapView::scheduleSnapshot(jni::JNIEnv&) { + snapshot = true; +} + +void NativeMapView::enableFps(jni::JNIEnv&, jni::jboolean enable) { + fpsEnabled = enable; +} + +jni::Array<jni::jdouble> NativeMapView::getCameraValues(jni::JNIEnv& env) { + //Create buffer with values + jdouble buf[5]; + mbgl::LatLng latLng = map->getLatLng(insets); + buf[0] = latLng.latitude; + buf[1] = latLng.longitude; + buf[2] = -map->getBearing(); + buf[3] = map->getPitch(); + buf[4] = map->getZoom(); + + //Convert to Java array + auto output = jni::Array<jni::jdouble>::New(env, 5); + jni::SetArrayRegion(env, *output, 0, 5, buf); + + return output; +} + +void NativeMapView::updateMarker(jni::JNIEnv& env, jni::jlong markerId, jni::jdouble lat, jni::jdouble lon, jni::String jid) { + if (markerId == -1) { + return; + } + + std::string iconId = jni::Make<std::string>(env, jid); + // Because Java only has int, not unsigned int, we need to bump the annotation id up to a long. + map->updateAnnotation(markerId, mbgl::SymbolAnnotation { mbgl::Point<double>(lon, lat), iconId }); +} + +jni::Array<jni::jlong> NativeMapView::addMarkers(jni::JNIEnv& env, jni::Array<jni::Object<Marker>> jmarkers) { + jni::NullCheck(env, &jmarkers); + std::size_t len = jmarkers.Length(env); + + std::vector<jni::jlong> ids; + ids.reserve(len); + + for (std::size_t i = 0; i < len; i++) { + jni::Object<Marker> marker = jmarkers.Get(env, i); + ids.push_back(map->addAnnotation(mbgl::SymbolAnnotation { + Marker::getPosition(env, marker), + Marker::getIconId(env, marker) + })); + + jni::DeleteLocalRef(env, marker); + } + + auto result = jni::Array<jni::jlong>::New(env, len); + result.SetRegion<std::vector<jni::jlong>>(env, 0, ids); + + return result; +} + +void NativeMapView::onLowMemory(JNIEnv&) { + map->onLowMemory(); +} + +using DebugOptions = mbgl::MapDebugOptions; + +void NativeMapView::setDebug(JNIEnv&, jni::jboolean debug) { + DebugOptions debugOptions = debug ? DebugOptions::TileBorders | DebugOptions::ParseStatus | DebugOptions::Collision + : DebugOptions::NoDebug; + map->setDebug(debugOptions); + fpsEnabled = debug; +} + +void NativeMapView::cycleDebugOptions(JNIEnv&) { + map->cycleDebugOptions(); + fpsEnabled = map->getDebug() != DebugOptions::NoDebug; +} + +jni::jboolean NativeMapView::getDebug(JNIEnv&) { + return map->getDebug() != DebugOptions::NoDebug; +} + +jni::jboolean NativeMapView::isFullyLoaded(JNIEnv&) { + return map->isFullyLoaded(); +} + +jni::jdouble NativeMapView::getMetersPerPixelAtLatitude(JNIEnv&, jni::jdouble lat, jni::jdouble zoom) { + return map->getMetersPerPixelAtLatitude(lat, zoom); +} + +jni::Object<ProjectedMeters> NativeMapView::projectedMetersForLatLng(JNIEnv& env, jni::jdouble latitude, jni::jdouble longitude) { + mbgl::ProjectedMeters projectedMeters = map->projectedMetersForLatLng(mbgl::LatLng(latitude, longitude)); + return ProjectedMeters::New(env, projectedMeters.northing, projectedMeters.easting); +} + +jni::Object<PointF> NativeMapView::pixelForLatLng(JNIEnv& env, jdouble latitude, jdouble longitude) { + mbgl::ScreenCoordinate pixel = map->pixelForLatLng(mbgl::LatLng(latitude, longitude)); + return PointF::New(env, static_cast<float>(pixel.x), static_cast<float>(pixel.y)); +} + +jni::Object<LatLng> NativeMapView::latLngForProjectedMeters(JNIEnv& env, jdouble northing, jdouble easting) { + mbgl::LatLng latLng = map->latLngForProjectedMeters(mbgl::ProjectedMeters(northing, easting)); + return LatLng::New(env, latLng.latitude, latLng.longitude); +} + +jni::Object<LatLng> NativeMapView::latLngForPixel(JNIEnv& env, jfloat x, jfloat y) { + mbgl::LatLng latLng = map->latLngForPixel(mbgl::ScreenCoordinate(x, y)); + return LatLng::New(env, latLng.latitude, latLng.longitude); +} + +jni::Array<jlong> NativeMapView::addPolylines(JNIEnv& env, jni::Array<jni::Object<Polyline>> polylines) { + NullCheck(env, &polylines); + std::size_t len = polylines.Length(env); + + std::vector<jni::jlong> ids; + ids.reserve(len); + + for (std::size_t i = 0; i < len; i++) { + auto polyline = polylines.Get(env, i); + + mbgl::LineAnnotation annotation = Polyline::toAnnotation(env, polyline); + ids.push_back(map->addAnnotation(annotation)); + + jni::DeleteLocalRef(env, polyline); + } + + auto result = jni::Array<jni::jlong>::New(env, len); + result.SetRegion<std::vector<jni::jlong>>(env, 0, ids); + + return result; +} + + +jni::Array<jlong> NativeMapView::addPolygons(JNIEnv& env, jni::Array<jni::Object<Polygon>> polygons) { + NullCheck(env, &polygons); + std::size_t len = polygons.Length(env); + + std::vector<jni::jlong> ids; + ids.reserve(len); + + for (std::size_t i = 0; i < len; i++) { + auto polygon = polygons.Get(env, i); + + mbgl::FillAnnotation annotation = Polygon::toAnnotation(env, polygon); + ids.push_back(map->addAnnotation(annotation)); + + jni::DeleteLocalRef(env, polygon); + } + + auto result = jni::Array<jni::jlong>::New(env, len); + result.SetRegion<std::vector<jni::jlong>>(env, 0, ids); + + return result; +} + +//TODO: Move to Polyline class and make native peer +void NativeMapView::updatePolyline(JNIEnv& env, jlong polylineId, jni::Object<Polyline> polyline) { + mbgl::LineAnnotation annotation = Polyline::toAnnotation(env, polyline); + map->updateAnnotation(polylineId, annotation); +} + +//TODO: Move to Polygon class and make native peer +void NativeMapView::updatePolygon(JNIEnv& env, jlong polygonId, jni::Object<Polygon> polygon) { + mbgl::FillAnnotation annotation = Polygon::toAnnotation(env, polygon); + map->updateAnnotation(polygonId, annotation); +} + +void NativeMapView::removeAnnotations(JNIEnv& env, jni::Array<jlong> ids) { + NullCheck(env, &ids); + std::size_t len = ids.Length(env); + auto elements = jni::GetArrayElements(env, *ids); + jlong* jids = std::get<0>(elements).get(); + + for (std::size_t i = 0; i < len; i++) { + if(jids[i] == -1L) { + continue; + } + map->removeAnnotation(jids[i]); + } +} + +void NativeMapView::addAnnotationIcon(JNIEnv& env, jni::String symbol, jint w, jint h, jfloat scale, jni::Array<jbyte> jpixels) { + const std::string symbolName = jni::Make<std::string>(env, symbol); + + NullCheck(env, &jpixels); + std::size_t size = jpixels.Length(env); + + mbgl::PremultipliedImage premultipliedImage({ static_cast<uint32_t>(w), static_cast<uint32_t>(h) }); + if (premultipliedImage.bytes() != uint32_t(size)) { + throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch"); + } + + jni::GetArrayRegion(env, *jpixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get())); + auto iconImage = std::make_shared<mbgl::SpriteImage>(std::move(premultipliedImage), float(scale)); + map->addAnnotationIcon(symbolName, iconImage); +} + +jdouble NativeMapView::getTopOffsetPixelsForAnnotationSymbol(JNIEnv& env, jni::String symbolName) { + return map->getTopOffsetPixelsForAnnotationIcon(jni::Make<std::string>(env, symbolName)); +} + +jlong NativeMapView::getTransitionDuration(JNIEnv&) { + const auto transitionOptions = map->getTransitionOptions(); + return transitionOptions.duration.value_or(mbgl::Duration::zero()).count(); +} + +void NativeMapView::setTransitionDuration(JNIEnv&, jlong duration) { + auto transitionOptions = map->getTransitionOptions(); + transitionOptions.duration = std::chrono::duration_cast<mbgl::Duration>(std::chrono::duration<jlong>(duration)); + map->setTransitionOptions(transitionOptions); +} + +jlong NativeMapView::getTransitionDelay(JNIEnv&) { + const auto transitionOptions = map->getTransitionOptions(); + return transitionOptions.delay.value_or(mbgl::Duration::zero()).count(); +} + +void NativeMapView::setTransitionDelay(JNIEnv&, jlong delay) { + auto transitionOptions = map->getTransitionOptions(); + transitionOptions.delay = std::chrono::duration_cast<mbgl::Duration>(std::chrono::duration<jlong>(delay)); + map->setTransitionOptions(transitionOptions); +} + +jni::Array<jlong> NativeMapView::queryPointAnnotations(JNIEnv& env, jni::Object<RectF> rect) { + // Convert input + mbgl::ScreenBox box = { + { RectF::getLeft(env, rect), RectF::getTop(env, rect) }, + { RectF::getRight(env, rect), RectF::getBottom(env, rect) }, + }; + + // Assume only points for now + mbgl::AnnotationIDs ids = map->queryPointAnnotations(box); + + // Convert result + std::vector<jlong> longIds(ids.begin(), ids.end()); + auto result = jni::Array<jni::jlong>::New(env, ids.size()); + result.SetRegion<std::vector<jni::jlong>>(env, 0, longIds); + + return result; +} + +jni::Array<jni::Object<Feature>> NativeMapView::queryRenderedFeaturesForPoint(JNIEnv& env, jni::jfloat x, jni::jfloat y, jni::Array<jni::String> layerIds) { + using namespace mbgl::android::conversion; + using namespace mapbox::geometry; + + mbgl::optional<std::vector<std::string>> layers; + if (layerIds != nullptr && layerIds.Length(env) > 0) { + layers = toVector(env, layerIds); + } + point<double> point = {x, y}; + + return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, map->queryRenderedFeatures(point, layers)); +} + +jni::Array<jni::Object<Feature>> NativeMapView::queryRenderedFeaturesForBox(JNIEnv& env, jni::jfloat left, jni::jfloat top, jni::jfloat right, jni::jfloat bottom, jni::Array<jni::String> layerIds) { + using namespace mbgl::android::conversion; + using namespace mapbox::geometry; + + mbgl::optional<std::vector<std::string>> layers; + if (layerIds != nullptr && layerIds.Length(env) > 0) { + layers = toVector(env, layerIds); + } + box<double> box = { point<double>{ left, top}, point<double>{ right, bottom } }; + + return *convert<jni::Array<jni::Object<Feature>>, std::vector<mbgl::Feature>>(env, map->queryRenderedFeatures(box, layers)); +} + +jni::Array<jni::Object<Layer>> NativeMapView::getLayers(JNIEnv& env) { + + // Get the core layers + std::vector<style::Layer*> layers = map->getLayers(); + + // Convert + jni::Array<jni::Object<Layer>> jLayers = jni::Array<jni::Object<Layer>>::New(env, layers.size(), Layer::javaClass); + int index = 0; + for (auto layer : layers) { + auto jLayer = jni::Object<Layer>(createJavaLayerPeer(env, *map, *layer)); + jLayers.Set(env, index, jLayer); + jni::DeleteLocalRef(env, jLayer); + index++; + } + + return jLayers; +} + +jni::Object<Layer> NativeMapView::getLayer(JNIEnv& env, jni::String layerId) { + + // Find the layer + mbgl::style::Layer* coreLayer = map->getLayer(jni::Make<std::string>(env, layerId)); + if (!coreLayer) { + mbgl::Log::Debug(mbgl::Event::JNI, "No layer found"); + return jni::Object<Layer>(); + } + + // Create and return the layer's native peer + return jni::Object<Layer>(createJavaLayerPeer(env, *map, *coreLayer)); +} -mbgl::DefaultFileSource &NativeMapView::getFileSource() { return *fileSource; } +void NativeMapView::addLayer(JNIEnv& env, jlong nativeLayerPtr, jni::String before) { + assert(nativeLayerPtr != 0); + + Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr); + try { + layer->addToMap(*map, before ? mbgl::optional<std::string>(jni::Make<std::string>(env, before)) : mbgl::optional<std::string>()); + } catch (const std::runtime_error& error) { + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); + } +} + +void NativeMapView::addLayerAbove(JNIEnv& env, jlong nativeLayerPtr, jni::String above) { + assert(nativeLayerPtr != 0); + + Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr); + + // Find the sibling + auto layers = map->getLayers(); + auto siblingId = jni::Make<std::string>(env, above); + + size_t index = 0; + for (auto l : layers) { + if (l->getID() == siblingId) { + break; + } + index++; + } + + // Check if we found a sibling to place before + mbgl::optional<std::string> before; + if (index + 1 > layers.size()) { + // Not found + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), + std::string("Could not find layer: ").append(siblingId).c_str()); + return; + } else if (index + 1 < layers.size()) { + // Place before the sibling + before = { layers.at(index + 1)->getID() }; + } + + // Add the layer + try { + layer->addToMap(*map, before); + } catch (const std::runtime_error& error) { + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); + } +} + +void NativeMapView::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint index) { + assert(nativeLayerPtr != 0); + + Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr); + auto layers = map->getLayers(); + + // Check index + int numLayers = layers.size() - 1; + if (index > numLayers || index < 0) { + Log::Error(Event::JNI, "Index out of range: %i", index); + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), + std::string("Invalid index").c_str()); + return; + } + + // Insert it below the current at that index + try { + layer->addToMap(*map, layers.at(index)->getID()); + } catch (const std::runtime_error& error) { + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); + } +} + +/** + * Remove by layer id. + */ +jni::Object<Layer> NativeMapView::removeLayerById(JNIEnv& env, jni::String id) { + std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(jni::Make<std::string>(env, id)); + if (coreLayer) { + return jni::Object<Layer>(createJavaLayerPeer(env, *map, std::move(coreLayer))); + } else { + return jni::Object<Layer>(); + } +} -void NativeMapView::initializeDisplay() { +/** + * Remove layer at index. + */ +jni::Object<Layer> NativeMapView::removeLayerAt(JNIEnv& env, jni::jint index) { + auto layers = map->getLayers(); + + // Check index + int numLayers = layers.size() - 1; + if (index > numLayers || index < 0) { + Log::Warning(Event::JNI, "Index out of range: %i", index); + return jni::Object<Layer>(); + } + + std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(layers.at(index)->getID()); + if (coreLayer) { + return jni::Object<Layer>(createJavaLayerPeer(env, *map, std::move(coreLayer))); + } else { + return jni::Object<Layer>(); + } +} + +/** + * Remove with wrapper object id. Ownership is transferred back to the wrapper + */ +void NativeMapView::removeLayer(JNIEnv&, jlong layerPtr) { + assert(layerPtr != 0); + + mbgl::android::Layer *layer = reinterpret_cast<mbgl::android::Layer *>(layerPtr); + std::unique_ptr<mbgl::style::Layer> coreLayer = map->removeLayer(layer->get().getID()); + if (coreLayer) { + layer->setLayer(std::move(coreLayer)); + } +} + +jni::Array<jni::Object<Source>> NativeMapView::getSources(JNIEnv& env) { + // Get the core sources + std::vector<style::Source*> sources = map->getSources(); + + // Convert + jni::Array<jni::Object<Source>> jSources = jni::Array<jni::Object<Source>>::New(env, sources.size(), Source::javaClass); + int index = 0; + for (auto source : sources) { + auto jSource = jni::Object<Source>(createJavaSourcePeer(env, *map, *source)); + jSources.Set(env, index, jSource); + jni::DeleteLocalRef(env, jSource); + index++; + } + + return jSources; +} + +jni::Object<Source> NativeMapView::getSource(JNIEnv& env, jni::String sourceId) { + // Find the source + mbgl::style::Source* coreSource = map->getSource(jni::Make<std::string>(env, sourceId)); + if (!coreSource) { + mbgl::Log::Debug(mbgl::Event::JNI, "No source found"); + return jni::Object<Source>(); + } + + // Create and return the source's native peer + return jni::Object<Source>(createJavaSourcePeer(env, *map, *coreSource)); +} + +void NativeMapView::addSource(JNIEnv& env, jni::jlong sourcePtr) { + assert(sourcePtr != 0); + + Source *source = reinterpret_cast<Source *>(sourcePtr); + try { + source->addToMap(*map); + } catch (const std::runtime_error& error) { + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/sources/CannotAddSourceException"), error.what()); + } +} + +jni::Object<Source> NativeMapView::removeSourceById(JNIEnv& env, jni::String id) { + std::unique_ptr<mbgl::style::Source> coreSource = map->removeSource(jni::Make<std::string>(env, id)); + if (coreSource) { + return jni::Object<Source>(createJavaSourcePeer(env, *map, *coreSource)); + } else { + return jni::Object<Source>(); + } +} + +void NativeMapView::removeSource(JNIEnv&, jlong sourcePtr) { + assert(sourcePtr != 0); + + mbgl::android::Source *source = reinterpret_cast<mbgl::android::Source *>(sourcePtr); + std::unique_ptr<mbgl::style::Source> coreSource = map->removeSource(source->get().getID()); + if (coreSource) { + source->setSource(std::move(coreSource)); + } +} + +void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::jint w, jni::jint h, jni::jfloat scale, jni::Array<jbyte> pixels) { + jni::NullCheck(env, &pixels); + std::size_t size = pixels.Length(env); + + mbgl::PremultipliedImage premultipliedImage({ static_cast<uint32_t>(w), static_cast<uint32_t>(h) }); + if (premultipliedImage.bytes() != uint32_t(size)) { + throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch"); + } + + jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get())); + auto spriteImage = std::make_unique<mbgl::SpriteImage>(std::move(premultipliedImage), float(scale)); + + map->addImage(jni::Make<std::string>(env, name), std::move(spriteImage)); +} + +void NativeMapView::removeImage(JNIEnv& env, jni::String name) { + map->removeImage(jni::Make<std::string>(env, name)); +} + +// Private methods // + +void NativeMapView::_initializeDisplay() { assert(display == EGL_NO_DISPLAY); assert(config == nullptr); assert(format < 0); @@ -287,145 +1050,6 @@ void NativeMapView::initializeDisplay() { } } -void 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 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 std::runtime_error("eglMakeCurrent() failed"); - } - - if (!eglTerminate(display)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d", - eglGetError()); - throw std::runtime_error("eglTerminate() failed"); - } - } - - display = EGL_NO_DISPLAY; - config = nullptr; - format = -1; -} - -void 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 std::runtime_error("eglCreateContext() failed"); - } -} - -void 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 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 std::runtime_error("eglDestroyContext() failed"); - } - } - } - - context = EGL_NO_CONTEXT; -} - -void NativeMapView::createSurface(ANativeWindow *window_) { - 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 std::runtime_error("eglCreateWindowSurface() failed"); - } - - if (!firstTime) { - firstTime = true; - - BackendScope guard(*this); - - if (!eglMakeCurrent(display, surface, surface, context)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", - eglGetError()); - throw std::runtime_error("eglMakeCurrent() failed"); - } - - mbgl::gl::InitializeExtensions([] (const char * name) { - return reinterpret_cast<mbgl::gl::glProc>(eglGetProcAddress(name)); - }); - } -} - -void NativeMapView::destroySurface() { - if (surface != EGL_NO_SURFACE) { - if (!eglDestroySurface(display, surface)) { - mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d", - eglGetError()); - throw std::runtime_error("eglDestroySurface() failed"); - } - } - - surface = EGL_NO_SURFACE; - - if (window != nullptr) { - ANativeWindow_release(window); - window = nullptr; - } -} - -void NativeMapView::scheduleTakeSnapshot() { - snapshot = true; -} - -// 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, @@ -590,18 +1214,134 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfi return configId; } -void NativeMapView::notifyMapChange(mbgl::MapChange change) { - assert(vm != nullptr); - assert(obj != nullptr); +void 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 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 std::runtime_error("eglMakeCurrent() failed"); + } + + if (!eglTerminate(display)) { + mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d", + eglGetError()); + throw std::runtime_error("eglTerminate() failed"); + } + } + + display = EGL_NO_DISPLAY; + config = nullptr; + format = -1; +} - env->CallVoidMethod(obj, onMapChangedId, change); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); +void 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 std::runtime_error("eglCreateContext() failed"); } } -void NativeMapView::enableFps(bool enable) { - fpsEnabled = enable; +void 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 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 std::runtime_error("eglDestroyContext() failed"); + } + } + } + + context = EGL_NO_CONTEXT; +} + +void NativeMapView::_createSurface(ANativeWindow *window_) { + 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 std::runtime_error("eglCreateWindowSurface() failed"); + } + + if (firstRender) { + firstRender = false; + + BackendScope guard(*this); + + if (!eglMakeCurrent(display, surface, surface, context)) { + mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", + eglGetError()); + throw std::runtime_error("eglMakeCurrent() failed"); + } + + mbgl::gl::InitializeExtensions([] (const char * name) { + return reinterpret_cast<mbgl::gl::glProc>(eglGetProcAddress(name)); + }); + } +} + +void NativeMapView::_destroySurface() { + if (surface != EGL_NO_SURFACE) { + if (!eglDestroySurface(display, surface)) { + mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d", + eglGetError()); + throw std::runtime_error("eglDestroySurface() failed"); + } + } + + surface = EGL_NO_SURFACE; + + if (window != nullptr) { + ANativeWindow_release(window); + window = nullptr; + } +} + +mbgl::Size NativeMapView::getFramebufferSize() const { + return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) }; +} + +void NativeMapView::updateViewBinding() { + getContext().bindFramebuffer.setCurrentValue(0); + assert(mbgl::gl::value::BindFramebuffer::Get() == getContext().bindFramebuffer.getCurrentValue()); + getContext().viewport.setCurrentValue({ 0, 0, getFramebufferSize() }); + assert(mbgl::gl::value::Viewport::Get() == getContext().viewport.getCurrentValue()); } void NativeMapView::updateFps() { @@ -619,35 +1359,121 @@ void NativeMapView::updateFps() { if (currentTime - timeElapsed >= 1) { fps = frames / ((currentTime - timeElapsed) / 1E9); - mbgl::Log::Debug(mbgl::Event::Render, "FPS: %4.2f", fps); + mbgl::Log::Info(mbgl::Event::Render, "FPS: %4.2f", fps); timeElapsed = currentTime; frames = 0; } assert(vm != nullptr); - assert(obj != nullptr); - - env->CallVoidMethod(obj, onFpsChangedId, fps); - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - } -} -void NativeMapView::resizeView(int w, int h) { - width = w; - height = h; - map->setSize({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) }); -} - -void NativeMapView::resizeFramebuffer(int w, int h) { - fbWidth = w; - fbHeight = h; - framebufferSizeChanged = true; - invalidate(); + android::UniqueEnv _env = android::AttachEnv(); + static auto onFpsChanged = javaClass.GetMethod<void (double)>(*_env, "onFpsChanged"); + javaPeer->Call(*_env, onFpsChanged, fps); } -void NativeMapView::setInsets(mbgl::EdgeInsets insets_) { - insets = insets_; +// Static methods // + +jni::Class<NativeMapView> NativeMapView::javaClass; + +void NativeMapView::registerNative(jni::JNIEnv& env) { + // Lookup the class + NativeMapView::javaClass = *jni::Class<NativeMapView>::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<NativeMapView>(env, NativeMapView::javaClass, "nativePtr", + std::make_unique<NativeMapView, JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::jfloat, jni::jint, jni::jlong>, + "nativeInitialize", + "nativeDestroy", + METHOD(&NativeMapView::render, "nativeRender"), + METHOD(&NativeMapView::update, "nativeUpdate"), + METHOD(&NativeMapView::resizeView, "nativeResizeView"), + METHOD(&NativeMapView::resizeFramebuffer, "nativeResizeFramebuffer"), + METHOD(&NativeMapView::initializeDisplay, "nativeInitializeDisplay"), + METHOD(&NativeMapView::terminateDisplay, "nativeTerminateDisplay"), + METHOD(&NativeMapView::initializeContext, "nativeInitializeContext"), + METHOD(&NativeMapView::terminateContext, "nativeTerminateContext"), + METHOD(&NativeMapView::createSurface, "nativeCreateSurface"), + METHOD(&NativeMapView::destroySurface, "nativeDestroySurface"), + METHOD(&NativeMapView::getStyleUrl, "nativeGetStyleUrl"), + METHOD(&NativeMapView::setStyleUrl, "nativeSetStyleUrl"), + METHOD(&NativeMapView::getStyleJson, "nativeGetStyleJson"), + METHOD(&NativeMapView::setStyleJson, "nativeSetStyleJson"), + METHOD(&NativeMapView::cancelTransitions, "nativeCancelTransitions"), + METHOD(&NativeMapView::setGestureInProgress, "nativeSetGestureInProgress"), + METHOD(&NativeMapView::moveBy, "nativeMoveBy"), + METHOD(&NativeMapView::jumpTo, "nativeJumpTo"), + METHOD(&NativeMapView::easeTo, "nativeEaseTo"), + METHOD(&NativeMapView::flyTo, "nativeFlyTo"), + METHOD(&NativeMapView::getLatLng, "nativeGetLatLng"), + METHOD(&NativeMapView::setLatLng, "nativeSetLatLng"), + METHOD(&NativeMapView::setReachability, "nativeSetReachability"), + METHOD(&NativeMapView::resetPosition, "nativeResetPosition"), + METHOD(&NativeMapView::getPitch, "nativeGetPitch"), + METHOD(&NativeMapView::setPitch, "nativeSetPitch"), + METHOD(&NativeMapView::scaleBy, "nativeScaleBy"), + METHOD(&NativeMapView::getScale, "nativeGetScale"), + METHOD(&NativeMapView::setScale, "nativeSetScale"), + METHOD(&NativeMapView::getZoom, "nativeGetZoom"), + METHOD(&NativeMapView::setZoom, "nativeSetZoom"), + METHOD(&NativeMapView::resetZoom, "nativeResetZoom"), + METHOD(&NativeMapView::setMinZoom, "nativeSetMinZoom"), + METHOD(&NativeMapView::getMinZoom, "nativeGetMinZoom"), + METHOD(&NativeMapView::setMaxZoom, "nativeSetMaxZoom"), + METHOD(&NativeMapView::getMaxZoom, "nativeGetMaxZoom"), + METHOD(&NativeMapView::rotateBy, "nativeRotateBy"), + METHOD(&NativeMapView::setBearing, "nativeSetBearing"), + METHOD(&NativeMapView::setBearingXY, "nativeSetBearingXY"), + METHOD(&NativeMapView::getBearing, "nativeGetBearing"), + METHOD(&NativeMapView::resetNorth, "nativeResetNorth"), + METHOD(&NativeMapView::setVisibleCoordinateBounds, "nativeSetVisibleCoordinateBounds"), + METHOD(&NativeMapView::setContentPadding, "nativeSetContentPadding"), + METHOD(&NativeMapView::scheduleSnapshot, "nativeTakeSnapshot"), + METHOD(&NativeMapView::enableFps, "nativeSetEnableFps"), + METHOD(&NativeMapView::getCameraValues, "nativeGetCameraValues"), + METHOD(&NativeMapView::updateMarker, "nativeUpdateMarker"), + METHOD(&NativeMapView::addMarkers, "nativeAddMarkers"), + METHOD(&NativeMapView::setDebug, "nativeSetDebug"), + METHOD(&NativeMapView::cycleDebugOptions, "nativeCycleDebugOptions"), + METHOD(&NativeMapView::getDebug, "nativeGetDebug"), + METHOD(&NativeMapView::isFullyLoaded, "nativeIsFullyLoaded"), + METHOD(&NativeMapView::onLowMemory, "nativeOnLowMemory"), + METHOD(&NativeMapView::getMetersPerPixelAtLatitude, "nativeGetMetersPerPixelAtLatitude"), + METHOD(&NativeMapView::projectedMetersForLatLng, "nativeProjectedMetersForLatLng"), + METHOD(&NativeMapView::pixelForLatLng, "nativePixelForLatLng"), + METHOD(&NativeMapView::latLngForProjectedMeters, "nativeLatLngForProjectedMeters"), + METHOD(&NativeMapView::latLngForPixel, "nativeLatLngForPixel"), + METHOD(&NativeMapView::addPolylines, "nativeAddPolylines"), + METHOD(&NativeMapView::addPolygons, "nativeAddPolygons"), + METHOD(&NativeMapView::updatePolyline, "nativeUpdatePolyline"), + METHOD(&NativeMapView::updatePolygon, "nativeUpdatePolygon"), + METHOD(&NativeMapView::removeAnnotations, "nativeRemoveAnnotations"), + METHOD(&NativeMapView::addAnnotationIcon, "nativeAddAnnotationIcon"), + METHOD(&NativeMapView::getTopOffsetPixelsForAnnotationSymbol, "nativeGetTopOffsetPixelsForAnnotationSymbol"), + METHOD(&NativeMapView::getTransitionDuration, "nativeGetTransitionDuration"), + METHOD(&NativeMapView::setTransitionDuration, "nativeSetTransitionDuration"), + METHOD(&NativeMapView::getTransitionDelay, "nativeGetTransitionDelay"), + METHOD(&NativeMapView::setTransitionDelay, "nativeSetTransitionDelay"), + METHOD(&NativeMapView::queryPointAnnotations, "nativeQueryPointAnnotations"), + METHOD(&NativeMapView::queryRenderedFeaturesForPoint, "nativeQueryRenderedFeaturesForPoint"), + METHOD(&NativeMapView::queryRenderedFeaturesForBox, "nativeQueryRenderedFeaturesForBox"), + METHOD(&NativeMapView::getLayers, "nativeGetLayers"), + METHOD(&NativeMapView::getLayer, "nativeGetLayer"), + METHOD(&NativeMapView::addLayer, "nativeAddLayer"), + METHOD(&NativeMapView::addLayerAbove, "nativeAddLayerAbove"), + METHOD(&NativeMapView::addLayerAt, "nativeAddLayerAt"), + METHOD(&NativeMapView::removeLayerById, "nativeRemoveLayerById"), + METHOD(&NativeMapView::removeLayerAt, "nativeRemoveLayerAt"), + METHOD(&NativeMapView::removeLayer, "nativeRemoveLayer"), + METHOD(&NativeMapView::getSources, "nativeGetSources"), + METHOD(&NativeMapView::getSource, "nativeGetSource"), + METHOD(&NativeMapView::addSource, "nativeAddSource"), + METHOD(&NativeMapView::removeSourceById, "nativeRemoveSourceById"), + METHOD(&NativeMapView::removeSource, "nativeRemoveSource"), + METHOD(&NativeMapView::addImage, "nativeAddImage"), + METHOD(&NativeMapView::removeImage, "nativeRemoveImage") + ); } } diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index e7379700a9..c38afd3e6c 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -1,71 +1,275 @@ #pragma once +#include <mbgl/map/backend.hpp> +#include <mbgl/map/camera.hpp> #include <mbgl/map/map.hpp> #include <mbgl/map/view.hpp> -#include <mbgl/map/backend.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/default_thread_pool.hpp> +#include <mbgl/util/run_loop.hpp> #include <mbgl/storage/default_file_source.hpp> +#include <mbgl/storage/network_status.hpp> + +#include "file_source.hpp" +#include "annotation/marker.hpp" +#include "annotation/polygon.hpp" +#include "annotation/polyline.hpp" +#include "graphics/pointf.hpp" +#include "graphics/rectf.hpp" +#include "geometry/feature.hpp" +#include "geometry/lat_lng.hpp" +#include "geometry/projected_meters.hpp" +#include "style/layers/layers.hpp" +#include "style/sources/sources.hpp" #include <string> #include <jni.h> #include <android/native_window.h> #include <EGL/egl.h> +#include <jni/jni.hpp> namespace mbgl { namespace android { -class NativeMapView : public mbgl::View, public mbgl::Backend { +class NativeMapView : public View, public Backend { public: - NativeMapView(JNIEnv *env, jobject obj, float pixelRatio, int availableProcessors, size_t totalMemory); + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/NativeMapView"; }; + + static jni::Class<NativeMapView> javaClass; + + static void registerNative(jni::JNIEnv&); + + NativeMapView(jni::JNIEnv&, jni::Object<NativeMapView>, jni::Object<FileSource>, jni::jfloat, jni::jint, jni::jlong); + virtual ~NativeMapView(); - mbgl::Size getFramebufferSize() const; - void updateViewBinding(); + // mbgl::View // + void bind() override; - void invalidate() override; + // mbgl::Backend // + void invalidate() override; void notifyMapChange(mbgl::MapChange) override; - mbgl::Map &getMap(); - mbgl::DefaultFileSource &getFileSource(); + // JNI // - void initializeDisplay(); - void terminateDisplay(); + void destroy(jni::JNIEnv&); - void initializeContext(); - void terminateContext(); + void render(jni::JNIEnv&); - void createSurface(ANativeWindow *window); - void destroySurface(); + void update(jni::JNIEnv&); - void render(); + void resizeView(jni::JNIEnv&, int, int); - void enableFps(bool enable); - void updateFps(); + void resizeFramebuffer(jni::JNIEnv&, int, int); + + void initializeDisplay(jni::JNIEnv&); + + void terminateDisplay(jni::JNIEnv&); + + void initializeContext(jni::JNIEnv&); + + void terminateContext(jni::JNIEnv&); + + void createSurface(jni::JNIEnv&, jni::Object<>); + + void destroySurface(jni::JNIEnv&); + + jni::String getStyleUrl(jni::JNIEnv&); + + void setStyleUrl(jni::JNIEnv&, jni::String); + + jni::String getStyleJson(jni::JNIEnv&); + + void setStyleJson(jni::JNIEnv&, jni::String); + + void cancelTransitions(jni::JNIEnv&); + + void setGestureInProgress(jni::JNIEnv&, jni::jboolean); + + void moveBy(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jlong); + + void jumpTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble); + + void easeTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble, jni::jboolean); + + void flyTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble); + + jni::Object<LatLng> getLatLng(JNIEnv&); + + void setLatLng(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jlong); + + void setReachability(jni::JNIEnv&, jni::jboolean); + + void resetPosition(jni::JNIEnv&); + + jni::jdouble getPitch(jni::JNIEnv&); + + void setPitch(jni::JNIEnv&, jni::jdouble, jni::jlong); + + void scaleBy(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong); + + void setScale(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong); + + jni::jdouble getScale(jni::JNIEnv&); + + void setZoom(jni::JNIEnv&, jni::jdouble, jni::jlong); + + jni::jdouble getZoom(jni::JNIEnv&); + + void resetZoom(jni::JNIEnv&); + + void setMinZoom(jni::JNIEnv&, jni::jdouble); + + jni::jdouble getMinZoom(jni::JNIEnv&); + + void setMaxZoom(jni::JNIEnv&, jni::jdouble); + + jni::jdouble getMaxZoom(jni::JNIEnv&); + + void rotateBy(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong); + + void setBearing(jni::JNIEnv&, jni::jdouble, jni::jlong); + + void setBearingXY(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong); + + jni::jdouble getBearing(jni::JNIEnv&); + + void resetNorth(jni::JNIEnv&); + + void setVisibleCoordinateBounds(JNIEnv&, jni::Array<jni::Object<LatLng>>, jni::Object<RectF>, jni::jdouble, jni::jlong); + + void setContentPadding(JNIEnv&, double, double, double, double); + + void scheduleSnapshot(jni::JNIEnv&); + + void enableFps(jni::JNIEnv&, jni::jboolean enable); + + jni::Array<jni::jdouble> getCameraValues(jni::JNIEnv&); + + void updateMarker(jni::JNIEnv&, jni::jlong, jni::jdouble, jni::jdouble, jni::String); + + jni::Array<jni::jlong> addMarkers(jni::JNIEnv&, jni::Array<jni::Object<Marker>>); + + void onLowMemory(JNIEnv& env); + + void setDebug(JNIEnv&, jni::jboolean); - void resizeView(int width, int height); - void resizeFramebuffer(int width, int height); - mbgl::EdgeInsets getInsets() { return insets;} - void setInsets(mbgl::EdgeInsets insets_); + void cycleDebugOptions(JNIEnv&); - void scheduleTakeSnapshot(); + jni::jboolean getDebug(JNIEnv&); + + jni::jboolean isFullyLoaded(JNIEnv&); + + jni::jdouble getMetersPerPixelAtLatitude(JNIEnv&, jni::jdouble, jni::jdouble); + + jni::Object<ProjectedMeters> projectedMetersForLatLng(JNIEnv&, jni::jdouble, jni::jdouble); + + jni::Object<PointF> pixelForLatLng(JNIEnv&, jdouble, jdouble); + + jni::Object<LatLng> latLngForProjectedMeters(JNIEnv&, jdouble, jdouble); + + jni::Object<LatLng> latLngForPixel(JNIEnv&, jfloat, jfloat); + + jni::Array<jlong> addPolylines(JNIEnv&, jni::Array<jni::Object<Polyline>>); + + jni::Array<jlong> addPolygons(JNIEnv&, jni::Array<jni::Object<Polygon>>); + + void updatePolyline(JNIEnv&, jlong, jni::Object<Polyline>); + + void updatePolygon(JNIEnv&, jlong, jni::Object<Polygon>); + + void removeAnnotations(JNIEnv&, jni::Array<jlong>); + + void addAnnotationIcon(JNIEnv&, jni::String, jint, jint, jfloat, jni::Array<jbyte>); + + jni::jdouble getTopOffsetPixelsForAnnotationSymbol(JNIEnv&, jni::String); + + jni::jlong getTransitionDuration(JNIEnv&); + + void setTransitionDuration(JNIEnv&, jni::jlong); + + jni::jlong getTransitionDelay(JNIEnv&); + + void setTransitionDelay(JNIEnv&, jni::jlong); + + jni::Array<jlong> queryPointAnnotations(JNIEnv&, jni::Object<RectF>); + + jni::Array<jni::Object<Feature>> queryRenderedFeaturesForPoint(JNIEnv&, jni::jfloat, jni::jfloat, jni::Array<jni::String>); + + jni::Array<jni::Object<Feature>> queryRenderedFeaturesForBox(JNIEnv&, jni::jfloat, jni::jfloat, jni::jfloat, jni::jfloat, jni::Array<jni::String>); + + jni::Array<jni::Object<Layer>> getLayers(JNIEnv&); + + jni::Object<Layer> getLayer(JNIEnv&, jni::String); + + void addLayer(JNIEnv&, jlong, jni::String); + + void addLayerAbove(JNIEnv&, jlong, jni::String); + + void addLayerAt(JNIEnv&, jni::jlong, jni::jint); + + jni::Object<Layer> removeLayerById(JNIEnv&, jni::String); + + jni::Object<Layer> removeLayerAt(JNIEnv&, jni::jint); + + void removeLayer(JNIEnv&, jlong); + + jni::Array<jni::Object<Source>> getSources(JNIEnv&); + + jni::Object<Source> getSource(JNIEnv&, jni::String); + + void addSource(JNIEnv&, jni::jlong); + + jni::Object<Source> removeSourceById(JNIEnv&, jni::String); + + void removeSource(JNIEnv&, jlong); + + void addImage(JNIEnv&, jni::String, jni::jint, jni::jint, jni::jfloat, jni::Array<jbyte>); + + void removeImage(JNIEnv&, jni::String); protected: + // mbgl::Backend // + void activate() override; void deactivate() override; private: + void _initializeDisplay(); + + void _terminateDisplay(); + + void _initializeContext(); + + void _terminateContext(); + + void _createSurface(ANativeWindow*); + + void _destroySurface(); + EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs); + void updateViewBinding(); + mbgl::Size getFramebufferSize() const; + + void updateFps(); + private: + JavaVM *vm = nullptr; - JNIEnv *env = nullptr; - jweak obj = nullptr; + jni::UniqueWeakObject<NativeMapView> javaPeer; + + std::string styleUrl; + std::string apiKey; ANativeWindow *window = nullptr; + EGLConfig config = nullptr; + EGLint format = -1; + EGLDisplay oldDisplay = EGL_NO_DISPLAY; EGLSurface oldReadSurface = EGL_NO_SURFACE; EGLSurface oldDrawSurface = EGL_NO_SURFACE; @@ -75,15 +279,11 @@ private: EGLSurface surface = EGL_NO_SURFACE; EGLContext context = EGL_NO_CONTEXT; - EGLConfig config = nullptr; - EGLint format = -1; - std::string styleUrl; - std::string apiKey; - - bool firstTime = false; + float pixelRatio; bool fpsEnabled = false; bool snapshot = false; + bool firstRender = true; double fps = 0.0; int width = 0; @@ -96,12 +296,12 @@ private: size_t totalMemory = 0; // Ensure these are initialised last - std::unique_ptr<mbgl::DefaultFileSource> fileSource; - mbgl::ThreadPool threadPool; + std::shared_ptr<mbgl::ThreadPool> threadPool; std::unique_ptr<mbgl::Map> map; mbgl::EdgeInsets insets; unsigned active = 0; }; -} -} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/offline/offline_manager.cpp b/platform/android/src/offline/offline_manager.cpp new file mode 100644 index 0000000000..02871e7fdf --- /dev/null +++ b/platform/android/src/offline/offline_manager.cpp @@ -0,0 +1,164 @@ +#include "offline_manager.hpp" + +#include <mbgl/util/string.hpp> + +#include "../attach_env.hpp" +#include "../jni/generic_global_ref_deleter.hpp" + +namespace mbgl { +namespace android { + +// OfflineManager // + +OfflineManager::OfflineManager(jni::JNIEnv& env, jni::Object<FileSource> jFileSource) + : fileSource(mbgl::android::FileSource::getDefaultFileSource(env, jFileSource)) { +} + +OfflineManager::~OfflineManager() {} + +void OfflineManager::setOfflineMapboxTileCountLimit(jni::JNIEnv&, jni::jlong limit) { + fileSource.setOfflineMapboxTileCountLimit(limit); +} + +void OfflineManager::listOfflineRegions(jni::JNIEnv& env_, jni::Object<FileSource> jFileSource_, jni::Object<ListOfflineRegionsCallback> callback_) { + // list regions + fileSource.listOfflineRegions([ + //Keep a shared ptr to a global reference of the callback and file source so they are not GC'd in the meanwhile + callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()), + jFileSource = std::shared_ptr<jni::jobject>(jFileSource_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()) + ](std::exception_ptr error, mbgl::optional<std::vector<mbgl::OfflineRegion>> regions) mutable { + + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (error) { + OfflineManager::ListOfflineRegionsCallback::onError(*env, jni::Object<ListOfflineRegionsCallback>(*callback), error); + } else if (regions) { + OfflineManager::ListOfflineRegionsCallback::onList(*env, jni::Object<FileSource>(*jFileSource), jni::Object<ListOfflineRegionsCallback>(*callback), std::move(regions)); + } + }); +} + +void OfflineManager::createOfflineRegion(jni::JNIEnv& env_, + jni::Object<FileSource> jFileSource_, + jni::Object<OfflineRegionDefinition> definition_, + jni::Array<jni::jbyte> metadata_, + jni::Object<CreateOfflineRegionCallback> callback_) { + // Convert + + // XXX hardcoded cast for now as we only support OfflineTilePyramidRegionDefinition + auto definition = OfflineTilePyramidRegionDefinition::getDefinition(env_, jni::Object<OfflineTilePyramidRegionDefinition>(*definition_)); + + mbgl::OfflineRegionMetadata metadata; + if (metadata_) { + metadata = OfflineRegion::metadata(env_, metadata_); + } + + // Create region + fileSource.createOfflineRegion(definition, metadata, [ + //Keep a shared ptr to a global reference of the callback and file source so they are not GC'd in the meanwhile + callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()), + jFileSource = std::shared_ptr<jni::jobject>(jFileSource_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()) + ](std::exception_ptr error, mbgl::optional<mbgl::OfflineRegion> region) mutable { + + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (error) { + OfflineManager::CreateOfflineRegionCallback::onError(*env, jni::Object<CreateOfflineRegionCallback>(*callback), error); + } else if (region) { + OfflineManager::CreateOfflineRegionCallback::onCreate( + *env, + jni::Object<FileSource>(*jFileSource), + jni::Object<CreateOfflineRegionCallback>(*callback), std::move(region) + ); + } + }); +} + +jni::Class<OfflineManager> OfflineManager::javaClass; + +void OfflineManager::registerNative(jni::JNIEnv& env) { + OfflineManager::ListOfflineRegionsCallback::registerNative(env); + OfflineManager::CreateOfflineRegionCallback::registerNative(env); + + javaClass = *jni::Class<OfflineManager>::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + jni::RegisterNativePeer<OfflineManager>( env, javaClass, "nativePtr", + std::make_unique<OfflineManager, JNIEnv&, jni::Object<FileSource>>, + "initialize", + "finalize", + METHOD(&OfflineManager::setOfflineMapboxTileCountLimit, "setOfflineMapboxTileCountLimit"), + METHOD(&OfflineManager::listOfflineRegions, "listOfflineRegions"), + METHOD(&OfflineManager::createOfflineRegion, "createOfflineRegion")); +} + +// OfflineManager::ListOfflineRegionsCallback // + +void OfflineManager::ListOfflineRegionsCallback::onError(jni::JNIEnv& env, + jni::Object<OfflineManager::ListOfflineRegionsCallback> callback, + std::exception_ptr error) { + static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError"); + std::string message = mbgl::util::toString(error); + callback.Call(env, method, jni::Make<jni::String>(env, message)); +} + +void OfflineManager::ListOfflineRegionsCallback::onList(jni::JNIEnv& env, + jni::Object<FileSource> jFileSource, + jni::Object<OfflineManager::ListOfflineRegionsCallback> callback, + mbgl::optional<std::vector<mbgl::OfflineRegion>> regions) { + //Convert the regions to java peer objects + std::size_t index = 0; + auto jregions = jni::Array<jni::Object<OfflineRegion>>::New(env, regions->size(), OfflineRegion::javaClass); + for (auto& region : *regions) { + auto jregion = OfflineRegion::New(env, jFileSource, std::move(region)); + jregions.Set(env, index, jregion); + jni::DeleteLocalRef(env, jregion); + index++; + } + + // Trigger callback + static auto method = javaClass.GetMethod<void (jni::Array<jni::Object<OfflineRegion>>)>(env, "onList"); + callback.Call(env, method, jregions); + jni::DeleteLocalRef(env, jregions); +} + +jni::Class<OfflineManager::ListOfflineRegionsCallback> OfflineManager::ListOfflineRegionsCallback::javaClass; + +void OfflineManager::ListOfflineRegionsCallback::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineManager::ListOfflineRegionsCallback>::Find(env).NewGlobalRef(env).release(); +} + +// OfflineManager::CreateOfflineRegionCallback // + +void OfflineManager::CreateOfflineRegionCallback::onError(jni::JNIEnv& env, + jni::Object<OfflineManager::CreateOfflineRegionCallback> callback, + std::exception_ptr error) { + static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError"); + std::string message = mbgl::util::toString(error); + callback.Call(env, method, jni::Make<jni::String>(env, message)); +} + +void OfflineManager::CreateOfflineRegionCallback::onCreate(jni::JNIEnv& env, + jni::Object<FileSource> jFileSource, + jni::Object<OfflineManager::CreateOfflineRegionCallback> callback, + mbgl::optional<mbgl::OfflineRegion> region) { + //Convert the region to java peer object + auto jregion = OfflineRegion::New(env, jFileSource, std::move(*region)); + + // Trigger callback + static auto method = javaClass.GetMethod<void (jni::Object<OfflineRegion>)>(env, "onCreate"); + callback.Call(env, method, jregion); + jni::DeleteLocalRef(env, jregion); +} + +jni::Class<OfflineManager::CreateOfflineRegionCallback> OfflineManager::CreateOfflineRegionCallback::javaClass; + +void OfflineManager::CreateOfflineRegionCallback::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineManager::CreateOfflineRegionCallback>::Find(env).NewGlobalRef(env).release(); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/offline/offline_manager.hpp b/platform/android/src/offline/offline_manager.hpp new file mode 100644 index 0000000000..9ae2714ca2 --- /dev/null +++ b/platform/android/src/offline/offline_manager.hpp @@ -0,0 +1,75 @@ +#pragma once + + +#include <mbgl/storage/default_file_source.hpp> +#include <mbgl/storage/offline.hpp> +#include <jni/jni.hpp> + +#include "../file_source.hpp" +#include "offline_region.hpp" +#include "offline_region_definition.hpp" + + +namespace mbgl { +namespace android { + +class OfflineManager { +public: + + class ListOfflineRegionsCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$ListOfflineRegionsCallback";} + + static void onError(jni::JNIEnv&, jni::Object<OfflineManager::ListOfflineRegionsCallback>, std::exception_ptr); + + static void onList(jni::JNIEnv&, + jni::Object<FileSource>, + jni::Object<OfflineManager::ListOfflineRegionsCallback>, + mbgl::optional<std::vector<mbgl::OfflineRegion>>); + + static jni::Class<OfflineManager::ListOfflineRegionsCallback> javaClass; + + static void registerNative(jni::JNIEnv&); + }; + + class CreateOfflineRegionCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$CreateOfflineRegionCallback"; } + + static void onError(jni::JNIEnv&, jni::Object<OfflineManager::CreateOfflineRegionCallback>, std::exception_ptr); + + static void onCreate(jni::JNIEnv&, + jni::Object<FileSource>, + jni::Object<OfflineManager::CreateOfflineRegionCallback>, + mbgl::optional<mbgl::OfflineRegion>); + + static jni::Class<OfflineManager::CreateOfflineRegionCallback> javaClass; + + static void registerNative(jni::JNIEnv&); + }; + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager"; }; + + static jni::Class<OfflineManager> javaClass; + + static void registerNative(jni::JNIEnv&); + + OfflineManager(jni::JNIEnv&, jni::Object<FileSource>); + ~OfflineManager(); + + void setOfflineMapboxTileCountLimit(jni::JNIEnv&, jni::jlong limit); + + void listOfflineRegions(jni::JNIEnv&, jni::Object<FileSource>, jni::Object<ListOfflineRegionsCallback> callback); + + void createOfflineRegion(jni::JNIEnv&, + jni::Object<FileSource> jFileSource_, + jni::Object<OfflineRegionDefinition> definition, + jni::Array<jni::jbyte> metadata, + jni::Object<OfflineManager::CreateOfflineRegionCallback> callback); + +private: + mbgl::DefaultFileSource& fileSource; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/offline/offline_region.cpp b/platform/android/src/offline/offline_region.cpp new file mode 100644 index 0000000000..856434d266 --- /dev/null +++ b/platform/android/src/offline/offline_region.cpp @@ -0,0 +1,308 @@ +#include "offline_region.hpp" + +#include <mbgl/util/logging.hpp> +#include <mbgl/util/string.hpp> + +#include "offline_region_definition.hpp" +#include "offline_region_error.hpp" +#include "offline_region_status.hpp" +#include "../attach_env.hpp" +#include "../jni/generic_global_ref_deleter.hpp" + +namespace mbgl { +namespace android { + +// OfflineRegion // + +OfflineRegion::OfflineRegion(jni::JNIEnv& env, jni::jlong offlineRegionPtr, jni::Object<FileSource> jFileSource) + : region(reinterpret_cast<mbgl::OfflineRegion *>(offlineRegionPtr)), + fileSource(mbgl::android::FileSource::getDefaultFileSource(env, jFileSource)) {} + +OfflineRegion::~OfflineRegion() {} + +void OfflineRegion::setOfflineRegionObserver(jni::JNIEnv& env_, jni::Object<OfflineRegion::OfflineRegionObserver> callback) { + + // Define the observer + class Observer : public mbgl::OfflineRegionObserver { + public: + Observer(jni::UniqueObject<OfflineRegion::OfflineRegionObserver>&& callback_) + //TODO add a generic deleter for jni::Object + : callback(callback_.release()->Get()) { + } + + ~Observer() override { + android::UniqueEnv env = android::AttachEnv(); + env->DeleteGlobalRef(Unwrap(*callback)); + } + + void statusChanged(mbgl::OfflineRegionStatus status) override { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + // Status object + auto jStatus = OfflineRegionStatus::New(*env, status); + + // Call + static auto method = OfflineRegion::OfflineRegionObserver::javaClass + .GetMethod<void (jni::Object<OfflineRegionStatus>)>(*env, "onStatusChanged"); + callback.Call(*env, method, jStatus); + + // Delete references + jni::DeleteLocalRef(*env, jStatus); + } + + void responseError(mbgl::Response::Error error) override { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + // Error object + auto jError = OfflineRegionError::New(*env, error); + + // Call + static auto method = OfflineRegion::OfflineRegionObserver::javaClass + .GetMethod<void (jni::Object<mbgl::android::OfflineRegionError>)>(*env, "onError"); + callback.Call(*env, method, jError); + + // Delete references + jni::DeleteLocalRef(*env, jError); + } + + void mapboxTileCountLimitExceeded(uint64_t limit) override { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + // Send limit + static auto method = OfflineRegion::OfflineRegionObserver::javaClass + .GetMethod<void (jni::jlong)>(*env, "mapboxTileCountLimitExceeded"); + callback.Call(*env, method, jlong(limit)); + } + + jni::Object<OfflineRegion::OfflineRegionObserver> callback; + }; + + // Set the observer + fileSource.setOfflineRegionObserver(*region, std::make_unique<Observer>(callback.NewGlobalRef(env_))); +} + +void OfflineRegion::setOfflineRegionDownloadState(jni::JNIEnv&, jni::jint jState) { + // State + mbgl::OfflineRegionDownloadState state; + switch (jState) { + case 0: + state = mbgl::OfflineRegionDownloadState::Inactive; + break; + case 1: + state = mbgl::OfflineRegionDownloadState::Active; + break; + default: + mbgl::Log::Error(mbgl::Event::JNI, "State can only be 0 (inactive) or 1 (active)."); + return; + } + + fileSource.setOfflineRegionDownloadState(*region, state); +} + +void OfflineRegion::getOfflineRegionStatus(jni::JNIEnv& env_, jni::Object<OfflineRegionStatusCallback> callback_) { + + fileSource.getOfflineRegionStatus(*region, [ + //Ensure the object is not gc'd in the meanwhile + callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()) + ](std::exception_ptr error, mbgl::optional<mbgl::OfflineRegionStatus> status) mutable { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (error) { + OfflineRegionStatusCallback::onError(*env, jni::Object<OfflineRegionStatusCallback>(*callback), error); + } else if (status) { + OfflineRegionStatusCallback::onStatus(*env, jni::Object<OfflineRegionStatusCallback>(*callback), std::move(status)); + } + }); +} + +void OfflineRegion::deleteOfflineRegion(jni::JNIEnv& env_, jni::Object<OfflineRegionDeleteCallback> callback_) { + // Delete + fileSource.deleteOfflineRegion(std::move(*region), [ + //Ensure the object is not gc'd in the meanwhile + callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()) + ](std::exception_ptr error) mutable { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (error) { + OfflineRegionDeleteCallback::onError(*env, jni::Object<OfflineRegionDeleteCallback>(*callback), error); + } else { + OfflineRegionDeleteCallback::onDelete(*env, jni::Object<OfflineRegionDeleteCallback>(*callback)); + } + }); +} + +void OfflineRegion::updateOfflineRegionMetadata(jni::JNIEnv& env_, jni::Array<jni::jbyte> jMetadata, jni::Object<OfflineRegionUpdateMetadataCallback> callback_) { + + // Convert + auto metadata = OfflineRegion::metadata(env_, jMetadata); + + fileSource.updateOfflineMetadata(region->getID(), metadata, [ + //Ensure the object is not gc'd in the meanwhile + callback = std::shared_ptr<jni::jobject>(callback_.NewGlobalRef(env_).release()->Get(), GenericGlobalRefDeleter()) + ](std::exception_ptr error, mbgl::optional<mbgl::OfflineRegionMetadata> data) mutable { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (error) { + OfflineRegionUpdateMetadataCallback::onError(*env, jni::Object<OfflineRegionUpdateMetadataCallback>(*callback), error); + } else if (data) { + OfflineRegionUpdateMetadataCallback::onUpdate(*env, jni::Object<OfflineRegionUpdateMetadataCallback>(*callback), std::move(data)); + } + }); +} + +jni::Object<OfflineRegion> OfflineRegion::New(jni::JNIEnv& env, jni::Object<FileSource> jFileSource, mbgl::OfflineRegion region) { + + // Definition + auto definition = jni::Object<OfflineRegionDefinition>(*OfflineTilePyramidRegionDefinition::New(env, region.getDefinition())); + + // Metadata + auto metadata = OfflineRegion::metadata(env, region.getMetadata()); + + // Create region java object + static auto constructor = OfflineRegion::javaClass.GetConstructor<jni::jlong, jni::Object<FileSource>, jni::jlong, jni::Object<OfflineRegionDefinition>, jni::Array<jni::jbyte>>(env); + auto jregion = OfflineRegion::javaClass.New(env, constructor, + reinterpret_cast<jni::jlong>(new mbgl::OfflineRegion(std::move(region))), //Copy a region to the heap + jFileSource, jni::jlong(region.getID()), definition, metadata); + + //Delete references + jni::DeleteLocalRef(env, definition); + jni::DeleteLocalRef(env, metadata); + + return jregion; +} + +jni::Array<jni::jbyte> OfflineRegion::metadata(jni::JNIEnv& env, mbgl::OfflineRegionMetadata metadata_) { + std::vector<jni::jbyte> convertedMetadata(metadata_.begin(), metadata_.end()); + std::size_t length = static_cast<std::size_t>(convertedMetadata.size()); + auto metadata = jni::Array<jni::jbyte>::New(env, length); + metadata.SetRegion<std::vector<jni::jbyte>>(env, 0, convertedMetadata); + return metadata; +} + +mbgl::OfflineRegionMetadata OfflineRegion::metadata(jni::JNIEnv& env, jni::Array<jni::jbyte> metadata_) { + std::size_t length = metadata_.Length(env); + auto metadata_tmp = std::vector<jni::jbyte>(); + metadata_tmp.resize(length); + metadata_.GetRegion<std::vector<jni::jbyte>>(env, 0, metadata_tmp); + auto metadata = std::vector<uint8_t>(metadata_tmp.begin(), metadata_tmp.end()); + return metadata; +} + +jni::Class<OfflineRegion> OfflineRegion::javaClass; + +void OfflineRegion::registerNative(jni::JNIEnv& env) { + OfflineRegion::OfflineRegionObserver::registerNative(env); + OfflineRegion::OfflineRegionStatusCallback::registerNative(env); + OfflineRegion::OfflineRegionDeleteCallback::registerNative(env); + OfflineRegion::OfflineRegionUpdateMetadataCallback::registerNative(env); + + javaClass = *jni::Class<OfflineRegion>::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + jni::RegisterNativePeer<OfflineRegion>( env, javaClass, "nativePtr", + std::make_unique<OfflineRegion, JNIEnv&, jni::jlong, jni::Object<FileSource>>, + "initialize", + "finalize", + METHOD(&OfflineRegion::setOfflineRegionObserver, "setOfflineRegionObserver"), + METHOD(&OfflineRegion::setOfflineRegionDownloadState, "setOfflineRegionDownloadState"), + METHOD(&OfflineRegion::getOfflineRegionStatus, "getOfflineRegionStatus"), + METHOD(&OfflineRegion::deleteOfflineRegion, "deleteOfflineRegion"), + METHOD(&OfflineRegion::updateOfflineRegionMetadata, "updateOfflineRegionMetadata") + ); +} + +// OfflineRegionObserver // + +jni::Class<OfflineRegion::OfflineRegionObserver> OfflineRegion::OfflineRegionObserver::javaClass; + +void OfflineRegion::OfflineRegionObserver::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineRegion::OfflineRegionObserver>::Find(env).NewGlobalRef(env).release(); +} + +// OfflineRegionStatusCallback // + +jni::Class<OfflineRegion::OfflineRegionStatusCallback> OfflineRegion::OfflineRegionStatusCallback::javaClass; + +void OfflineRegion::OfflineRegionStatusCallback::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineRegionStatusCallback>::Find(env).NewGlobalRef(env).release(); +} + +void OfflineRegion::OfflineRegionStatusCallback::onError(jni::JNIEnv& env, + jni::Object<OfflineRegion::OfflineRegionStatusCallback> callback, + std::exception_ptr error) { + static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError"); + std::string message = mbgl::util::toString(error); + callback.Call(env, method, jni::Make<jni::String>(env, message)); +} + +void OfflineRegion::OfflineRegionStatusCallback::onStatus(jni::JNIEnv& env, + jni::Object<OfflineRegion::OfflineRegionStatusCallback> callback, + mbgl::optional<mbgl::OfflineRegionStatus> status) { + //Convert to java peer object + auto jStatus = OfflineRegionStatus::New(env, std::move(*status)); + + // Trigger callback + static auto method = javaClass.GetMethod<void (jni::Object<OfflineRegionStatus>)>(env, "onStatus"); + callback.Call(env, method, jStatus); + jni::DeleteLocalRef(env, jStatus); +} + +// OfflineRegionDeleteCallback // + +jni::Class<OfflineRegion::OfflineRegionDeleteCallback> OfflineRegion::OfflineRegionDeleteCallback::javaClass; + +void OfflineRegion::OfflineRegionDeleteCallback::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineRegionDeleteCallback>::Find(env).NewGlobalRef(env).release(); +} + +void OfflineRegion::OfflineRegionDeleteCallback::onError(jni::JNIEnv& env, + jni::Object<OfflineRegion::OfflineRegionDeleteCallback> callback, + std::exception_ptr error) { + static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError"); + std::string message = mbgl::util::toString(error); + callback.Call(env, method, jni::Make<jni::String>(env, message)); +} + +void OfflineRegion::OfflineRegionDeleteCallback::onDelete(jni::JNIEnv& env, jni::Object<OfflineRegion::OfflineRegionDeleteCallback> callback) { + // Trigger callback + static auto method = javaClass.GetMethod<void ()>(env, "onDelete"); + callback.Call(env, method); +} + +// OfflineRegionUpdateMetadataCallback // + +jni::Class<OfflineRegion::OfflineRegionUpdateMetadataCallback> OfflineRegion::OfflineRegionUpdateMetadataCallback::javaClass; + +void OfflineRegion::OfflineRegionUpdateMetadataCallback::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineRegionUpdateMetadataCallback>::Find(env).NewGlobalRef(env).release(); +} + +void OfflineRegion::OfflineRegionUpdateMetadataCallback::onError(jni::JNIEnv& env, + jni::Object<OfflineRegion::OfflineRegionUpdateMetadataCallback> callback, + std::exception_ptr error) { + static auto method = javaClass.GetMethod<void (jni::String)>(env, "onError"); + std::string message = mbgl::util::toString(error); + callback.Call(env, method, jni::Make<jni::String>(env, message)); +} + +void OfflineRegion::OfflineRegionUpdateMetadataCallback::onUpdate(jni::JNIEnv& env, + jni::Object<OfflineRegion::OfflineRegionUpdateMetadataCallback> callback, + mbgl::optional<mbgl::OfflineRegionMetadata> metadata) { + //Convert to java peer object + auto jMetadata = OfflineRegion::metadata(env, std::move(*metadata)); + + // Trigger callback + static auto method = javaClass.GetMethod<void (jni::Array<jni::jbyte>)>(env, "onUpdate"); + callback.Call(env, method, jMetadata); + jni::DeleteLocalRef(env, jMetadata); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/offline/offline_region.hpp b/platform/android/src/offline/offline_region.hpp new file mode 100644 index 0000000000..c05383a91a --- /dev/null +++ b/platform/android/src/offline/offline_region.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include <mbgl/storage/offline.hpp> +#include <jni/jni.hpp> + +#include "../file_source.hpp" + +#include <memory> + +namespace mbgl { +namespace android { + +class OfflineRegion { +public: + class OfflineRegionObserver { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionObserver"; }; + + static jni::Class<OfflineRegionObserver> javaClass; + + static void registerNative(jni::JNIEnv&); + }; + + class OfflineRegionStatusCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionStatusCallback"; }; + + static void onError(jni::JNIEnv&, jni::Object<OfflineRegionStatusCallback>, std::exception_ptr); + + static void onStatus(jni::JNIEnv&, + jni::Object<OfflineRegionStatusCallback>, + mbgl::optional<mbgl::OfflineRegionStatus>); + + static jni::Class<OfflineRegionStatusCallback> javaClass; + + static void registerNative(jni::JNIEnv&); + }; + + class OfflineRegionDeleteCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionDeleteCallback"; }; + + static void onError(jni::JNIEnv&, jni::Object<OfflineRegionDeleteCallback>, std::exception_ptr); + + static void onDelete(jni::JNIEnv&, jni::Object<OfflineRegionDeleteCallback>); + + static jni::Class<OfflineRegionDeleteCallback> javaClass; + + static void registerNative(jni::JNIEnv&); + }; + + class OfflineRegionUpdateMetadataCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionUpdateMetadataCallback"; }; + + static void onError(jni::JNIEnv&, jni::Object<OfflineRegionUpdateMetadataCallback>, std::exception_ptr); + + static void onUpdate(jni::JNIEnv&, + jni::Object<OfflineRegionUpdateMetadataCallback>, + mbgl::optional<mbgl::OfflineRegionMetadata>); + + static jni::Class<OfflineRegionUpdateMetadataCallback> javaClass; + + static void registerNative(jni::JNIEnv&); + }; + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion"; }; + + OfflineRegion(jni::JNIEnv&, jni::jlong, jni::Object<FileSource>); + + ~OfflineRegion(); + + void setOfflineRegionObserver(jni::JNIEnv&, jni::Object<OfflineRegion::OfflineRegionObserver>); + + void setOfflineRegionDownloadState(jni::JNIEnv&, jni::jint); + + void getOfflineRegionStatus(jni::JNIEnv&, jni::Object<OfflineRegion::OfflineRegionStatusCallback>); + + void deleteOfflineRegion(jni::JNIEnv&, jni::Object<OfflineRegionDeleteCallback>); + + void updateOfflineRegionMetadata(jni::JNIEnv&, jni::Array<jni::jbyte>, jni::Object<OfflineRegionUpdateMetadataCallback>); + + static jni::Object<OfflineRegion> New(jni::JNIEnv&, jni::Object<FileSource>, mbgl::OfflineRegion); + + static jni::Array<jni::jbyte> metadata(jni::JNIEnv&, mbgl::OfflineRegionMetadata); + + static mbgl::OfflineRegionMetadata metadata(jni::JNIEnv&, jni::Array<jni::jbyte>); + + static jni::Class<OfflineRegion> javaClass; + + static void registerNative(jni::JNIEnv&); + +private: + std::unique_ptr<mbgl::OfflineRegion> region; + mbgl::DefaultFileSource& fileSource; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/offline/offline_region_definition.cpp b/platform/android/src/offline/offline_region_definition.cpp new file mode 100644 index 0000000000..66a9bdf99d --- /dev/null +++ b/platform/android/src/offline/offline_region_definition.cpp @@ -0,0 +1,69 @@ +#include "offline_region_definition.hpp" + +#include "../geometry/lat_lng_bounds.hpp" + +namespace mbgl { +namespace android { + +// OfflineRegionDefinition // + +jni::Class<OfflineRegionDefinition> OfflineRegionDefinition::javaClass; + +void OfflineRegionDefinition::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineRegionDefinition>::Find(env).NewGlobalRef(env).release(); +} + +// OfflineTilePyramidRegionDefinition // + +jni::Object<OfflineTilePyramidRegionDefinition> OfflineTilePyramidRegionDefinition::New(jni::JNIEnv& env, mbgl::OfflineTilePyramidRegionDefinition definition) { + + //Convert objects + auto styleURL = jni::Make<jni::String>(env, definition.styleURL); + auto bounds = LatLngBounds::New(env, definition.bounds); + + static auto constructor = javaClass.GetConstructor<jni::String, jni::Object<LatLngBounds>, jni::jdouble, jni::jdouble, jni::jfloat>(env); + auto jdefinition = javaClass.New(env, constructor, styleURL, bounds, definition.minZoom, definition.maxZoom, definition.pixelRatio); + + //Delete References + jni::DeleteLocalRef(env, styleURL); + jni::DeleteLocalRef(env, bounds); + + return jdefinition; +} + +mbgl::OfflineTilePyramidRegionDefinition OfflineTilePyramidRegionDefinition::getDefinition(jni::JNIEnv& env, jni::Object<OfflineTilePyramidRegionDefinition> jDefinition) { + // Field references + static auto styleURLF = javaClass.GetField<jni::String>(env, "styleURL"); + static auto boundsF = javaClass.GetField<jni::Object<LatLngBounds>>(env, "bounds"); + static auto minZoomF = javaClass.GetField<jni::jdouble>(env, "minZoom"); + static auto maxZoomF = javaClass.GetField<jni::jdouble>(env, "maxZoom"); + static auto pixelRatioF = javaClass.GetField<jni::jfloat>(env, "pixelRatio"); + + // Get objects + auto jStyleURL = jDefinition.Get(env, styleURLF); + auto jBounds = jDefinition.Get(env, boundsF); + + // Create definition + mbgl::OfflineTilePyramidRegionDefinition definition( + jni::Make<std::string>(env, jStyleURL), + LatLngBounds::getLatLngBounds(env, jBounds), + jDefinition.Get(env, minZoomF), + jDefinition.Get(env, maxZoomF), + jDefinition.Get(env, pixelRatioF) + ); + + // Delete references + jni::DeleteLocalRef(env, jStyleURL); + jni::DeleteLocalRef(env, jBounds); + + return definition; +} + +jni::Class<OfflineTilePyramidRegionDefinition> OfflineTilePyramidRegionDefinition::javaClass; + +void OfflineTilePyramidRegionDefinition::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineTilePyramidRegionDefinition>::Find(env).NewGlobalRef(env).release(); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/offline/offline_region_definition.hpp b/platform/android/src/offline/offline_region_definition.hpp new file mode 100644 index 0000000000..2ca82a4d96 --- /dev/null +++ b/platform/android/src/offline/offline_region_definition.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include <mbgl/storage/offline.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class OfflineRegionDefinition { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegionDefinition"; }; + + static jni::Class<OfflineRegionDefinition> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + +class OfflineTilePyramidRegionDefinition: public OfflineRegionDefinition { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition"; }; + + static jni::Object<OfflineTilePyramidRegionDefinition> New(jni::JNIEnv&, mbgl::OfflineTilePyramidRegionDefinition); + + static mbgl::OfflineTilePyramidRegionDefinition getDefinition(jni::JNIEnv&, jni::Object<OfflineTilePyramidRegionDefinition>); + + static jni::Class<OfflineTilePyramidRegionDefinition> javaClass; + + static void registerNative(jni::JNIEnv&); + +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/offline/offline_region_error.cpp b/platform/android/src/offline/offline_region_error.cpp new file mode 100644 index 0000000000..b0a19f934f --- /dev/null +++ b/platform/android/src/offline/offline_region_error.cpp @@ -0,0 +1,53 @@ +#include "offline_region_error.hpp" + +namespace mbgl { +namespace android { + +jni::Object<OfflineRegionError> OfflineRegionError::New(jni::JNIEnv& env, mbgl::Response::Error error) { + + // Handle the value of reason independently of the underlying int value + std::string reason; + switch(error.reason) { + case mbgl::Response::Error::Reason::Success: + reason = "REASON_SUCCESS"; + break; + case mbgl::Response::Error::Reason::NotFound: + reason = "REASON_NOT_FOUND"; + break; + case mbgl::Response::Error::Reason::Server: + reason = "REASON_SERVER"; + break; + case mbgl::Response::Error::Reason::Connection: + reason = "REASON_CONNECTION"; + break; + case mbgl::Response::Error::Reason::RateLimit: + reason = "REASON_RATE_LIMIT"; + break; + case mbgl::Response::Error::Reason::Other: + reason = "REASON_OTHER"; + break; + } + + // Convert + auto jReason = jni::Make<jni::String>(env, reason); + auto jMessage = jni::Make<jni::String>(env, error.message); + + // Create java object + static auto constructor = javaClass.GetConstructor<jni::String, jni::String>(env); + auto jError = javaClass.New(env, constructor, jReason, jMessage); + + // Delete references + jni::DeleteLocalRef(env, jReason); + jni::DeleteLocalRef(env, jMessage); + + return jError; +} + +jni::Class<OfflineRegionError> OfflineRegionError::javaClass; + +void OfflineRegionError::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineRegionError>::Find(env).NewGlobalRef(env).release(); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/offline/offline_region_error.hpp b/platform/android/src/offline/offline_region_error.hpp new file mode 100644 index 0000000000..61efaca67e --- /dev/null +++ b/platform/android/src/offline/offline_region_error.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include <mbgl/storage/offline.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class OfflineRegionError { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegionError"; }; + + static jni::Object<OfflineRegionError> New(jni::JNIEnv&, mbgl::Response::Error); + + static jni::Class<OfflineRegionError> javaClass; + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/offline/offline_region_status.cpp b/platform/android/src/offline/offline_region_status.cpp new file mode 100644 index 0000000000..d0bbae124f --- /dev/null +++ b/platform/android/src/offline/offline_region_status.cpp @@ -0,0 +1,39 @@ +#include "offline_region_status.hpp" + +namespace mbgl { +namespace android { + +jni::Object<OfflineRegionStatus> OfflineRegionStatus::New(jni::JNIEnv& env, mbgl::OfflineRegionStatus status) { + + // Convert to jint + jint downloadState; + switch(status.downloadState) { + case mbgl::OfflineRegionDownloadState::Inactive: + downloadState = 0; + break; + case mbgl::OfflineRegionDownloadState::Active: + downloadState = 1; + break; + } + + // Create java object + static auto constructor = javaClass.GetConstructor<jint, jlong, jlong, jlong, jlong, jlong, jboolean>(env); + return javaClass.New(env, constructor, + downloadState, + jlong(status.completedResourceCount), + jlong(status.completedResourceSize), + jlong(status.completedTileCount), + jlong(status.completedTileSize), + jlong(status.requiredResourceCount), + jboolean(status.requiredResourceCountIsPrecise) + ); +} + +jni::Class<OfflineRegionStatus> OfflineRegionStatus::javaClass; + +void OfflineRegionStatus::registerNative(jni::JNIEnv& env) { + javaClass = *jni::Class<OfflineRegionStatus>::Find(env).NewGlobalRef(env).release(); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/offline/offline_region_status.hpp b/platform/android/src/offline/offline_region_status.hpp new file mode 100644 index 0000000000..b29a653655 --- /dev/null +++ b/platform/android/src/offline/offline_region_status.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include <mbgl/storage/offline.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class OfflineRegionStatus { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegionStatus"; }; + + static jni::Object<OfflineRegionStatus> New(jni::JNIEnv&, mbgl::OfflineRegionStatus status); + + static jni::Class<OfflineRegionStatus> javaClass; + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/android_conversion.hpp b/platform/android/src/style/android_conversion.hpp index de0ac91502..d9b88ab52b 100644 --- a/platform/android/src/style/android_conversion.hpp +++ b/platform/android/src/style/android_conversion.hpp @@ -4,7 +4,6 @@ #include <mbgl/util/logging.hpp> #include <mbgl/style/conversion.hpp> -#include <mbgl/util/feature.hpp> #include <mbgl/util/optional.hpp> #include <jni/jni.hpp> @@ -45,7 +44,7 @@ inline optional<mbgl::android::Value> objectMember(const mbgl::android::Value& v template <class Fn> optional<Error> eachMember(const mbgl::android::Value&, Fn&&) { - //TODO + // TODO mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented"); return {}; } @@ -82,7 +81,7 @@ inline optional<Value> toValue(const mbgl::android::Value& value) { } else if (value.isString()) { return { value.toString() }; } else if (value.isNumber()) { - //Need to cast to a double here as the float is otherwise considered a bool... + // Need to cast to a double here as the float is otherwise considered a bool... return { (double) value.toNumber() }; } else { return {}; diff --git a/platform/android/src/style/conversion/function.hpp b/platform/android/src/style/conversion/function.hpp index 26dd5c21fd..ad01a7afc2 100644 --- a/platform/android/src/style/conversion/function.hpp +++ b/platform/android/src/style/conversion/function.hpp @@ -4,51 +4,224 @@ #include "../../conversion/conversion.hpp" #include "../../conversion/constant.hpp" #include "types.hpp" -#include "function.hpp" +#include "../../java/lang.hpp" +#include "../functions/stop.hpp" +#include "../functions/categorical_stops.hpp" +#include "../functions/exponential_stops.hpp" +#include "../functions/identity_stops.hpp" +#include "../functions/interval_stops.hpp" #include <jni/jni.hpp> #include <tuple> -#include <vector> +#include <map> namespace mbgl { namespace android { namespace conversion { -template <class T> -inline jni::jobject* toFunctionStopJavaArray(jni::JNIEnv& env, std::vector<std::pair<float, T>> value) { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/Function$Stop")).release(); +/** + * Conversion from core composite value to java type + */ +class CategoricalValueEvaluator { +public: + + CategoricalValueEvaluator(jni::JNIEnv& _env) : env(_env) {} + + template <class T> + jni::jobject* operator()(const T &value) const { + return *convert<jni::jobject*, T>(env, value); + } + +private: + jni::JNIEnv& env; +}; + +/** + * Conversion from core composite value to java type + */ +template <> +struct Converter<jni::jobject*, mbgl::style::CategoricalValue> { + + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::CategoricalValue& value) const { + CategoricalValueEvaluator evaluator(env); + return apply_visitor(evaluator, value); + } +}; + +template <class I, class O> +jni::Array<jni::Object<Stop>> toFunctionStopJavaArray(jni::JNIEnv& env, std::map<I, O> value) { + + auto jarray = jni::Array<jni::Object<Stop>>::New(env, value.size(), Stop::javaClass); + + size_t i = 0; + for (auto const& stop : value) { + jni::jobject* in = *convert<jni::jobject*, I>(env, stop.first); + jni::jobject* out = *convert<jni::jobject*, O>(env, stop.second); + + auto jstop = Stop::New(env, jni::Object<>(in), jni::Object<>(out)); + jarray.Set(env, i, jstop); + + jni::DeleteLocalRef(env, in); + jni::DeleteLocalRef(env, out); + jni::DeleteLocalRef(env, jstop); + + i++; + } + + return jarray; +} + +template <class I, class O> +jni::Array<jni::Object<Stop>> toFunctionStopJavaArray(jni::JNIEnv& env, std::map<float, std::map<I, O>> value) { + + auto jarray = jni::Array<jni::Object<Stop>>::New(env, value.size(), Stop::javaClass); + + for (auto const& zoomLevelMap : value) { + size_t i = 0; + for (auto const& stop: zoomLevelMap.second) { + auto zoom = jni::Object<java::lang::Number>(*convert<jni::jobject*>(env, zoomLevelMap.first)); + auto in = jni::Object<>(*convert<jni::jobject*, I>(env, stop.first)); + auto out = jni::Object<>(*convert<jni::jobject*, O>(env, stop.second)); + auto compositeValue = Stop::CompositeValue::New(env, zoom, in); + + auto jstop = Stop::New(env, compositeValue, out); + jarray.Set(env, i, jstop); + + jni::DeleteLocalRef(env, zoom); + jni::DeleteLocalRef(env, in); + jni::DeleteLocalRef(env, out); + jni::DeleteLocalRef(env, compositeValue); + jni::DeleteLocalRef(env, jstop); + + i++; + } + } + + return jarray; +} + +template <class I, typename O> +inline jni::jobject* convertCompositeStopsArray(jni::JNIEnv& env, std::map<float, std::map<I, O>> value) { + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/functions/stops/Stop")).release(); static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V"); jni::jarray<jni::jobject>& jarray = jni::NewObjectArray(env, value.size(), *javaClass); - for(size_t i = 0; i < value.size(); i = i + 1) { - jni::jobject* in = *convert<jni::jobject*, float>(env, value[i].first); - jni::jobject* out = *convert<jni::jobject*, T>(env, value[i].second); + size_t i = 0; + for (auto const& stop : value) { + jni::jobject* in = *convert<jni::jobject*, I>(env, stop.first); + jni::jobject* out = *convert<jni::jobject*, O>(env, stop.second); jni::SetObjectArrayElement(env, jarray, i, &jni::NewObject(env, *javaClass, *constructor, in, out)); + i++; + jni::DeleteLocalRef(env, in); + jni::DeleteLocalRef(env, out); } return &jarray; } +/** + * Conversion from core function stops to Stops java subclasses + */ +template <class T> +class StopsEvaluator { +public: + + StopsEvaluator(jni::JNIEnv& _env) : env(_env) {} + + jni::jobject* operator()(const mbgl::style::CategoricalStops<T> &value) const { + return CategoricalStops::New(env, toFunctionStopJavaArray(env, value.stops)).Get(); + } + + jni::jobject* operator()(const mbgl::style::CompositeCategoricalStops<T> &value) const { + return CategoricalStops::New(env, toFunctionStopJavaArray(env, value.stops)).Get(); + } + + jni::jobject* operator()(const mbgl::style::ExponentialStops<T> &value) const { + return ExponentialStops::New(env, jni::Object<java::lang::Float>(*convert<jni::jobject*>(env, value.base)), toFunctionStopJavaArray(env, value.stops)).Get(); + } + + jni::jobject* operator()(const mbgl::style::CompositeExponentialStops<T> &value) const { + return ExponentialStops::New(env, jni::Object<java::lang::Float>(*convert<jni::jobject*>(env, value.base)), toFunctionStopJavaArray(env, value.stops)).Get(); + } + + jni::jobject* operator()(const mbgl::style::IdentityStops<T> &) const { + return IdentityStops::New(env).Get(); + } + + jni::jobject* operator()(const mbgl::style::IntervalStops<T> &value) const { + return IntervalStops::New(env, toFunctionStopJavaArray(env, value.stops)).Get(); + } + + jni::jobject* operator()(const mbgl::style::CompositeIntervalStops<T> &value) const { + return IntervalStops::New(env, toFunctionStopJavaArray(env, value.stops)).Get(); + } + +private: + jni::JNIEnv& env; +}; + template <class T> -struct Converter<jni::jobject*, mbgl::style::Function<T>> { +struct Converter<jni::jobject*, mbgl::style::CameraFunction<T>> { + + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::CameraFunction<T>& value) const { + static jni::jclass* clazz = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/functions/CameraFunction")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *clazz, "<init>", "(Lcom/mapbox/mapboxsdk/style/functions/stops/Stops;)V"); + + StopsEvaluator<T> evaluator(env); + jni::jobject* stops = apply_visitor(evaluator, value.stops); + jni::jobject* converted = &jni::NewObject(env, *clazz, *constructor, stops); + + return { converted }; + } +}; + +template <class T> +struct Converter<jni::jobject*, mbgl::style::SourceFunction<T>> { + + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::SourceFunction<T>& value) const { + static jni::jclass* clazz = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/functions/SourceFunction")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *clazz, "<init>", + "(Ljava/lang/Object;Ljava/lang/String;Lcom/mapbox/mapboxsdk/style/functions/stops/Stops;)V"); + + // Convert stops + StopsEvaluator<T> evaluator(env); + jni::jobject* stops = apply_visitor(evaluator, value.stops); + + // Convert default value + jni::jobject* defaultValue = nullptr; + if (value.defaultValue) { + defaultValue = *convert<jni::jobject*>(env, *value.defaultValue); + } + + return { &jni::NewObject(env, *clazz, *constructor, defaultValue, jni::Make<jni::String>(env, value.property).Get(), stops) }; + } +}; + +template <class T> +struct Converter<jni::jobject*, mbgl::style::CompositeFunction<T>> { + + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::CompositeFunction<T>& value) const { + static jni::jclass* clazz = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/functions/CompositeFunction")).release(); + static jni::jmethodID* constructor = &jni::GetMethodID(env, *clazz, "<init>", + "(Ljava/lang/Object;Ljava/lang/String;Lcom/mapbox/mapboxsdk/style/functions/stops/Stops;)V"); - Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::Function<T>& value) const { - static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/Function")).release(); - static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "([Lcom/mapbox/mapboxsdk/style/layers/Function$Stop;)V"); - static jni::jmethodID* withBase = &jni::GetMethodID(env, *javaClass, "withBase", "(F)Lcom/mapbox/mapboxsdk/style/layers/Function;"); + // Convert stops + StopsEvaluator<T> evaluator(env); + jni::jobject* stops = apply_visitor(evaluator, value.stops); - //Create object - jni::jobject* jfunction = &jni::NewObject(env, *javaClass, *constructor, *toFunctionStopJavaArray(env, value.getStops())); - //Set base - jni::CallMethod<jni::jobject*>(env, jfunction, *withBase, value.getBase()); + // Convert default value + jni::jobject* defaultValue = nullptr; + if (value.defaultValue) { + defaultValue = *convert<jni::jobject*>(env, *value.defaultValue); + } - return {jfunction}; + return { &jni::NewObject(env, *clazz, *constructor, defaultValue, jni::Make<jni::String>(env, value.property).Get(), stops) }; } }; } // namespace conversion } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/style/conversion/geojson.hpp b/platform/android/src/style/conversion/geojson.hpp index 6bc48b3700..415d96f467 100644 --- a/platform/android/src/style/conversion/geojson.hpp +++ b/platform/android/src/style/conversion/geojson.hpp @@ -19,7 +19,7 @@ namespace conversion { template <> Result<GeoJSON> convertGeoJSON(const mbgl::android::Value& value) { - //Value should be a string wrapped in an object + // Value should be a string wrapped in an object mbgl::android::Value jsonValue = value.get("data"); if(value.isNull()) { return Error { "no json data found" }; @@ -54,4 +54,4 @@ struct Converter<GeoJSON> { } // namespace conversion } // namespace style -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/style/conversion/property_value.hpp b/platform/android/src/style/conversion/property_value.hpp index 4121192f3f..a58cf975a7 100644 --- a/platform/android/src/style/conversion/property_value.hpp +++ b/platform/android/src/style/conversion/property_value.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/style/property_value.hpp> +#include <mbgl/style/data_driven_property_value.hpp> #include "../../conversion/conversion.hpp" #include "../../conversion/constant.hpp" #include "types.hpp" @@ -10,29 +11,65 @@ namespace mbgl { namespace android { namespace conversion { +/** + * Conversion from core property value types to Java property value types + */ +template <typename T> +class PropertyValueEvaluator { +public: + + PropertyValueEvaluator(jni::JNIEnv& _env) : env(_env) {} + + jni::jobject* operator()(const mbgl::style::Undefined) const { + return nullptr; + } + + jni::jobject* operator()(const T &value) const { + Result<jni::jobject*> result = convert<jni::jobject*>(env, value); + return *result; + } + + jni::jobject* operator()(const mbgl::style::CameraFunction<T> &value) const { + return *convert<jni::jobject*, mbgl::style::CameraFunction<T>>(env, value); + } + + jni::jobject* operator()(const mbgl::style::SourceFunction<T> &value) const { + return *convert<jni::jobject*, mbgl::style::SourceFunction<T>>(env, value); + } + + jni::jobject* operator()(const mbgl::style::CompositeFunction<T> &value) const { + return *convert<jni::jobject*, mbgl::style::CompositeFunction<T>>(env, value); + } + +private: + jni::JNIEnv& env; + +}; + +/** + * Convert core property values to java + */ template <class T> struct Converter<jni::jobject*, mbgl::style::PropertyValue<T>> { Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::PropertyValue<T>& value) const { + PropertyValueEvaluator<T> evaluator(env); + return value.evaluate(evaluator); + } +}; - if(value.isUndefined()) { - //Return a nullptr representing a Java null value - return {nullptr}; - } else if (value.isConstant()) { - //Time to convert the constant value - Result<jni::jobject*> result = convert<jni::jobject*, T>(env, value.asConstant()); - return {*result}; - //return converted; - } else if (value.isFunction()) { - //Must be a function than - return convert<jni::jobject*, mbgl::style::Function<T>>(env, value.asFunction()); - } else { - throw std::runtime_error("Unknown property value type"); - } +/** + * Convert core data driven property values to java + */ +template <class T> +struct Converter<jni::jobject*, mbgl::style::DataDrivenPropertyValue<T>> { + Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::DataDrivenPropertyValue<T>& value) const { + PropertyValueEvaluator<T> evaluator(env); + return value.evaluate(evaluator); } }; } // namespace conversion } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/style/conversion/types.hpp b/platform/android/src/style/conversion/types.hpp index 1c433bb264..d9921e582e 100644 --- a/platform/android/src/style/conversion/types.hpp +++ b/platform/android/src/style/conversion/types.hpp @@ -95,4 +95,4 @@ struct Converter<jni::jobject*, mbgl::style::CirclePitchScaleType> { } // namespace conversion } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/style/conversion/types.hpp.ejs b/platform/android/src/style/conversion/types.hpp.ejs index d248d42b72..3cd4764015 100644 --- a/platform/android/src/style/conversion/types.hpp.ejs +++ b/platform/android/src/style/conversion/types.hpp.ejs @@ -37,4 +37,4 @@ struct Converter<jni::jobject*, mbgl::style::<%- propertyNativeType(property) %> } // namespace conversion } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/style/conversion/types_string_values.hpp b/platform/android/src/style/conversion/types_string_values.hpp index 9f21a2fed9..e3108fdf5b 100644 --- a/platform/android/src/style/conversion/types_string_values.hpp +++ b/platform/android/src/style/conversion/types_string_values.hpp @@ -10,7 +10,7 @@ namespace mbgl { namespace android { namespace conversion { - //visibility + // visibility inline std::string toString(mbgl::style::VisibilityType value) { switch (value) { case mbgl::style::VisibilityType::Visible: @@ -24,7 +24,7 @@ namespace conversion { } } - //line-cap + // line-cap inline std::string toString(mbgl::style::LineCapType value) { switch (value) { case mbgl::style::LineCapType::Butt: @@ -41,7 +41,7 @@ namespace conversion { } } - //line-join + // line-join inline std::string toString(mbgl::style::LineJoinType value) { switch (value) { case mbgl::style::LineJoinType::Bevel: @@ -58,7 +58,7 @@ namespace conversion { } } - //symbol-placement + // symbol-placement inline std::string toString(mbgl::style::SymbolPlacementType value) { switch (value) { case mbgl::style::SymbolPlacementType::Point: @@ -72,7 +72,7 @@ namespace conversion { } } - //icon-rotation-alignment + // icon-rotation-alignment inline std::string toString(mbgl::style::AlignmentType value) { switch (value) { case mbgl::style::AlignmentType::Map: @@ -89,7 +89,7 @@ namespace conversion { } } - //icon-text-fit + // icon-text-fit inline std::string toString(mbgl::style::IconTextFitType value) { switch (value) { case mbgl::style::IconTextFitType::None: @@ -109,7 +109,7 @@ namespace conversion { } } - //text-justify + // text-justify inline std::string toString(mbgl::style::TextJustifyType value) { switch (value) { case mbgl::style::TextJustifyType::Left: @@ -126,7 +126,7 @@ namespace conversion { } } - //text-anchor + // text-anchor inline std::string toString(mbgl::style::TextAnchorType value) { switch (value) { case mbgl::style::TextAnchorType::Center: @@ -161,7 +161,7 @@ namespace conversion { } } - //text-transform + // text-transform inline std::string toString(mbgl::style::TextTransformType value) { switch (value) { case mbgl::style::TextTransformType::None: @@ -178,7 +178,7 @@ namespace conversion { } } - //fill-translate-anchor + // fill-translate-anchor inline std::string toString(mbgl::style::TranslateAnchorType value) { switch (value) { case mbgl::style::TranslateAnchorType::Map: @@ -192,7 +192,7 @@ namespace conversion { } } - //circle-pitch-scale + // circle-pitch-scale inline std::string toString(mbgl::style::CirclePitchScaleType value) { switch (value) { case mbgl::style::CirclePitchScaleType::Map: @@ -209,4 +209,4 @@ namespace conversion { } // namespace conversion } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/style/conversion/types_string_values.hpp.ejs b/platform/android/src/style/conversion/types_string_values.hpp.ejs index c1646baa1a..bf52919741 100644 --- a/platform/android/src/style/conversion/types_string_values.hpp.ejs +++ b/platform/android/src/style/conversion/types_string_values.hpp.ejs @@ -13,7 +13,7 @@ namespace mbgl { namespace android { namespace conversion { - //visibility + // visibility inline std::string toString(mbgl::style::VisibilityType value) { switch (value) { case mbgl::style::VisibilityType::Visible: @@ -28,7 +28,7 @@ namespace conversion { } <% for (const property of properties) { -%> - //<%- property.name %> + // <%- property.name %> inline std::string toString(mbgl::style::<%- propertyNativeType(property) %> value) { switch (value) { <% for (const value in property.values) { -%> @@ -45,4 +45,4 @@ namespace conversion { } // namespace conversion } // namespace android -} // namespace mbgl
\ No newline at end of file +} // namespace mbgl diff --git a/platform/android/src/style/conversion/url_or_tileset.hpp b/platform/android/src/style/conversion/url_or_tileset.hpp index c1801f56d0..4e502324d0 100644 --- a/platform/android/src/style/conversion/url_or_tileset.hpp +++ b/platform/android/src/style/conversion/url_or_tileset.hpp @@ -35,4 +35,4 @@ struct Converter<variant<std::string, Tileset>> { } } -}
\ No newline at end of file +} diff --git a/platform/android/src/style/functions/categorical_stops.cpp b/platform/android/src/style/functions/categorical_stops.cpp new file mode 100644 index 0000000000..2aff9730a7 --- /dev/null +++ b/platform/android/src/style/functions/categorical_stops.cpp @@ -0,0 +1,18 @@ +#include "categorical_stops.hpp" + +namespace mbgl { +namespace android { + +jni::Object<CategoricalStops> CategoricalStops::New(jni::JNIEnv& env, jni::Array<jni::Object<Stop>> stops) { + static auto constructor = CategoricalStops::javaClass.GetConstructor<jni::Array<jni::Object<Stop>>>(env); + return CategoricalStops::javaClass.New(env, constructor, stops); +} + +jni::Class<CategoricalStops> CategoricalStops::javaClass; + +void CategoricalStops::registerNative(jni::JNIEnv& env) { + CategoricalStops::javaClass = *jni::Class<CategoricalStops>::Find(env).NewGlobalRef(env).release(); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/functions/categorical_stops.hpp b/platform/android/src/style/functions/categorical_stops.hpp new file mode 100644 index 0000000000..a198c8d5c9 --- /dev/null +++ b/platform/android/src/style/functions/categorical_stops.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <jni/jni.hpp> + +#include "stop.hpp" + +namespace mbgl { +namespace android { + +class CategoricalStops : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/CategoricalStops"; }; + + static jni::Object<CategoricalStops> New(jni::JNIEnv&, jni::Array<jni::Object<Stop>>); + + static jni::Class<CategoricalStops> javaClass; + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/functions/exponential_stops.cpp b/platform/android/src/style/functions/exponential_stops.cpp new file mode 100644 index 0000000000..6390a0ec35 --- /dev/null +++ b/platform/android/src/style/functions/exponential_stops.cpp @@ -0,0 +1,18 @@ +#include "exponential_stops.hpp" + +namespace mbgl { +namespace android { + +jni::Object<ExponentialStops> ExponentialStops::New(jni::JNIEnv& env, jni::Object<java::lang::Float> base, jni::Array<jni::Object<Stop>> stops) { + static auto constructor = ExponentialStops::javaClass.GetConstructor<jni::Object<java::lang::Float>, jni::Array<jni::Object<Stop>>>(env); + return ExponentialStops::javaClass.New(env, constructor, base, stops); +} + +jni::Class<ExponentialStops> ExponentialStops::javaClass; + +void ExponentialStops::registerNative(jni::JNIEnv& env) { + ExponentialStops::javaClass = *jni::Class<ExponentialStops>::Find(env).NewGlobalRef(env).release(); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/functions/exponential_stops.hpp b/platform/android/src/style/functions/exponential_stops.hpp new file mode 100644 index 0000000000..391d723cef --- /dev/null +++ b/platform/android/src/style/functions/exponential_stops.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <jni/jni.hpp> + +#include "../../java/lang.hpp" +#include "stop.hpp" + +namespace mbgl { +namespace android { + +class ExponentialStops : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/ExponentialStops"; }; + + static jni::Object<ExponentialStops> New(jni::JNIEnv&, jni::Object<java::lang::Float>, jni::Array<jni::Object<Stop>>); + + static jni::Class<ExponentialStops> javaClass; + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/functions/identity_stops.cpp b/platform/android/src/style/functions/identity_stops.cpp new file mode 100644 index 0000000000..239b0ddb88 --- /dev/null +++ b/platform/android/src/style/functions/identity_stops.cpp @@ -0,0 +1,18 @@ +#include "identity_stops.hpp" + +namespace mbgl { +namespace android { + +jni::Object<IdentityStops> IdentityStops::New(jni::JNIEnv& env) { + static auto constructor = IdentityStops::javaClass.GetConstructor<>(env); + return IdentityStops::javaClass.New(env, constructor); +} + +jni::Class<IdentityStops> IdentityStops::javaClass; + +void IdentityStops::registerNative(jni::JNIEnv& env) { + IdentityStops::javaClass = *jni::Class<IdentityStops>::Find(env).NewGlobalRef(env).release(); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/functions/identity_stops.hpp b/platform/android/src/style/functions/identity_stops.hpp new file mode 100644 index 0000000000..150b2135f0 --- /dev/null +++ b/platform/android/src/style/functions/identity_stops.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class IdentityStops : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/IdentityStops"; }; + + static jni::Object<IdentityStops> New(jni::JNIEnv&); + + static jni::Class<IdentityStops> javaClass; + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/functions/interval_stops.cpp b/platform/android/src/style/functions/interval_stops.cpp new file mode 100644 index 0000000000..c3d9b6513f --- /dev/null +++ b/platform/android/src/style/functions/interval_stops.cpp @@ -0,0 +1,18 @@ +#include "interval_stops.hpp" + +namespace mbgl { +namespace android { + +jni::Object<IntervalStops> IntervalStops::New(jni::JNIEnv& env, jni::Array<jni::Object<Stop>> stops) { + static auto constructor = IntervalStops::javaClass.GetConstructor<jni::Array<jni::Object<Stop>>>(env); + return IntervalStops::javaClass.New(env, constructor, stops); +} + +jni::Class<IntervalStops> IntervalStops::javaClass; + +void IntervalStops::registerNative(jni::JNIEnv& env) { + IntervalStops::javaClass = *jni::Class<IntervalStops>::Find(env).NewGlobalRef(env).release(); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/functions/interval_stops.hpp b/platform/android/src/style/functions/interval_stops.hpp new file mode 100644 index 0000000000..e3f75159cf --- /dev/null +++ b/platform/android/src/style/functions/interval_stops.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <jni/jni.hpp> + +#include "stop.hpp" + +namespace mbgl { +namespace android { + +class IntervalStops : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/IntervalStops"; }; + + static jni::Object<IntervalStops> New(jni::JNIEnv&, jni::Array<jni::Object<Stop>>); + + static jni::Class<IntervalStops> javaClass; + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/functions/stop.cpp b/platform/android/src/style/functions/stop.cpp new file mode 100644 index 0000000000..f9ed4b7368 --- /dev/null +++ b/platform/android/src/style/functions/stop.cpp @@ -0,0 +1,21 @@ +#include "interval_stops.hpp" + +namespace mbgl { +namespace android { + +jni::Object<Stop::CompositeValue> Stop::CompositeValue::New(jni::JNIEnv& env, jni::Object<java::lang::Number> zoom, jni::Object<> value) { + static auto constructor = Stop::CompositeValue::javaClass.GetConstructor<jni::Object<java::lang::Number>, jni::Object<>>(env); + return Stop::CompositeValue::javaClass.New(env, constructor, zoom, value); +} + +jni::Class<Stop> Stop::javaClass; + +jni::Class<Stop::CompositeValue> Stop::CompositeValue::javaClass; + +void Stop::registerNative(jni::JNIEnv& env) { + Stop::javaClass = *jni::Class<Stop>::Find(env).NewGlobalRef(env).release(); + Stop::CompositeValue::javaClass = *jni::Class<Stop::CompositeValue>::Find(env).NewGlobalRef(env).release(); +} + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/functions/stop.hpp b/platform/android/src/style/functions/stop.hpp new file mode 100644 index 0000000000..7c697db65d --- /dev/null +++ b/platform/android/src/style/functions/stop.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include <mbgl/util/noncopyable.hpp> +#include <jni/jni.hpp> + +#include "../../java/lang.hpp" + +namespace mbgl { +namespace android { + +class Stop : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/Stop"; }; + + template<class I, class O> + static jni::Object<Stop> New(jni::JNIEnv& env, jni::Object<I> in, jni::Object<O> out) { + static auto constructor = Stop::javaClass.GetConstructor<jni::Object<>, jni::Object<>>(env); + return Stop::javaClass.New(env, constructor, (jni::Object<>) in, (jni::Object<>) out); + } + + static jni::Class<Stop> javaClass; + + static void registerNative(jni::JNIEnv&); + + class CompositeValue : private mbgl::util::noncopyable { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/Stop$CompositeValue"; }; + + static jni::Object<Stop::CompositeValue> New(jni::JNIEnv&, jni::Object<java::lang::Number>, jni::Object<>); + + static jni::Class<Stop::CompositeValue> javaClass; + }; +}; + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/layers/background_layer.cpp b/platform/android/src/style/layers/background_layer.cpp index 021ac947ad..9915f3894e 100644 --- a/platform/android/src/style/layers/background_layer.cpp +++ b/platform/android/src/style/layers/background_layer.cpp @@ -9,14 +9,27 @@ namespace mbgl { namespace android { + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ BackgroundLayer::BackgroundLayer(jni::JNIEnv& env, jni::String layerId) : Layer(env, std::make_unique<mbgl::style::BackgroundLayer>(jni::Make<std::string>(env, layerId))) { } + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ BackgroundLayer::BackgroundLayer(mbgl::Map& map, mbgl::style::BackgroundLayer& coreLayer) : Layer(map, coreLayer) { } + /** + * Creates an owning peer object (for layers not attached to the map) + */ + BackgroundLayer::BackgroundLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::BackgroundLayer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + BackgroundLayer::~BackgroundLayer() = default; // Property getters @@ -47,12 +60,12 @@ namespace android { } void BackgroundLayer::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class BackgroundLayer::javaClass = *jni::Class<BackgroundLayer>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<BackgroundLayer>( env, BackgroundLayer::javaClass, "nativePtr", std::make_unique<BackgroundLayer, JNIEnv&, jni::String>, diff --git a/platform/android/src/style/layers/background_layer.hpp b/platform/android/src/style/layers/background_layer.hpp index bd62c024f4..2fdc948892 100644 --- a/platform/android/src/style/layers/background_layer.hpp +++ b/platform/android/src/style/layers/background_layer.hpp @@ -22,6 +22,8 @@ public: BackgroundLayer(mbgl::Map&, mbgl::style::BackgroundLayer&); + BackgroundLayer(mbgl::Map&, std::unique_ptr<mbgl::style::BackgroundLayer>); + ~BackgroundLayer(); // Property getters diff --git a/platform/android/src/style/layers/circle_layer.cpp b/platform/android/src/style/layers/circle_layer.cpp index 4a6ba95d31..948c397829 100644 --- a/platform/android/src/style/layers/circle_layer.cpp +++ b/platform/android/src/style/layers/circle_layer.cpp @@ -9,14 +9,27 @@ namespace mbgl { namespace android { + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ CircleLayer::CircleLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId) : Layer(env, std::make_unique<mbgl::style::CircleLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) { } + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ CircleLayer::CircleLayer(mbgl::Map& map, mbgl::style::CircleLayer& coreLayer) : Layer(map, coreLayer) { } + /** + * Creates an owning peer object (for layers not attached to the map) + */ + CircleLayer::CircleLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::CircleLayer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + CircleLayer::~CircleLayer() = default; // Property getters @@ -89,12 +102,12 @@ namespace android { } void CircleLayer::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class CircleLayer::javaClass = *jni::Class<CircleLayer>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<CircleLayer>( env, CircleLayer::javaClass, "nativePtr", std::make_unique<CircleLayer, JNIEnv&, jni::String, jni::String>, diff --git a/platform/android/src/style/layers/circle_layer.hpp b/platform/android/src/style/layers/circle_layer.hpp index d45984f23b..ee988d7c57 100644 --- a/platform/android/src/style/layers/circle_layer.hpp +++ b/platform/android/src/style/layers/circle_layer.hpp @@ -22,6 +22,8 @@ public: CircleLayer(mbgl::Map&, mbgl::style::CircleLayer&); + CircleLayer(mbgl::Map&, std::unique_ptr<mbgl::style::CircleLayer>); + ~CircleLayer(); // Property getters diff --git a/platform/android/src/style/layers/custom_layer.cpp b/platform/android/src/style/layers/custom_layer.cpp index d5d330a019..9bdc308d85 100644 --- a/platform/android/src/style/layers/custom_layer.cpp +++ b/platform/android/src/style/layers/custom_layer.cpp @@ -40,12 +40,12 @@ namespace android { } void CustomLayer::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class CustomLayer::javaClass = *jni::Class<CustomLayer>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<CustomLayer>( env, CustomLayer::javaClass, "nativePtr", std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong>, diff --git a/platform/android/src/style/layers/fill_layer.cpp b/platform/android/src/style/layers/fill_layer.cpp index 84d47b6afe..fc1dfccfcc 100644 --- a/platform/android/src/style/layers/fill_layer.cpp +++ b/platform/android/src/style/layers/fill_layer.cpp @@ -9,14 +9,27 @@ namespace mbgl { namespace android { + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ FillLayer::FillLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId) : Layer(env, std::make_unique<mbgl::style::FillLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) { } + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ FillLayer::FillLayer(mbgl::Map& map, mbgl::style::FillLayer& coreLayer) : Layer(map, coreLayer) { } + /** + * Creates an owning peer object (for layers not attached to the map) + */ + FillLayer::FillLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::FillLayer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + FillLayer::~FillLayer() = default; // Property getters @@ -71,12 +84,12 @@ namespace android { } void FillLayer::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class FillLayer::javaClass = *jni::Class<FillLayer>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<FillLayer>( env, FillLayer::javaClass, "nativePtr", std::make_unique<FillLayer, JNIEnv&, jni::String, jni::String>, diff --git a/platform/android/src/style/layers/fill_layer.hpp b/platform/android/src/style/layers/fill_layer.hpp index 7609a5742f..f43c263ab8 100644 --- a/platform/android/src/style/layers/fill_layer.hpp +++ b/platform/android/src/style/layers/fill_layer.hpp @@ -22,6 +22,8 @@ public: FillLayer(mbgl::Map&, mbgl::style::FillLayer&); + FillLayer(mbgl::Map&, std::unique_ptr<mbgl::style::FillLayer>); + ~FillLayer(); // Property getters diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index c0c57c839d..dbf71fd2af 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -5,12 +5,12 @@ #include <mbgl/util/logging.hpp> -//Java -> C++ conversion +// Java -> C++ conversion #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/source.hpp> -//C++ -> Java conversion +// C++ -> Java conversion #include "../conversion/property_value.hpp" #include <string> @@ -26,22 +26,35 @@ namespace android { , layer(*ownedLayer) { } + /** + * Takes a non-owning reference. For lookup methods + */ Layer::Layer(mbgl::Map& coreMap, mbgl::style::Layer& coreLayer) : layer(coreLayer) , map(&coreMap) { } + /** + * Takes a owning reference. Ownership is transfered to this peer, eg after removing + * from the map + */ + Layer::Layer(mbgl::Map& coreMap, std::unique_ptr<mbgl::style::Layer> coreLayer) + : ownedLayer(std::move(coreLayer)) + , layer(*ownedLayer) + , map(&coreMap) { + } + Layer::~Layer() { } void Layer::addToMap(mbgl::Map& _map, mbgl::optional<std::string> before) { - //Check to see if we own the layer first + // Check to see if we own the layer first if (!ownedLayer) { throw std::runtime_error("Cannot add layer twice"); } - //Add layer to map + // Add layer to map _map.addLayer(releaseCoreLayer(), before); - //Save pointer to the map + // Save pointer to the map this->map = &_map; } @@ -65,7 +78,7 @@ namespace android { void Layer::setLayoutProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) { Value value(env, jvalue); - //Convert and set property + // Convert and set property optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setLayoutProperty(layer, jni::Make<std::string>(env, jname), value); if (error) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message); @@ -76,7 +89,7 @@ namespace android { void Layer::setPaintProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) { Value value(env, jvalue); - //Convert and set property + // Convert and set property optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), value, mbgl::optional<std::string>()); if (error) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message); @@ -153,12 +166,12 @@ namespace android { jni::Class<Layer> Layer::javaClass; void Layer::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class Layer::javaClass = *jni::Class<Layer>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<Layer>(env, Layer::javaClass, "nativePtr", METHOD(&Layer::getId, "nativeGetId"), METHOD(&Layer::setLayoutProperty, "nativeSetLayoutProperty"), @@ -174,5 +187,5 @@ namespace android { } -} //android -} //mbgl
\ No newline at end of file +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/layers/layer.cpp.ejs b/platform/android/src/style/layers/layer.cpp.ejs index 500c76ea7a..5da397d77d 100644 --- a/platform/android/src/style/layers/layer.cpp.ejs +++ b/platform/android/src/style/layers/layer.cpp.ejs @@ -14,18 +14,34 @@ namespace mbgl { namespace android { <% if (type === 'background') { -%> + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(jni::JNIEnv& env, jni::String layerId) : Layer(env, std::make_unique<mbgl::style::<%- camelize(type) %>Layer>(jni::Make<std::string>(env, layerId))) { <% } else { -%> + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId) : Layer(env, std::make_unique<mbgl::style::<%- camelize(type) %>Layer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) { <% } -%> } + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(mbgl::Map& map, mbgl::style::<%- camelize(type) %>Layer& coreLayer) : Layer(map, coreLayer) { } + /** + * Creates an owning peer object (for layers not attached to the map) + */ + <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(mbgl::Map& map, std::unique_ptr<mbgl::style::<%- camelize(type) %>Layer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + <%- camelize(type) %>Layer::~<%- camelize(type) %>Layer() = default; // Property getters @@ -46,12 +62,12 @@ namespace android { } void <%- camelize(type) %>Layer::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class <%- camelize(type) %>Layer::javaClass = *jni::Class<<%- camelize(type) %>Layer>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<<%- camelize(type) %>Layer>( env, <%- camelize(type) %>Layer::javaClass, "nativePtr", <% if (type === 'background') { -%> diff --git a/platform/android/src/style/layers/layer.hpp b/platform/android/src/style/layers/layer.hpp index f3cd073552..deea7a6613 100644 --- a/platform/android/src/style/layers/layer.hpp +++ b/platform/android/src/style/layers/layer.hpp @@ -21,11 +21,16 @@ public: static void registerNative(jni::JNIEnv&); /* - * Called when a Java object is created on the c++ side + * Called when a non-owning peer object is created on the c++ side */ Layer(mbgl::Map&, mbgl::style::Layer&); /* + * Called when a owning peer object is created on the c++ side + */ + Layer(mbgl::Map&, std::unique_ptr<mbgl::style::Layer>); + + /* * Called when a Java object was created from the jvm side */ Layer(jni::JNIEnv&, std::unique_ptr<mbgl::style::Layer>); @@ -49,7 +54,7 @@ public: void setPaintProperty(jni::JNIEnv&, jni::String, jni::Object<> value); - //Zoom + // Zoom jni::jfloat getMinZoom(jni::JNIEnv&); @@ -65,28 +70,24 @@ public: void setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer); - //Property getters + // Property getters jni::Object<jni::ObjectTag> getVisibility(jni::JNIEnv&); protected: - //Release the owned view and return it + // Release the owned view and return it std::unique_ptr<mbgl::style::Layer> releaseCoreLayer(); - //Owned layer is set when creating a new layer, before adding it to the map + // Owned layer is set when creating a new layer, before adding it to the map std::unique_ptr<mbgl::style::Layer> ownedLayer; - //Raw reference to the layer + // Raw reference to the layer mbgl::style::Layer& layer; - //Map is set when the layer is retrieved or after adding to the map + // Map is set when the layer is retrieved or after adding to the map mbgl::Map* map; }; -} //android -} //mbgl - - - - +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/layers/layer.hpp.ejs b/platform/android/src/style/layers/layer.hpp.ejs index 3d715746ff..102efd2d4d 100644 --- a/platform/android/src/style/layers/layer.hpp.ejs +++ b/platform/android/src/style/layers/layer.hpp.ejs @@ -30,6 +30,8 @@ public: <%- camelize(type) %>Layer(mbgl::Map&, mbgl::style::<%- camelize(type) %>Layer&); + <%- camelize(type) %>Layer(mbgl::Map&, std::unique_ptr<mbgl::style::<%- camelize(type) %>Layer>); + ~<%- camelize(type) %>Layer(); // Property getters diff --git a/platform/android/src/style/layers/layers.cpp b/platform/android/src/style/layers/layers.cpp index 57dbf6f4b1..5c6ee1ae8f 100644 --- a/platform/android/src/style/layers/layers.cpp +++ b/platform/android/src/style/layers/layers.cpp @@ -1,5 +1,6 @@ #include "layers.hpp" +#include <mbgl/style/layer.hpp> #include <mbgl/style/layers/background_layer.hpp> #include <mbgl/style/layers/circle_layer.hpp> #include <mbgl/style/layers/fill_layer.hpp> @@ -10,55 +11,86 @@ #include "background_layer.hpp" #include "circle_layer.hpp" +#include "custom_layer.hpp" #include "fill_layer.hpp" #include "line_layer.hpp" #include "raster_layer.hpp" #include "symbol_layer.hpp" -#include "custom_layer.hpp" +#include "unknown_layer.hpp" namespace mbgl { namespace android { -Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) { - Layer* layer; +static Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) { if (coreLayer.is<mbgl::style::BackgroundLayer>()) { - layer = new BackgroundLayer(map, *coreLayer.as<mbgl::style::BackgroundLayer>()); + return new BackgroundLayer(map, *coreLayer.as<mbgl::style::BackgroundLayer>()); } else if (coreLayer.is<mbgl::style::CircleLayer>()) { - layer = new CircleLayer(map, *coreLayer.as<mbgl::style::CircleLayer>()); + return new CircleLayer(map, *coreLayer.as<mbgl::style::CircleLayer>()); } else if (coreLayer.is<mbgl::style::FillLayer>()) { - layer = new FillLayer(map, *coreLayer.as<mbgl::style::FillLayer>()); + return new FillLayer(map, *coreLayer.as<mbgl::style::FillLayer>()); } else if (coreLayer.is<mbgl::style::LineLayer>()) { - layer = new LineLayer(map, *coreLayer.as<mbgl::style::LineLayer>()); + return new LineLayer(map, *coreLayer.as<mbgl::style::LineLayer>()); } else if (coreLayer.is<mbgl::style::RasterLayer>()) { - layer = new RasterLayer(map, *coreLayer.as<mbgl::style::RasterLayer>()); + return new RasterLayer(map, *coreLayer.as<mbgl::style::RasterLayer>()); } else if (coreLayer.is<mbgl::style::SymbolLayer>()) { - layer = new SymbolLayer(map, *coreLayer.as<mbgl::style::SymbolLayer>()); + return new SymbolLayer(map, *coreLayer.as<mbgl::style::SymbolLayer>()); } else if (coreLayer.is<mbgl::style::CustomLayer>()) { - layer = new CustomLayer(map, *coreLayer.as<mbgl::style::CustomLayer>()); + return new CustomLayer(map, *coreLayer.as<mbgl::style::CustomLayer>()); } else { - throw new std::runtime_error("Layer type not implemented"); + return new UnknownLayer(map, coreLayer); } +} - return layer; +template <class LayerType, class PeerType> +static PeerType* createPeer(Map& map, std::unique_ptr<mbgl::style::Layer> layer) { + return new PeerType(map, std::move(std::unique_ptr<LayerType>(layer.release()->as<LayerType>()))); +} + +static Layer* initializeLayerPeer(Map& map, std::unique_ptr<mbgl::style::Layer> coreLayer) { + if (coreLayer->is<style::BackgroundLayer>()) { + return createPeer<style::BackgroundLayer, BackgroundLayer>(map, std::move(coreLayer)); + } else if (coreLayer->is<style::CircleLayer>()) { + return createPeer<style::CircleLayer, CircleLayer>(map, std::move(coreLayer)); + } else if (coreLayer->is<style::FillLayer>()) { + return createPeer<style::FillLayer, FillLayer>(map, std::move(coreLayer)); + } else if (coreLayer->is<style::LineLayer>()) { + return createPeer<style::LineLayer, LineLayer>(map, std::move(coreLayer)); + } else if (coreLayer->is<style::RasterLayer>()) { + return createPeer<style::RasterLayer, RasterLayer>(map, std::move(coreLayer)); + } else if (coreLayer->is<style::SymbolLayer>()) { + return createPeer<style::SymbolLayer, SymbolLayer>(map, std::move(coreLayer)); + } else if (coreLayer->is<mbgl::style::CustomLayer>()) { + return createPeer<style::SymbolLayer, SymbolLayer>(map, std::move(coreLayer)); + } else { + return new UnknownLayer(map, std::move(coreLayer)); + } } -jni::jobject* createJavaLayerPeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style::Layer& coreLayer) { +jni::jobject* createJavaLayerPeer(jni::JNIEnv& env, Map& map, style::Layer& coreLayer) { std::unique_ptr<Layer> peerLayer = std::unique_ptr<Layer>(initializeLayerPeer(map, coreLayer)); jni::jobject* result = peerLayer->createJavaPeer(env); peerLayer.release(); return result; } +jni::jobject* createJavaLayerPeer(jni::JNIEnv& env, mbgl::Map& map, std::unique_ptr<mbgl::style::Layer> coreLayer) { + std::unique_ptr<Layer> peerLayer = std::unique_ptr<Layer>(initializeLayerPeer(map, std::move(coreLayer))); + jni::jobject* result = peerLayer->createJavaPeer(env); + peerLayer.release(); + return result; +} + void registerNativeLayers(jni::JNIEnv& env) { Layer::registerNative(env); BackgroundLayer::registerNative(env); CircleLayer::registerNative(env); + CustomLayer::registerNative(env); FillLayer::registerNative(env); LineLayer::registerNative(env); RasterLayer::registerNative(env); SymbolLayer::registerNative(env); - CustomLayer::registerNative(env); + UnknownLayer::registerNative(env); } -} //android -} //mbgl
\ No newline at end of file +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/layers/layers.hpp b/platform/android/src/style/layers/layers.hpp index 0c979ec2cf..75863a324a 100644 --- a/platform/android/src/style/layers/layers.hpp +++ b/platform/android/src/style/layers/layers.hpp @@ -10,11 +10,17 @@ namespace mbgl { namespace android { -mbgl::android::Layer* initializeLayerPeer(mbgl::Map&, mbgl::style::Layer&); - +/** + * Create a non-owning peer + */ jni::jobject* createJavaLayerPeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Layer&); +/** + * Create an owning peer + */ +jni::jobject* createJavaLayerPeer(jni::JNIEnv& env, mbgl::Map& map, std::unique_ptr<mbgl::style::Layer>); + void registerNativeLayers(jni::JNIEnv&); } -}
\ No newline at end of file +} diff --git a/platform/android/src/style/layers/line_layer.cpp b/platform/android/src/style/layers/line_layer.cpp index 2dce8b618a..1a3a666a7b 100644 --- a/platform/android/src/style/layers/line_layer.cpp +++ b/platform/android/src/style/layers/line_layer.cpp @@ -9,14 +9,27 @@ namespace mbgl { namespace android { + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ LineLayer::LineLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId) : Layer(env, std::make_unique<mbgl::style::LineLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) { } + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ LineLayer::LineLayer(mbgl::Map& map, mbgl::style::LineLayer& coreLayer) : Layer(map, coreLayer) { } + /** + * Creates an owning peer object (for layers not attached to the map) + */ + LineLayer::LineLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::LineLayer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + LineLayer::~LineLayer() = default; // Property getters @@ -113,12 +126,12 @@ namespace android { } void LineLayer::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class LineLayer::javaClass = *jni::Class<LineLayer>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<LineLayer>( env, LineLayer::javaClass, "nativePtr", std::make_unique<LineLayer, JNIEnv&, jni::String, jni::String>, diff --git a/platform/android/src/style/layers/line_layer.hpp b/platform/android/src/style/layers/line_layer.hpp index e2fc93329e..a79c8b9021 100644 --- a/platform/android/src/style/layers/line_layer.hpp +++ b/platform/android/src/style/layers/line_layer.hpp @@ -22,6 +22,8 @@ public: LineLayer(mbgl::Map&, mbgl::style::LineLayer&); + LineLayer(mbgl::Map&, std::unique_ptr<mbgl::style::LineLayer>); + ~LineLayer(); // Property getters diff --git a/platform/android/src/style/layers/raster_layer.cpp b/platform/android/src/style/layers/raster_layer.cpp index 25b26155ae..8a324b88f2 100644 --- a/platform/android/src/style/layers/raster_layer.cpp +++ b/platform/android/src/style/layers/raster_layer.cpp @@ -9,14 +9,27 @@ namespace mbgl { namespace android { + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ RasterLayer::RasterLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId) : Layer(env, std::make_unique<mbgl::style::RasterLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) { } + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ RasterLayer::RasterLayer(mbgl::Map& map, mbgl::style::RasterLayer& coreLayer) : Layer(map, coreLayer) { } + /** + * Creates an owning peer object (for layers not attached to the map) + */ + RasterLayer::RasterLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::RasterLayer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + RasterLayer::~RasterLayer() = default; // Property getters @@ -71,12 +84,12 @@ namespace android { } void RasterLayer::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class RasterLayer::javaClass = *jni::Class<RasterLayer>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<RasterLayer>( env, RasterLayer::javaClass, "nativePtr", std::make_unique<RasterLayer, JNIEnv&, jni::String, jni::String>, diff --git a/platform/android/src/style/layers/raster_layer.hpp b/platform/android/src/style/layers/raster_layer.hpp index 3cc2d96dde..2f5d4f6fcd 100644 --- a/platform/android/src/style/layers/raster_layer.hpp +++ b/platform/android/src/style/layers/raster_layer.hpp @@ -22,6 +22,8 @@ public: RasterLayer(mbgl::Map&, mbgl::style::RasterLayer&); + RasterLayer(mbgl::Map&, std::unique_ptr<mbgl::style::RasterLayer>); + ~RasterLayer(); // Property getters diff --git a/platform/android/src/style/layers/symbol_layer.cpp b/platform/android/src/style/layers/symbol_layer.cpp index 9318d42d5b..e42eeb4c77 100644 --- a/platform/android/src/style/layers/symbol_layer.cpp +++ b/platform/android/src/style/layers/symbol_layer.cpp @@ -9,14 +9,27 @@ namespace mbgl { namespace android { + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ SymbolLayer::SymbolLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId) : Layer(env, std::make_unique<mbgl::style::SymbolLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) { } + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ SymbolLayer::SymbolLayer(mbgl::Map& map, mbgl::style::SymbolLayer& coreLayer) : Layer(map, coreLayer) { } + /** + * Creates an owning peer object (for layers not attached to the map) + */ + SymbolLayer::SymbolLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::SymbolLayer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + SymbolLayer::~SymbolLayer() = default; // Property getters @@ -317,12 +330,12 @@ namespace android { } void SymbolLayer::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class SymbolLayer::javaClass = *jni::Class<SymbolLayer>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<SymbolLayer>( env, SymbolLayer::javaClass, "nativePtr", std::make_unique<SymbolLayer, JNIEnv&, jni::String, jni::String>, diff --git a/platform/android/src/style/layers/symbol_layer.hpp b/platform/android/src/style/layers/symbol_layer.hpp index 1048b01b14..98ce5572e9 100644 --- a/platform/android/src/style/layers/symbol_layer.hpp +++ b/platform/android/src/style/layers/symbol_layer.hpp @@ -22,6 +22,8 @@ public: SymbolLayer(mbgl::Map&, mbgl::style::SymbolLayer&); + SymbolLayer(mbgl::Map&, std::unique_ptr<mbgl::style::SymbolLayer>); + ~SymbolLayer(); // Property getters diff --git a/platform/android/src/style/layers/unknown_layer.cpp b/platform/android/src/style/layers/unknown_layer.cpp new file mode 100644 index 0000000000..9ec963a41b --- /dev/null +++ b/platform/android/src/style/layers/unknown_layer.cpp @@ -0,0 +1,49 @@ +#include "unknown_layer.hpp" + +#include <string> + +namespace { + + // Dummy initializer (We don't support initializing this from the JVM) + std::unique_ptr<mbgl::android::UnknownLayer> init(jni::JNIEnv&) { + throw new std::runtime_error("UnknownLayer should not be initialized from the JVM"); + } + +} // namespace + +namespace mbgl { +namespace android { + + UnknownLayer::UnknownLayer(mbgl::Map& map, mbgl::style::Layer& coreLayer) + : Layer(map, coreLayer) { + } + + UnknownLayer::UnknownLayer(mbgl::Map& map, std::unique_ptr<mbgl::style::Layer> coreLayer) + : Layer(map, std::move(coreLayer)) { + } + + jni::Class<UnknownLayer> UnknownLayer::javaClass; + + jni::jobject* UnknownLayer::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = UnknownLayer::javaClass.template GetConstructor<jni::jlong>(env); + return UnknownLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)); + } + + void UnknownLayer::registerNative(jni::JNIEnv& env) { + // Lookup the class + UnknownLayer::javaClass = *jni::Class<UnknownLayer>::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + std::function<std::unique_ptr<UnknownLayer>(JNIEnv&)> initializer = nullptr; + + // Register the peer + jni::RegisterNativePeer<UnknownLayer>( + env, UnknownLayer::javaClass, "nativePtr", + init, + "initialize", + "finalize"); + } + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/layers/unknown_layer.hpp b/platform/android/src/style/layers/unknown_layer.hpp new file mode 100644 index 0000000000..67992ea007 --- /dev/null +++ b/platform/android/src/style/layers/unknown_layer.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "layer.hpp" +#include <mbgl/style/layer.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class UnknownLayer : public Layer { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/UnknownLayer"; }; + + static jni::Class<UnknownLayer> javaClass; + + static void registerNative(jni::JNIEnv&); + + UnknownLayer(mbgl::Map&, mbgl::style::Layer&); + + UnknownLayer(mbgl::Map&, std::unique_ptr<mbgl::style::Layer>); + + ~UnknownLayer() = default; + + jni::jobject* createJavaPeer(jni::JNIEnv&); + +}; // class UnknownLayer + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/geojson_source.cpp b/platform/android/src/style/sources/geojson_source.cpp index 234dccb315..37ce0644c1 100644 --- a/platform/android/src/style/sources/geojson_source.cpp +++ b/platform/android/src/style/sources/geojson_source.cpp @@ -27,19 +27,19 @@ namespace android { void GeoJSONSource::setGeoJSON(jni::JNIEnv& env, jni::Object<> json) { using namespace mbgl::style::conversion; - //Convert the jni object + // Convert the jni object Result<GeoJSON> converted = convert<GeoJSON>(Value(env, json)); if(!converted) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + converted.error().message); return; } - //Update the core source + // Update the core source source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setGeoJSON(*converted); } void GeoJSONSource::setURL(jni::JNIEnv& env, jni::String url) { - //Update the core source + // Update the core source source.as<mbgl::style::GeoJSONSource>()->GeoJSONSource::setURL(jni::Make<std::string>(env, url)); } @@ -51,12 +51,12 @@ namespace android { } void GeoJSONSource::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class GeoJSONSource::javaClass = *jni::Class<GeoJSONSource>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<GeoJSONSource>( env, GeoJSONSource::javaClass, "nativePtr", std::make_unique<GeoJSONSource, JNIEnv&, jni::String, jni::Object<>>, diff --git a/platform/android/src/style/sources/raster_source.cpp b/platform/android/src/style/sources/raster_source.cpp index b56b56676d..42ac4cda99 100644 --- a/platform/android/src/style/sources/raster_source.cpp +++ b/platform/android/src/style/sources/raster_source.cpp @@ -36,12 +36,12 @@ namespace android { } void RasterSource::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class RasterSource::javaClass = *jni::Class<RasterSource>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<RasterSource>( env, RasterSource::javaClass, "nativePtr", std::make_unique<RasterSource, JNIEnv&, jni::String, jni::Object<>, jni::jint>, diff --git a/platform/android/src/style/sources/source.cpp b/platform/android/src/style/sources/source.cpp index aca7bd6a84..b780de5627 100644 --- a/platform/android/src/style/sources/source.cpp +++ b/platform/android/src/style/sources/source.cpp @@ -5,11 +5,11 @@ #include <mbgl/util/logging.hpp> -//Java -> C++ conversion +// Java -> C++ conversion #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/source.hpp> -//C++ -> Java conversion +// C++ -> Java conversion #include "../conversion/property_value.hpp" #include <string> @@ -44,15 +44,15 @@ namespace android { } void Source::addToMap(mbgl::Map& _map) { - //Check to see if we own the source first + // Check to see if we own the source first if (!ownedSource) { throw std::runtime_error("Cannot add source twice"); } - //Add source to map + // Add source to map _map.addSource(releaseCoreSource()); - //Save pointer to the map + // Save pointer to the map this->map = &_map; } @@ -64,17 +64,17 @@ namespace android { jni::Class<Source> Source::javaClass; void Source::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class Source::javaClass = *jni::Class<Source>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<Source>(env, Source::javaClass, "nativePtr", METHOD(&Source::getId, "nativeGetId") ); } -} //android -} //mbgl
\ No newline at end of file +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/source.hpp b/platform/android/src/style/sources/source.hpp index 0e5d354d93..9a9d504d68 100644 --- a/platform/android/src/style/sources/source.hpp +++ b/platform/android/src/style/sources/source.hpp @@ -46,22 +46,18 @@ public: jni::String getId(jni::JNIEnv&); protected: - //Release the owned view and return it + // Release the owned view and return it std::unique_ptr<mbgl::style::Source> releaseCoreSource(); - //Set on newly created sources until added to the map + // Set on newly created sources until added to the map std::unique_ptr<mbgl::style::Source> ownedSource; - //Raw pointer that is valid until the source is removed from the map + // Raw pointer that is valid until the source is removed from the map mbgl::style::Source& source; - //Map pointer is valid for newly created sources only after adding to the map + // Map pointer is valid for newly created sources only after adding to the map mbgl::Map* map; }; -} //android -} //mbgl - - - - +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/sources.cpp b/platform/android/src/style/sources/sources.cpp index 210acd607f..b4e70202b4 100644 --- a/platform/android/src/style/sources/sources.cpp +++ b/platform/android/src/style/sources/sources.cpp @@ -8,6 +8,7 @@ #include "source.hpp" #include "geojson_source.hpp" #include "raster_source.hpp" +#include "unknown_source.hpp" #include "vector_source.hpp" namespace mbgl { @@ -22,7 +23,7 @@ Source* initializeSourcePeer(mbgl::Map& map, mbgl::style::Source& coreSource) { } else if (coreSource.is<mbgl::style::GeoJSONSource>()) { source = new GeoJSONSource(map, *coreSource.as<mbgl::style::GeoJSONSource>()); } else { - throw new std::runtime_error("Source type not implemented"); + source = new UnknownSource(map, coreSource); } return source; @@ -37,10 +38,11 @@ jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style void registerNativeSources(jni::JNIEnv& env) { Source::registerNative(env); - VectorSource::registerNative(env); - RasterSource::registerNative(env); GeoJSONSource::registerNative(env); + RasterSource::registerNative(env); + UnknownSource::registerNative(env); + VectorSource::registerNative(env); } } -}
\ No newline at end of file +} diff --git a/platform/android/src/style/sources/sources.hpp b/platform/android/src/style/sources/sources.hpp index 3038873733..09a8b35067 100644 --- a/platform/android/src/style/sources/sources.hpp +++ b/platform/android/src/style/sources/sources.hpp @@ -9,12 +9,12 @@ namespace mbgl { namespace android { - + mbgl::android::Source* initializeSourcePeer(mbgl::Map&, mbgl::style::Source&); - + jni::jobject* createJavaSourcePeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Source&); - + void registerNativeSources(jni::JNIEnv&); } -}
\ No newline at end of file +} diff --git a/platform/android/src/style/sources/unknown_source.cpp b/platform/android/src/style/sources/unknown_source.cpp new file mode 100644 index 0000000000..c1b1cc8c02 --- /dev/null +++ b/platform/android/src/style/sources/unknown_source.cpp @@ -0,0 +1,42 @@ +#include "unknown_source.hpp" + +namespace { + + // Dummy initializer (We don't support initializing this from the JVM) + std::unique_ptr<mbgl::android::UnknownSource> init(jni::JNIEnv&) { + throw new std::runtime_error("UnknownSource should not be initialized from the JVM"); + } + +} // namespace + +namespace mbgl { +namespace android { + + UnknownSource::UnknownSource(mbgl::Map& map, mbgl::style::Source& coreSource) + : Source(map, coreSource) { + } + + jni::Class<UnknownSource> UnknownSource::javaClass; + + jni::jobject* UnknownSource::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = UnknownSource::javaClass.template GetConstructor<jni::jlong>(env); + return UnknownSource::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this)); + } + + void UnknownSource::registerNative(jni::JNIEnv& env) { + // Lookup the class + UnknownSource::javaClass = *jni::Class<UnknownSource>::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) + + // Register the peer + jni::RegisterNativePeer<UnknownSource>( + env, UnknownSource::javaClass, "nativePtr", + init, + "initialize", + "finalize" + ); + } + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/unknown_source.hpp b/platform/android/src/style/sources/unknown_source.hpp new file mode 100644 index 0000000000..3c37239792 --- /dev/null +++ b/platform/android/src/style/sources/unknown_source.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "source.hpp" +#include <mbgl/style/source.hpp> +#include <jni/jni.hpp> + +namespace mbgl { +namespace android { + +class UnknownSource : public Source { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/UnknownSource"; }; + + static jni::Class<UnknownSource> javaClass; + + static void registerNative(jni::JNIEnv&); + + UnknownSource(mbgl::Map&, mbgl::style::Source&); + + ~UnknownSource() = default; + + jni::jobject* createJavaPeer(jni::JNIEnv&); + +}; // class UnknownSource + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/vector_source.cpp b/platform/android/src/style/sources/vector_source.cpp index 0d065a3348..e60d8d4641 100644 --- a/platform/android/src/style/sources/vector_source.cpp +++ b/platform/android/src/style/sources/vector_source.cpp @@ -35,12 +35,12 @@ namespace android { } void VectorSource::registerNative(jni::JNIEnv& env) { - //Lookup the class + // Lookup the class VectorSource::javaClass = *jni::Class<VectorSource>::Find(env).NewGlobalRef(env).release(); #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name) - //Register the peer + // Register the peer jni::RegisterNativePeer<VectorSource>( env, VectorSource::javaClass, "nativePtr", std::make_unique<VectorSource, JNIEnv&, jni::String, jni::Object<>>, diff --git a/platform/android/src/style/value.cpp b/platform/android/src/style/value.cpp index c8aad1682b..0b5d81feb1 100644 --- a/platform/android/src/style/value.cpp +++ b/platform/android/src/style/value.cpp @@ -20,7 +20,7 @@ namespace android { JNIEnv& env; }; - //Instance + // Instance Value::Value(jni::JNIEnv& env, jni::jobject* _value) : jenv(env), value(_value, ObjectDeleter(env)) {} diff --git a/platform/android/src/test/Main.java b/platform/android/src/test/Main.java index b6f540f666..b0f3aeb7b9 100644 --- a/platform/android/src/test/Main.java +++ b/platform/android/src/test/Main.java @@ -3,13 +3,13 @@ public class Main { public native void runAllTests(String[] args); public static void main(String[] args) throws Exception { - //Load the tests + // Load the tests System.loadLibrary("mbgl-test"); - //Run the tests + // Run the tests new Main().runAllTests(args); - //Exit explicitly otherwise dalvikvm won't quit + // Exit explicitly otherwise dalvikvm won't quit System.exit(0); } } diff --git a/platform/android/src/test/main.jni.cpp b/platform/android/src/test/main.jni.cpp index f79d7671cb..f96dd6aa5e 100644 --- a/platform/android/src/test/main.jni.cpp +++ b/platform/android/src/test/main.jni.cpp @@ -1,7 +1,5 @@ #include "../jni.hpp" -#include <android/log.h> -#include <jni/jni.hpp> #include <jni/jni.hpp> #include <mbgl/util/logging.hpp> @@ -9,10 +7,6 @@ #include <vector> -#pragma clang diagnostic ignored "-Wunused-parameter" - -#define MAKE_NATIVE_METHOD(name, sig) jni::MakeNativeMethod<decltype(name), name>( #name, sig ) - namespace { // Main class (entry point for tests from dalvikvm) @@ -48,31 +42,26 @@ struct Main { } // namespace -// JNI Bindings to stub the android.util.Log implementation - -static jboolean isLoggable(JNIEnv* env, jni::jobject* clazz, jni::jstring* tag, jint level) { - return true; -} - -static jint println_native(JNIEnv* env, jni::jobject* clazz, jint bufID, jint priority, jni::jstring* jtag, jni::jstring* jmessage) { - if (jtag == nullptr || jmessage == nullptr) { - return false; - } - - std::string tag = jni::Make<std::string>(*env, jni::String(jtag)); - std::string message = jni::Make<std::string>(*env, jni::String(jmessage)); - - return __android_log_print(priority, tag.c_str(), "%s", message.c_str()); -} - -static jint logger_entry_max_payload_native(JNIEnv* env, jni::jobject* clazz) { - return static_cast<jint>(4068); -} - +// We're declaring the function, which is libandroid_runtime.so. +// It is defined in AndroidRuntime.cpp: +// https://github.com/android/platform_frameworks_base/blob/master/core/jni/AndroidRuntime.cpp +// Once this symbol goes away, we'll have to revisit. +// This method loads and registers all of the Android-native frameworks so that we can use +// android.util.Log, android.graphics.Bitmap and so on. +// Setting the weak attribute tells the linker that this symbol is loaded at runtime and avoids +// a missing symbol error. +namespace android { +struct AndroidRuntime { + static int startReg(JNIEnv* env) __attribute__((weak)); +}; +} // namespace android // Main entry point +extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) { + // Load Android-native jni bindings + jni::JNIEnv& env = jni::GetEnv(*vm, jni::jni_version_1_6); + android::AndroidRuntime::startReg(&env); -extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { // Load the main library jni bindings mbgl::Log::Info(mbgl::Event::JNI, "Registering main JNI Methods"); mbgl::android::registerNatives(vm); @@ -80,22 +69,8 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { // Load the test library jni bindings mbgl::Log::Info(mbgl::Event::JNI, "Registering test JNI Methods"); - jni::JNIEnv& env = jni::GetEnv(*vm, jni::jni_version_1_6); - jni::RegisterNatives(env, jni::Class<Main>::Find(env), jni::MakeNativeMethod<decltype(Main::runAllTests), &Main::runAllTests>("runAllTests")); - // Bindings for system classes - struct Log { static constexpr auto Name() { return "android/util/Log"; } }; - try { - jni::RegisterNatives(env, jni::Class<Log>::Find(env), - MAKE_NATIVE_METHOD(isLoggable, "(Ljava/lang/String;I)Z"), - MAKE_NATIVE_METHOD(logger_entry_max_payload_native, "()I"), - MAKE_NATIVE_METHOD(println_native, "(IILjava/lang/String;Ljava/lang/String;)I") - ); - } catch (jni::PendingJavaException ex) { - env.ThrowNew(jni::JavaErrorClass(env), "Could not register Log mocks"); - } - return JNI_VERSION_1_6; } diff --git a/platform/android/src/timer.cpp b/platform/android/src/timer.cpp index 2eb003b2bd..2d9ee49e9b 100644 --- a/platform/android/src/timer.cpp +++ b/platform/android/src/timer.cpp @@ -23,7 +23,7 @@ public: repeat = repeat_; task = std::move(task_); - //Prevent overflows when timeout is set to Duration::max() + // Prevent overflows when timeout is set to Duration::max() due = (timeout == Duration::max()) ? std::chrono::time_point<Clock>::max() : Clock::now() + timeout; loop->addRunnable(this); } |