From 4d7785574d47d71fd2f4868cf405c6e2577b20f6 Mon Sep 17 00:00:00 2001 From: Matt Blair Date: Wed, 9 Sep 2020 23:12:59 -0400 Subject: [PATCH 1/3] Update Gradle Android build plugin to 4.0.1 --- platforms/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/android/build.gradle b/platforms/android/build.gradle index 364d3bc6c9..a3ee4dca35 100644 --- a/platforms/android/build.gradle +++ b/platforms/android/build.gradle @@ -5,7 +5,7 @@ allprojects { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.0' + classpath 'com.android.tools.build:gradle:4.0.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' } } From 086eda375be01d4e52793718991b87bf58bf28d0 Mon Sep 17 00:00:00 2001 From: Matt Blair Date: Sat, 12 Sep 2020 02:29:02 -0400 Subject: [PATCH 2/3] Refactor Android JNI methods --- platforms/android/config.cmake | 7 +- .../tangram/src/main/cpp/AndroidMap.cpp | 150 +++++ .../android/tangram/src/main/cpp/AndroidMap.h | 26 + .../tangram/src/main/cpp/AndroidPlatform.cpp | 299 +++++++++ .../tangram/src/main/cpp/AndroidPlatform.h | 55 ++ .../tangram/src/main/cpp/JniHelpers.cpp | 24 + .../android/tangram/src/main/cpp/JniHelpers.h | 23 + .../tangram/src/main/cpp/JniOnLoad.cpp | 27 + ...{jniThreadBinding.h => JniThreadBinding.h} | 0 .../src/main/cpp/{jniWorker.h => JniWorker.h} | 0 .../tangram/src/main/cpp/NativeMap.cpp | 538 +++++++++++++++ .../tangram/src/main/cpp/androidPlatform.cpp | 474 ------------- .../tangram/src/main/cpp/androidPlatform.h | 76 --- .../tangram/src/main/cpp/jniExports.cpp | 626 ------------------ .../com/mapzen/tangram/MapController.java | 238 ++----- .../java/com/mapzen/tangram/MapRenderer.java | 44 +- .../java/com/mapzen/tangram/NativeMap.java | 81 +++ 17 files changed, 1303 insertions(+), 1385 deletions(-) create mode 100644 platforms/android/tangram/src/main/cpp/AndroidMap.cpp create mode 100644 platforms/android/tangram/src/main/cpp/AndroidMap.h create mode 100644 platforms/android/tangram/src/main/cpp/AndroidPlatform.cpp create mode 100644 platforms/android/tangram/src/main/cpp/AndroidPlatform.h create mode 100644 platforms/android/tangram/src/main/cpp/JniHelpers.cpp create mode 100644 platforms/android/tangram/src/main/cpp/JniHelpers.h create mode 100644 platforms/android/tangram/src/main/cpp/JniOnLoad.cpp rename platforms/android/tangram/src/main/cpp/{jniThreadBinding.h => JniThreadBinding.h} (100%) rename platforms/android/tangram/src/main/cpp/{jniWorker.h => JniWorker.h} (100%) create mode 100644 platforms/android/tangram/src/main/cpp/NativeMap.cpp delete mode 100644 platforms/android/tangram/src/main/cpp/androidPlatform.cpp delete mode 100644 platforms/android/tangram/src/main/cpp/androidPlatform.h delete mode 100644 platforms/android/tangram/src/main/cpp/jniExports.cpp create mode 100644 platforms/android/tangram/src/main/java/com/mapzen/tangram/NativeMap.java diff --git a/platforms/android/config.cmake b/platforms/android/config.cmake index be38d92246..6603180c2f 100644 --- a/platforms/android/config.cmake +++ b/platforms/android/config.cmake @@ -11,8 +11,11 @@ endif() add_library(tangram SHARED platforms/common/platform_gl.cpp - platforms/android/tangram/src/main/cpp/jniExports.cpp - platforms/android/tangram/src/main/cpp/androidPlatform.cpp + platforms/android/tangram/src/main/cpp/JniHelpers.cpp + platforms/android/tangram/src/main/cpp/JniOnLoad.cpp + platforms/android/tangram/src/main/cpp/AndroidPlatform.cpp + platforms/android/tangram/src/main/cpp/AndroidMap.cpp + platforms/android/tangram/src/main/cpp/NativeMap.cpp ) if(TANGRAM_MBTILES_DATASOURCE) diff --git a/platforms/android/tangram/src/main/cpp/AndroidMap.cpp b/platforms/android/tangram/src/main/cpp/AndroidMap.cpp new file mode 100644 index 0000000000..eb21d89f93 --- /dev/null +++ b/platforms/android/tangram/src/main/cpp/AndroidMap.cpp @@ -0,0 +1,150 @@ +#include "AndroidMap.h" +#include "AndroidPlatform.h" +#include "JniHelpers.h" +#include "JniThreadBinding.h" +#include "data/properties.h" +#include "data/propertyItem.h" + +namespace Tangram { + +static jmethodID sceneReadyCallbackMID = nullptr; +static jmethodID cameraAnimationCallbackMID = nullptr; +static jmethodID featurePickCallbackMID = nullptr; +static jmethodID labelPickCallbackMID = nullptr; +static jmethodID markerPickCallbackMID = nullptr; + +static jclass hashMapClass = nullptr; +static jmethodID hashMapInitMID = nullptr; +static jmethodID hashMapPutMID = nullptr; + +void AndroidMap::jniOnLoad(JavaVM* javaVM, JNIEnv* jniEnv) { + // JNI OnLoad is invoked once when the native library is loaded so this is a good place to cache + // any method or class IDs that we'll need. + jclass mapControllerClass = jniEnv->FindClass("com/mapzen/tangram/MapController"); + sceneReadyCallbackMID = jniEnv->GetMethodID(mapControllerClass, "sceneReadyCallback", "(IILjava/lang/String;Ljava/lang/String;)V"); + cameraAnimationCallbackMID = jniEnv->GetMethodID(mapControllerClass, "cameraAnimationCallback", "(Z)V"); + featurePickCallbackMID = jniEnv->GetMethodID(mapControllerClass, "featurePickCallback", "(Ljava/util/Map;FF)V"); + labelPickCallbackMID = jniEnv->GetMethodID(mapControllerClass, "labelPickCallback", "(Ljava/util/Map;FFIDD)V"); + markerPickCallbackMID = jniEnv->GetMethodID(mapControllerClass, "markerPickCallback", "(JFFDD)V"); + + // We need a reference to the class object later to invoke the constructor. FindClass produces a + // local reference that may not be valid later, so create a global reference to the class. + hashMapClass = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("java/util/HashMap")); + hashMapInitMID = jniEnv->GetMethodID(hashMapClass, "", "()V"); + hashMapPutMID = jniEnv->GetMethodID(hashMapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); +} + +void AndroidMap::jniOnUnload(JavaVM* javaVM, JNIEnv* jniEnv) { + jniEnv->DeleteGlobalRef(hashMapClass); + hashMapClass = nullptr; +} + +AndroidMap::AndroidMap(JNIEnv* env, jobject mapController, jobject assetManager) + : Map(std::make_unique(env, mapController, assetManager)) { + + m_mapController = env->NewWeakGlobalRef(mapController); + + setSceneReadyListener([this](SceneID sceneId, const SceneError* sceneError) { + JniThreadBinding jniEnv(JniHelpers::getJVM()); + + jint jErrorType = -1; + jstring jUpdatePath = nullptr; + jstring jUpdateValue = nullptr; + + if (sceneError) { + jUpdatePath = JniHelpers::javaStringFromString(jniEnv, sceneError->update.path); + jUpdateValue = JniHelpers::javaStringFromString(jniEnv, sceneError->update.value); + jErrorType = (jint) sceneError->error; + } + + jniEnv->CallVoidMethod(m_mapController, sceneReadyCallbackMID, sceneId, jErrorType, + jUpdatePath, jUpdateValue); + }); + + setCameraAnimationListener([this](bool finished) { + JniThreadBinding jniEnv(JniHelpers::getJVM()); + + jniEnv->CallVoidMethod(m_mapController, cameraAnimationCallbackMID, finished); + }); +} + +void AndroidMap::pickFeature(float posX, float posY) { + + pickFeatureAt(posX, posY, [this](const FeaturePickResult* featurePickResult) { + JniThreadBinding jniEnv(JniHelpers::getJVM()); + + float x = 0.f, y = 0.f; + jobject hashMap = nullptr; + + if (featurePickResult) { + x = featurePickResult->position[0]; + y = featurePickResult->position[1]; + + hashMap = jniEnv->NewObject(hashMapClass, hashMapInitMID); + const auto& properties = featurePickResult->properties; + for (const auto& item : properties->items()) { + jstring jkey = JniHelpers::javaStringFromString(jniEnv, item.key); + jstring jvalue = JniHelpers::javaStringFromString(jniEnv, properties->asString(item.value)); + jniEnv->CallObjectMethod(hashMap, hashMapPutMID, jkey, jvalue); + } + } + jniEnv->CallVoidMethod(m_mapController, featurePickCallbackMID, hashMap, x, y); + }); +} + +void AndroidMap::pickLabel(float posX, float posY) { + + pickLabelAt(posX, posY, [this](const LabelPickResult* labelPickResult) { + JniThreadBinding jniEnv(JniHelpers::getJVM()); + + float x = 0.f, y = 0.f; + double lng = 0., lat = 0.; + int type = 0; + jobject hashmap = nullptr; + + if (labelPickResult) { + x = labelPickResult->touchItem.position[0]; + y = labelPickResult->touchItem.position[1]; + lng = labelPickResult->coordinates.longitude; + lat = labelPickResult->coordinates.latitude; + type = labelPickResult->type; + + hashmap = jniEnv->NewObject(hashMapClass, hashMapInitMID); + const auto& properties = labelPickResult->touchItem.properties; + for (const auto& item : properties->items()) { + jstring jkey = JniHelpers::javaStringFromString(jniEnv, item.key); + jstring jvalue = JniHelpers::javaStringFromString(jniEnv, properties->asString(item.value)); + jniEnv->CallObjectMethod(hashmap, hashMapPutMID, jkey, jvalue); + } + } + jniEnv->CallVoidMethod(m_mapController, labelPickCallbackMID, hashmap, x, y, type, lng, + lat); + }); +} + +void AndroidMap::pickMarker(float posX, float posY) { + + pickMarkerAt(posX, posY, [this](const MarkerPickResult* markerPickResult) { + JniThreadBinding jniEnv(JniHelpers::getJVM()); + + float x = 0.f, y = 0.f; + double lng = 0., lat = 0.; + jlong markerID = 0; + + if (markerPickResult) { + x = markerPickResult->position[0]; + y = markerPickResult->position[1]; + lng = markerPickResult->coordinates.longitude; + lat = markerPickResult->coordinates.latitude; + markerID = markerPickResult->id; + } + + jniEnv->CallVoidMethod(m_mapController, markerPickCallbackMID, markerID, x, y, lng, lat); + }); +} + +AndroidPlatform& AndroidMap::androidPlatform() { + return static_cast(getPlatform()); +} + +} // namespace Tangram diff --git a/platforms/android/tangram/src/main/cpp/AndroidMap.h b/platforms/android/tangram/src/main/cpp/AndroidMap.h new file mode 100644 index 0000000000..67e4d41e39 --- /dev/null +++ b/platforms/android/tangram/src/main/cpp/AndroidMap.h @@ -0,0 +1,26 @@ +#pragma once + +#include "map.h" +#include + +namespace Tangram { + +class AndroidPlatform; + +class AndroidMap : public Map { +public: + AndroidMap(JNIEnv* env, jobject mapController, jobject assetManager); + void pickFeature(float posX, float posY); + void pickMarker(float posX, float posY); + void pickLabel(float posX, float posY); + + AndroidPlatform& androidPlatform(); + + static void jniOnLoad(JavaVM* javaVM, JNIEnv* jniEnv); + static void jniOnUnload(JavaVM* javaVM,JNIEnv* jniEnv); + +protected: + jobject m_mapController; +}; + +} // namespace Tangram diff --git a/platforms/android/tangram/src/main/cpp/AndroidPlatform.cpp b/platforms/android/tangram/src/main/cpp/AndroidPlatform.cpp new file mode 100644 index 0000000000..f891f55a66 --- /dev/null +++ b/platforms/android/tangram/src/main/cpp/AndroidPlatform.cpp @@ -0,0 +1,299 @@ +#include "AndroidPlatform.h" + +#include "JniHelpers.h" +#include "JniThreadBinding.h" + +#include "log.h" +#include "util/url.h" + +#ifndef GL_GLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES 1 +#endif + +#include +#include +#include +#include +#include +#include +#include // dlopen, dlsym +#include +#include +#include + +#ifdef TANGRAM_MBTILES_DATASOURCE +#include "sqlite3ndk.h" +#endif + +PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT = 0; +PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT = 0; +PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT = 0; + +namespace Tangram { + +/* Followed the following document for JavaVM tips when used with native threads + * http://android.wooyd.org/JNIExample/#NWD1sCYeT-I + * http://developer.android.com/training/articles/perf-jni.html and + * http://www.ibm.com/developerworks/library/j-jni/ + * http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html + */ + +// JNI Env bound on androids render thread (our native main thread) +static jmethodID requestRenderMethodID = nullptr; +static jmethodID setRenderModeMethodID = nullptr; +static jmethodID startUrlRequestMID = nullptr; +static jmethodID cancelUrlRequestMID = nullptr; +static jmethodID getFontFilePath = nullptr; +static jmethodID getFontFallbackFilePath = nullptr; + +static bool glExtensionsLoaded = false; + +void AndroidPlatform::jniOnLoad(JavaVM* javaVM, JNIEnv* jniEnv) { + // JNI OnLoad is invoked once when the native library is loaded so this is a good place to cache + // any method or class IDs that we'll need. + jclass tangramClass = jniEnv->FindClass("com/mapzen/tangram/MapController"); + startUrlRequestMID = jniEnv->GetMethodID(tangramClass, "startUrlRequest", "(Ljava/lang/String;J)V"); + cancelUrlRequestMID = jniEnv->GetMethodID(tangramClass, "cancelUrlRequest", "(J)V"); + getFontFilePath = jniEnv->GetMethodID(tangramClass, "getFontFilePath", "(Ljava/lang/String;)Ljava/lang/String;"); + getFontFallbackFilePath = jniEnv->GetMethodID(tangramClass, "getFontFallbackFilePath", "(II)Ljava/lang/String;"); + requestRenderMethodID = jniEnv->GetMethodID(tangramClass, "requestRender", "()V"); + setRenderModeMethodID = jniEnv->GetMethodID(tangramClass, "setRenderMode", "(I)V"); +} + +void logMsg(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + __android_log_vprint(ANDROID_LOG_DEBUG, "Tangram", fmt, args); + va_end(args); +} + +AndroidPlatform::AndroidPlatform(JNIEnv* jniEnv, jobject mapController, jobject assetManager) + : m_jniWorker(JniHelpers::getJVM()) { + + m_mapController = jniEnv->NewWeakGlobalRef(mapController); + + m_assetManager = AAssetManager_fromJava(jniEnv, assetManager); + + if (m_assetManager == nullptr) { + LOGE("Could not obtain Asset Manager reference"); + return; + } + +#ifdef TANGRAM_MBTILES_DATASOURCE + sqlite3_ndk_init(m_assetManager); +#endif +} + +void AndroidPlatform::shutdown() { + Platform::shutdown(); + m_jniWorker.stop(); +} + +std::string AndroidPlatform::fontPath(const std::string& family, const std::string& weight, const std::string& style) const { + + JniThreadBinding jniEnv(JniHelpers::getJVM()); + + std::string key = family + "_" + weight + "_" + style; + + jstring jkey = JniHelpers::javaStringFromString(jniEnv, key); + jstring returnStr = (jstring) jniEnv->CallObjectMethod(m_mapController, getFontFilePath, jkey); + + auto resultStr = JniHelpers::stringFromJavaString(jniEnv, returnStr); + jniEnv->DeleteLocalRef(returnStr); + jniEnv->DeleteLocalRef(jkey); + + return resultStr; +} + +void AndroidPlatform::requestRender() const { + m_jniWorker.enqueue([&](JNIEnv *jniEnv) { + jniEnv->CallVoidMethod(m_mapController, requestRenderMethodID); + }); +} + +std::vector AndroidPlatform::systemFontFallbacksHandle() const { + JniThreadBinding jniEnv(JniHelpers::getJVM()); + + std::vector handles; + + int importance = 0; + int weightHint = 400; + + auto fontFallbackPath = [&](int _importance, int _weightHint) { + + jstring returnStr = (jstring) jniEnv->CallObjectMethod(m_mapController, + getFontFallbackFilePath, _importance, + _weightHint); + + auto resultStr = JniHelpers::stringFromJavaString(jniEnv, returnStr); + jniEnv->DeleteLocalRef(returnStr); + + return resultStr; + }; + + std::string fallbackPath = fontFallbackPath(importance, weightHint); + + while (!fallbackPath.empty()) { + handles.emplace_back(Url(fallbackPath)); + + fallbackPath = fontFallbackPath(++importance, weightHint); + } + + return handles; +} + +FontSourceHandle AndroidPlatform::systemFont(const std::string& name, const std::string& weight, + const std::string& face) const { + std::string path = fontPath(name, weight, face); + + if (path.empty()) { return {}; } + + auto data = bytesFromFile(path.c_str()); + + return FontSourceHandle([data]() { return data; }); +} + +void AndroidPlatform::setContinuousRendering(bool isContinuous) { + Platform::setContinuousRendering(isContinuous); + + JniThreadBinding jniEnv(JniHelpers::getJVM()); + + jniEnv->CallVoidMethod(m_mapController, setRenderModeMethodID, isContinuous ? 1 : 0); +} + +bool AndroidPlatform::bytesFromAssetManager(const char* path, std::function allocator) const { + + AAsset* asset = AAssetManager_open(m_assetManager, path, AASSET_MODE_UNKNOWN); + if (asset == nullptr) { + LOGW("Failed to open asset at path: %s", path); + return false; + } + + size_t size = AAsset_getLength(asset); + unsigned char* data = reinterpret_cast(allocator(size)); + + int read = AAsset_read(asset, data, size); + if (read <= 0) { + LOGW("Failed to read asset at path: %s", path); + } + AAsset_close(asset); + + return read > 0; +} + +std::vector AndroidPlatform::bytesFromFile(const Url& url) const { + std::vector data; + + auto allocator = [&](size_t size) { + data.resize(size); + return data.data(); + }; + + auto path = url.path(); + + if (url.scheme() == "asset") { + // The asset manager doesn't like paths starting with '/'. + if (!path.empty() && path.front() == '/') { + path = path.substr(1); + } + bytesFromAssetManager(path.c_str(), allocator); + } else { + Platform::bytesFromFileSystem(path.c_str(), allocator); + } + + return data; +} + +bool AndroidPlatform::startUrlRequestImpl(const Url& url, const UrlRequestHandle request, UrlRequestId& id) { + + // If the requested URL does not use HTTP or HTTPS, retrieve it asynchronously. + if (!url.hasHttpScheme()) { + m_fileWorker.enqueue([=](){ + UrlResponse response; + response.content = bytesFromFile(url); + onUrlResponse(request, std::move(response)); + }); + return false; + } + + // We can use UrlRequestHandle to cancel requests. MapController handles the + // mapping between UrlRequestHandle and request object + id = request; + + m_jniWorker.enqueue([=](JNIEnv *jniEnv) { + jlong jRequestHandle = static_cast(request); + + // Make sure no one changed UrlRequestHandle from being uint64_t, + // so that it's safe to convert to jlong and back. + static_assert(sizeof(jlong) == sizeof(UrlRequestHandle), "Who changed UrlRequestHandle?!"); + static_assert(static_cast(std::numeric_limits::max()) == + static_cast(std::numeric_limits::max()), + "Cannot convert jlong to UrlRequestHandle!"); + + jstring jUrl = JniHelpers::javaStringFromString(jniEnv, url.string()); + + // Call the MapController method to start the URL request. + jniEnv->CallVoidMethod(m_mapController, startUrlRequestMID, jUrl, jRequestHandle); + jniEnv->DeleteLocalRef(jUrl); + }); + + return true; +} + +void AndroidPlatform::cancelUrlRequestImpl(const UrlRequestId id) { + + m_jniWorker.enqueue([=](JNIEnv *jniEnv) { + + jlong jRequestHandle = static_cast(id); + + jniEnv->CallVoidMethod(m_mapController, cancelUrlRequestMID, jRequestHandle); + }); +} + +void AndroidPlatform::onUrlComplete(JNIEnv* _jniEnv, jlong _jRequestHandle, jbyteArray _jBytes, jstring _jError) { + // Start building a response object. + UrlResponse response; + + // If the request was successful, we will receive a non-null byte array. + if (_jBytes != nullptr) { + size_t length = _jniEnv->GetArrayLength(_jBytes); + response.content.resize(length); + _jniEnv->GetByteArrayRegion(_jBytes, 0, length, reinterpret_cast(response.content.data())); + // TODO: Can we use a DirectByteBuffer to transfer data with fewer copies? + } + + // If the request was unsuccessful, we will receive a non-null error string. + std::string error; + if (_jError != nullptr) { + error = JniHelpers::stringFromJavaString(_jniEnv, _jError); + response.error = error.c_str(); + } + + // Handle callbacks on worker thread to not block Java side. + // (The calling thread has probably also other work to do) + m_fileWorker.enqueue([this, _jRequestHandle, r = std::move(response)]() mutable { + UrlRequestHandle requestHandle = static_cast(_jRequestHandle); + + onUrlResponse(requestHandle, std::move(r)); + }); +} + +void setCurrentThreadPriority(int priority) { + setpriority(PRIO_PROCESS, 0, priority); +} + +void initGLExtensions() { + if (glExtensionsLoaded) { + return; + } + + void* libhandle = dlopen("libGLESv2.so", RTLD_LAZY); + + glBindVertexArrayOESEXT = (PFNGLBINDVERTEXARRAYOESPROC) dlsym(libhandle, "glBindVertexArrayOES"); + glDeleteVertexArraysOESEXT = (PFNGLDELETEVERTEXARRAYSOESPROC) dlsym(libhandle, "glDeleteVertexArraysOES"); + glGenVertexArraysOESEXT = (PFNGLGENVERTEXARRAYSOESPROC) dlsym(libhandle, "glGenVertexArraysOES"); + + glExtensionsLoaded = true; +} + +} // namespace Tangram diff --git a/platforms/android/tangram/src/main/cpp/AndroidPlatform.h b/platforms/android/tangram/src/main/cpp/AndroidPlatform.h new file mode 100644 index 0000000000..b84c88a7b7 --- /dev/null +++ b/platforms/android/tangram/src/main/cpp/AndroidPlatform.h @@ -0,0 +1,55 @@ +#pragma once + +#include "platform.h" +#include "JniWorker.h" +#include "util/asyncWorker.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace Tangram { + +struct LabelPickResult; +struct FeaturePickResult; +struct MarkerPickResult; +class Map; +struct SceneUpdate; +struct SceneError; +using SceneID = int32_t; + +class AndroidPlatform : public Platform { + +public: + + AndroidPlatform(JNIEnv* jniEnv, jobject mapController, jobject assetManager); + void shutdown() override; + void requestRender() const override; + void setContinuousRendering(bool isContinuous) override; + FontSourceHandle systemFont(const std::string& name, const std::string& weight, const std::string& face) const override; + std::vector systemFontFallbacksHandle() const override; + bool startUrlRequestImpl(const Url& url, const UrlRequestHandle request, UrlRequestId& id) override; + void cancelUrlRequestImpl(const UrlRequestId id) override; + + void onUrlComplete(JNIEnv* jniEnv, jlong jRequestHandle, jbyteArray jBytes, jstring jError); + + static void jniOnLoad(JavaVM* javaVM, JNIEnv* jniEnv); + +private: + + std::vector bytesFromFile(const Url& url) const; + bool bytesFromAssetManager(const char* path, std::function allocator) const; + std::string fontPath(const std::string& family, const std::string& weight, const std::string& style) const; + + jobject m_mapController; + AAssetManager* m_assetManager; + + mutable JniWorker m_jniWorker; + AsyncWorker m_fileWorker; +}; + +} // namespace Tangram diff --git a/platforms/android/tangram/src/main/cpp/JniHelpers.cpp b/platforms/android/tangram/src/main/cpp/JniHelpers.cpp new file mode 100644 index 0000000000..075feb6c58 --- /dev/null +++ b/platforms/android/tangram/src/main/cpp/JniHelpers.cpp @@ -0,0 +1,24 @@ +#include "JniHelpers.h" +#include +#include + +namespace Tangram { + +JavaVM* JniHelpers::s_jvm = nullptr; + +std::string JniHelpers::stringFromJavaString(JNIEnv* jniEnv, jstring javaString) { + auto length = jniEnv->GetStringLength(javaString); + std::u16string chars(length, char16_t()); + if(!chars.empty()) { + jniEnv->GetStringRegion(javaString, 0, length, reinterpret_cast(&chars[0])); + } + return std::wstring_convert, char16_t>().to_bytes(chars); +} + +jstring JniHelpers::javaStringFromString(JNIEnv* jniEnv, const std::string& string) { + auto chars = std::wstring_convert, char16_t>().from_bytes(string); + auto s = reinterpret_cast(chars.empty() ? u"" : chars.data()); + return jniEnv->NewString(s, chars.length()); +} + +} // namespace Tangram diff --git a/platforms/android/tangram/src/main/cpp/JniHelpers.h b/platforms/android/tangram/src/main/cpp/JniHelpers.h new file mode 100644 index 0000000000..bd3336beae --- /dev/null +++ b/platforms/android/tangram/src/main/cpp/JniHelpers.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace Tangram { + +class JniHelpers { + +public: + + static void jniOnLoad(JavaVM* jvm) { s_jvm = jvm; } + static JavaVM* getJVM() { return s_jvm; } + + static std::string stringFromJavaString(JNIEnv* jniEnv, jstring javaString); + static jstring javaStringFromString(JNIEnv* jniEnv, const std::string& string); + +protected: + + static JavaVM* s_jvm; +}; + +} // namespace Tangram diff --git a/platforms/android/tangram/src/main/cpp/JniOnLoad.cpp b/platforms/android/tangram/src/main/cpp/JniOnLoad.cpp new file mode 100644 index 0000000000..9d673da3b7 --- /dev/null +++ b/platforms/android/tangram/src/main/cpp/JniOnLoad.cpp @@ -0,0 +1,27 @@ +#include "AndroidMap.h" +#include "AndroidPlatform.h" +#include "JniHelpers.h" + +#define TANGRAM_JNI_VERSION JNI_VERSION_1_6 + +extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* javaVM, void*) { + JNIEnv* jniEnv = nullptr; + if (javaVM->GetEnv(reinterpret_cast(&jniEnv), TANGRAM_JNI_VERSION) != JNI_OK) { + return -1; + } + + Tangram::JniHelpers::jniOnLoad(javaVM); + Tangram::AndroidPlatform::jniOnLoad(javaVM, jniEnv); + Tangram::AndroidMap::jniOnLoad(javaVM, jniEnv); + + return TANGRAM_JNI_VERSION; +} + +extern "C" JNIEXPORT void JNI_OnUnload(JavaVM* javaVM, void*) { + JNIEnv* jniEnv = nullptr; + if (javaVM->GetEnv(reinterpret_cast(&jniEnv), TANGRAM_JNI_VERSION) != JNI_OK) { + return; + } + + Tangram::AndroidMap::jniOnUnload(javaVM, jniEnv); +} \ No newline at end of file diff --git a/platforms/android/tangram/src/main/cpp/jniThreadBinding.h b/platforms/android/tangram/src/main/cpp/JniThreadBinding.h similarity index 100% rename from platforms/android/tangram/src/main/cpp/jniThreadBinding.h rename to platforms/android/tangram/src/main/cpp/JniThreadBinding.h diff --git a/platforms/android/tangram/src/main/cpp/jniWorker.h b/platforms/android/tangram/src/main/cpp/JniWorker.h similarity index 100% rename from platforms/android/tangram/src/main/cpp/jniWorker.h rename to platforms/android/tangram/src/main/cpp/JniWorker.h diff --git a/platforms/android/tangram/src/main/cpp/NativeMap.cpp b/platforms/android/tangram/src/main/cpp/NativeMap.cpp new file mode 100644 index 0000000000..b23e921f24 --- /dev/null +++ b/platforms/android/tangram/src/main/cpp/NativeMap.cpp @@ -0,0 +1,538 @@ +#include "AndroidMap.h" +#include "AndroidPlatform.h" +#include "JniHelpers.h" +#include "JniThreadBinding.h" +#include +#include + +namespace Tangram { + +AndroidMap* androidMapFromJava(JNIEnv* env, jobject nativeMapObject) { + static jclass nativeMapClass = env->FindClass("com/mapzen/tangram/NativeMap"); + static jfieldID nativePointerFID = env->GetFieldID(nativeMapClass, "nativePointer", "J"); + jlong nativePointer = env->GetLongField(nativeMapObject, nativePointerFID); + assert(nativePointer > 0); + return reinterpret_cast(nativePointer); +} + +std::vector unpackSceneUpdates(JNIEnv* jniEnv, jobjectArray updateStrings) { + int nUpdateStrings = (updateStrings == nullptr)? 0 : jniEnv->GetArrayLength(updateStrings); + + std::vector sceneUpdates; + for (int i = 0; i < nUpdateStrings;) { + auto path = (jstring) (jniEnv->GetObjectArrayElement(updateStrings, i++)); + auto value = (jstring) (jniEnv->GetObjectArrayElement(updateStrings, i++)); + sceneUpdates.emplace_back(JniHelpers::stringFromJavaString(jniEnv, path), JniHelpers::stringFromJavaString(jniEnv, value)); + jniEnv->DeleteLocalRef(path); + jniEnv->DeleteLocalRef(value); + } + return sceneUpdates; +} + +extern "C" { + +#define NATIVE_METHOD(NAME) JNIEXPORT JNICALL Java_com_mapzen_tangram_NativeMap_ ## NAME + +jlong NATIVE_METHOD(init)(JNIEnv* env, jobject obj, jobject mapController, jobject assetManager) { + auto map = new AndroidMap(env, mapController, assetManager); + return reinterpret_cast(map); +} + +void NATIVE_METHOD(dispose)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + delete map; +} + +void NATIVE_METHOD(shutdown)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + map->getPlatform().shutdown(); +} + +void NATIVE_METHOD(onLowMemory)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + map->onMemoryWarning(); +} + +jint NATIVE_METHOD(loadScene)(JNIEnv* env, jobject obj, jstring path, + jobjectArray updateStrings) { + auto* map = androidMapFromJava(env, obj); + + auto cPath = JniHelpers::stringFromJavaString(env, path); + + auto sceneUpdates = unpackSceneUpdates(env, updateStrings); + Url sceneUrl = Url(cPath).resolved("asset:///"); + jint sceneId = map->loadScene(sceneUrl.string(), false, sceneUpdates); + + return sceneId; +} + +jint NATIVE_METHOD(loadSceneAsync)(JNIEnv* env, jobject obj, jstring path, + jobjectArray updateStrings) { + auto* map = androidMapFromJava(env, obj); + + auto cPath = JniHelpers::stringFromJavaString(env, path); + + auto sceneUpdates = unpackSceneUpdates(env, updateStrings); + Url sceneUrl = Url(cPath).resolved("asset:///"); + jint sceneId = map->loadSceneAsync(sceneUrl.string(), false, sceneUpdates); + + return sceneId; +} + +jint NATIVE_METHOD(loadSceneYaml)(JNIEnv* env, jobject obj, jstring yaml, jstring path, + jobjectArray updateStrings) { + auto* map = androidMapFromJava(env, obj); + + auto cYaml = JniHelpers::stringFromJavaString(env, yaml); + auto cPath = JniHelpers::stringFromJavaString(env, path); + + auto sceneUpdates = unpackSceneUpdates(env, updateStrings); + Url sceneUrl = Url(cPath).resolved("asset:///"); + jint sceneId = map->loadSceneYaml(cYaml, sceneUrl.string(), false, sceneUpdates); + + return sceneId; +} + +jint NATIVE_METHOD(loadSceneYamlAsync)(JNIEnv* env, jobject obj, jstring yaml, jstring path, + jobjectArray updateStrings) { + auto* map = androidMapFromJava(env, obj); + + auto cYaml = JniHelpers::stringFromJavaString(env, yaml); + auto cPath = JniHelpers::stringFromJavaString(env, path); + + auto sceneUpdates = unpackSceneUpdates(env, updateStrings); + Url sceneUrl = Url(cPath).resolved("asset:///"); + jint sceneId = map->loadSceneYamlAsync(cYaml, sceneUrl.string(), false, sceneUpdates); + + return sceneId; +} + +void NATIVE_METHOD(setupGL)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + map->setupGL(); +} + +void NATIVE_METHOD(resize)(JNIEnv* env, jobject obj, jint width, jint height) { + auto* map = androidMapFromJava(env, obj); + map->resize(width, height); +} + +jint NATIVE_METHOD(update)(JNIEnv* env, jobject obj, jfloat dt) { + auto* map = androidMapFromJava(env, obj); + auto result = map->update(dt); + return static_cast(result.flags); +} + +void NATIVE_METHOD(render)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + map->render(); +} + +void NATIVE_METHOD(captureSnapshot)(JNIEnv* env, jobject obj, jintArray buffer) { + auto* map = androidMapFromJava(env, obj); + jint* ptr = env->GetIntArrayElements(buffer, nullptr); + auto* data = reinterpret_cast(ptr); + map->captureSnapshot(data); + env->ReleaseIntArrayElements(buffer, ptr, 0); +} + +void NATIVE_METHOD(getCameraPosition)(JNIEnv* env, jobject obj, jdoubleArray lonLat, + jfloatArray zoomRotationTilt) { + auto* map = androidMapFromJava(env, obj); + jdouble* pos = env->GetDoubleArrayElements(lonLat, nullptr); + jfloat* zrt = env->GetFloatArrayElements(zoomRotationTilt, nullptr); + + auto camera = map->getCameraPosition(); + pos[0] = camera.longitude; + pos[1] = camera.latitude; + zrt[0] = camera.zoom; + zrt[1] = camera.rotation; + zrt[2] = camera.tilt; + + env->ReleaseDoubleArrayElements(lonLat, pos, 0); + env->ReleaseFloatArrayElements(zoomRotationTilt, zrt, 0); +} + +void NATIVE_METHOD(updateCameraPosition)(JNIEnv* env, jobject obj, + jint set, jdouble lon, jdouble lat, + jfloat zoom, jfloat zoomBy, + jfloat rotation, jfloat rotateBy, + jfloat tilt, jfloat tiltBy, + jdouble b1lon, jdouble b1lat, + jdouble b2lon, jdouble b2lat, + jintArray jpad, jfloat duration, jint ease) { + auto* map = androidMapFromJava(env, obj); + + CameraUpdate update; + update.set = set; + + update.lngLat = LngLat{lon,lat}; + update.zoom = zoom; + update.zoomBy = zoomBy; + update.rotation = rotation; + update.rotationBy = rotateBy; + update.tilt = tilt; + update.tiltBy = tiltBy; + update.bounds = std::array{{LngLat{b1lon, b1lat}, LngLat{b2lon, b2lat}}}; + if (jpad != nullptr) { + jint* jpadArray = env->GetIntArrayElements(jpad, nullptr); + update.padding = EdgePadding{jpadArray[0], jpadArray[1], jpadArray[2], jpadArray[3]}; + env->ReleaseIntArrayElements(jpad, jpadArray, JNI_ABORT); + } + map->updateCameraPosition(update, duration, static_cast(ease)); +} + +void NATIVE_METHOD(getEnclosingCameraPosition)(JNIEnv* env, jobject obj, + jdouble aLng, jdouble aLat, + jdouble bLng, jdouble bLat, + jintArray jpad, jdoubleArray lngLatZoom) { + auto* map = androidMapFromJava(env, obj); + + EdgePadding padding; + if (jpad != nullptr) { + jint* jpadArray = env->GetIntArrayElements(jpad, nullptr); + padding = EdgePadding(jpadArray[0], jpadArray[1], jpadArray[2], jpadArray[3]); + env->ReleaseIntArrayElements(jpad, jpadArray, JNI_ABORT); + } + CameraPosition camera = map->getEnclosingCameraPosition(LngLat{aLng,aLat}, LngLat{bLng,bLat}, padding); + jdouble* arr = env->GetDoubleArrayElements(lngLatZoom, nullptr); + arr[0] = camera.longitude; + arr[1] = camera.latitude; + arr[2] = camera.zoom; + env->ReleaseDoubleArrayElements(lngLatZoom, arr, 0); +} + +void NATIVE_METHOD(flyTo)(JNIEnv* env, jobject obj, jdouble lon, jdouble lat, + jfloat zoom, jfloat duration, jfloat speed) { + auto* map = androidMapFromJava(env, obj); + + CameraPosition camera = map->getCameraPosition(); + camera.longitude = lon; + camera.latitude = lat; + camera.zoom = zoom; + map->flyTo(camera, duration, speed); +} + +void NATIVE_METHOD(cancelCameraAnimation)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + map->cancelCameraAnimation(); +} + +jboolean NATIVE_METHOD(screenPositionToLngLat)(JNIEnv* env, jobject obj, + jdoubleArray coordinates) { + auto* map = androidMapFromJava(env, obj); + + jdouble* arr = env->GetDoubleArrayElements(coordinates, nullptr); + bool result = map->screenPositionToLngLat(arr[0], arr[1], &arr[0], &arr[1]); + env->ReleaseDoubleArrayElements(coordinates, arr, 0); + return static_cast(result); +} + +jboolean NATIVE_METHOD(lngLatToScreenPosition)(JNIEnv* env, jobject obj, + jdoubleArray coordinates, jboolean clipToViewport) { + auto* map = androidMapFromJava(env, obj); + + jdouble* arr = env->GetDoubleArrayElements(coordinates, nullptr); + bool result = map->lngLatToScreenPosition(arr[0], arr[1], &arr[0], &arr[1], clipToViewport); + env->ReleaseDoubleArrayElements(coordinates, arr, 0); + return static_cast(result); +} + + + +void NATIVE_METHOD(setPixelScale)(JNIEnv* env, jobject obj, jfloat scale) { + auto* map = androidMapFromJava(env, obj); + map->setPixelScale(scale); +} + +void NATIVE_METHOD(setCameraType)(JNIEnv* env, jobject obj, jint type) { + auto* map = androidMapFromJava(env, obj); + map->setCameraType(type); +} + +jint NATIVE_METHOD(getCameraType)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + return map->getCameraType(); +} + +jfloat NATIVE_METHOD(getMinZoom)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + return map->getMinZoom(); +} + +void NATIVE_METHOD(setMinZoom)(JNIEnv* env, jobject obj, jfloat minZoom) { + auto* map = androidMapFromJava(env, obj); + map->setMinZoom(minZoom); +} + +jfloat NATIVE_METHOD(getMaxZoom)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + return map->getMaxZoom(); +} + +void NATIVE_METHOD(setMaxZoom)(JNIEnv* env, jobject obj, jfloat maxZoom) { + auto* map = androidMapFromJava(env, obj); + map->setMaxZoom(maxZoom); +} + +void NATIVE_METHOD(handleTapGesture)(JNIEnv* env, jobject obj, jfloat posX, jfloat posY) { + auto* map = androidMapFromJava(env, obj); + map->handleTapGesture(posX, posY); +} + +void NATIVE_METHOD(handleDoubleTapGesture)(JNIEnv* env, jobject obj, jfloat posX, jfloat posY) { + auto* map = androidMapFromJava(env, obj); + map->handleDoubleTapGesture(posX, posY); +} + +void NATIVE_METHOD(handlePanGesture)(JNIEnv* env, jobject obj, jfloat startX, jfloat startY, + jfloat endX, jfloat endY) { + auto* map = androidMapFromJava(env, obj); + map->handlePanGesture(startX, startY, endX, endY); +} + +void NATIVE_METHOD(handleFlingGesture)(JNIEnv* env, jobject obj, jfloat posX, jfloat posY, + jfloat velocityX, jfloat velocityY) { + auto* map = androidMapFromJava(env, obj); + map->handleFlingGesture(posX, posY, velocityX, velocityY); +} + +void NATIVE_METHOD(handlePinchGesture)(JNIEnv* env, jobject obj, jfloat posX, jfloat posY, + jfloat scale, jfloat velocity) { + auto* map = androidMapFromJava(env, obj); + map->handlePinchGesture(posX, posY, scale, velocity); +} + +void NATIVE_METHOD(handleRotateGesture)(JNIEnv* env, jobject obj, jfloat posX, jfloat posY, + jfloat rotation) { + auto* map = androidMapFromJava(env, obj); + map->handleRotateGesture(posX, posY, rotation); +} + +void NATIVE_METHOD(handleShoveGesture)(JNIEnv* env, jobject obj, jfloat distance) { + auto* map = androidMapFromJava(env, obj); + map->handleShoveGesture(distance); +} + +void NATIVE_METHOD(onUrlComplete)(JNIEnv* env, jobject obj, jlong requestHandle, + jbyteArray fetchedBytes, jstring errorString) { + auto* map = androidMapFromJava(env, obj); + map->androidPlatform().onUrlComplete(env, requestHandle, fetchedBytes, errorString); +} + +void NATIVE_METHOD(setPickRadius)(JNIEnv* env, jobject obj, jfloat radius) { + auto* map = androidMapFromJava(env, obj); + map->setPickRadius(radius); +} + +void NATIVE_METHOD(pickFeature)(JNIEnv* env, jobject obj, jfloat posX, jfloat posY) { + auto* map = androidMapFromJava(env, obj); + map->pickFeature(posX, posY); + +} + +void NATIVE_METHOD(pickMarker)(JNIEnv* env, jobject obj, jfloat posX, jfloat posY) { + auto* map = androidMapFromJava(env, obj); + map->pickMarker(posX, posY); +} + +void NATIVE_METHOD(pickLabel)(JNIEnv* env, jobject obj, jfloat posX, jfloat posY) { + auto* map = androidMapFromJava(env, obj); + map->pickLabel(posX, posY); +} + +jlong NATIVE_METHOD(markerAdd)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + auto markerID = map->markerAdd(); + // unsigned int to jlong for precision... else we can do jint return + return static_cast(markerID); +} + +jboolean NATIVE_METHOD(markerRemove)(JNIEnv* env, jobject obj, jlong markerID) { + auto* map = androidMapFromJava(env, obj); + auto result = map->markerRemove(static_cast(markerID)); + return static_cast(result); +} + +jboolean NATIVE_METHOD(markerSetStylingFromString)(JNIEnv* env, jobject obj, jlong markerID, + jstring styling) { + auto* map = androidMapFromJava(env, obj); + + auto styleString = JniHelpers::stringFromJavaString(env, styling); + auto result = map->markerSetStylingFromString(static_cast(markerID), styleString.c_str()); + return static_cast(result); +} + +jboolean NATIVE_METHOD(markerSetStylingFromPath)(JNIEnv* env, jobject obj, jlong markerID, + jstring path) { + auto* map = androidMapFromJava(env, obj); + + auto pathString = JniHelpers::stringFromJavaString(env, path); + auto result = map->markerSetStylingFromPath(static_cast(markerID), pathString.c_str()); + return static_cast(result); +} + +jboolean NATIVE_METHOD(markerSetBitmap)(JNIEnv* env, jobject obj, jlong markerID, + jobject jbitmap, jfloat density) { + auto* map = androidMapFromJava(env, obj); + + AndroidBitmapInfo bitmapInfo; + if (AndroidBitmap_getInfo(env, jbitmap, &bitmapInfo) != ANDROID_BITMAP_RESULT_SUCCESS) { + return static_cast(false); + } + if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { + // TODO: Add different conversion functions for other formats. + return static_cast(false); + } + uint32_t* pixelInput; + if (AndroidBitmap_lockPixels(env, jbitmap, (void**)&pixelInput) != ANDROID_BITMAP_RESULT_SUCCESS) { + return static_cast(false); + } + int width = bitmapInfo.width; + int height = bitmapInfo.height; + uint32_t* pixelOutput = new uint32_t[height * width]; + int i = 0; + for (int row = 0; row < height; row++) { + // Flips image upside-down + int flippedRow = (height - 1 - row) * width; + for (int col = 0; col < width; col++) { + uint32_t pixel = pixelInput[i++]; + // Undo alpha pre-multiplication. + auto rgba = reinterpret_cast(&pixel); + int a = rgba[3]; + if (a != 0) { + auto alphaInv = 255.f/a; + rgba[0] = static_cast(rgba[0] * alphaInv); + rgba[1] = static_cast(rgba[1] * alphaInv); + rgba[2] = static_cast(rgba[2] * alphaInv); + } + pixelOutput[flippedRow + col] = pixel; + } + } + AndroidBitmap_unlockPixels(env, jbitmap); + auto result = map->markerSetBitmap(static_cast(markerID), width, height, pixelOutput, density); + delete[] pixelOutput; + return static_cast(result); +} + +jboolean NATIVE_METHOD(markerSetPoint)(JNIEnv* env, jobject obj, jlong markerID, + jdouble lng, jdouble lat) { + auto* map = androidMapFromJava(env, obj); + + auto result = map->markerSetPoint(static_cast(markerID), Tangram::LngLat(lng, lat)); + return static_cast(result); +} + +jboolean NATIVE_METHOD(markerSetPointEased)(JNIEnv* env, jobject obj, jlong markerID, + jdouble lng, jdouble lat, jfloat duration, jint ease) { + auto* map = androidMapFromJava(env, obj); + + auto result = map->markerSetPointEased(static_cast(markerID), + Tangram::LngLat(lng, lat), duration, + static_cast(ease)); + return static_cast(result); +} + +jboolean NATIVE_METHOD(markerSetPolyline)(JNIEnv* env, jobject obj, jlong markerID, + jdoubleArray jcoordinates, jint count) { + auto* map = androidMapFromJava(env, obj); + + if (!jcoordinates || count == 0) { return static_cast(false); } + + auto* coordinates = env->GetDoubleArrayElements(jcoordinates, nullptr); + std::vector polyline; + polyline.reserve(static_cast(count)); + + for (size_t i = 0; i < count; ++i) { + polyline.emplace_back(coordinates[2 * i], coordinates[2 * i + 1]); + } + + auto result = map->markerSetPolyline(static_cast(markerID), polyline.data(), count); + return static_cast(result); +} + +jboolean NATIVE_METHOD(markerSetPolygon)(JNIEnv* env, jobject obj, jlong markerID, + jdoubleArray jcoordinates, jintArray jcounts, jint rings) { + auto* map = androidMapFromJava(env, obj); + + if (!jcoordinates || !jcounts || rings == 0) { return static_cast(false); } + + auto* coordinates = env->GetDoubleArrayElements(jcoordinates, nullptr); + auto* counts = env->GetIntArrayElements(jcounts, nullptr); + + std::vector polygonCoords; + + int coordsCount = 0; + for (int i = 0; i < rings; i++) { + int ringCount = *(counts+i); + for (int j = 0; j < ringCount; j++) { + polygonCoords.emplace_back(coordinates[coordsCount + 2 * j], + coordinates[coordsCount + 2 * j + 1]); + } + coordsCount += ringCount; + } + + auto result = map->markerSetPolygon(static_cast(markerID), + polygonCoords.data(), counts, rings); + + return static_cast(result); +} + +jboolean NATIVE_METHOD(markerSetVisible)(JNIEnv* env, jobject obj, jlong markerID, + jboolean visible) { + auto* map = androidMapFromJava(env, obj); + + auto result = map->markerSetVisible(static_cast(markerID), visible); + return static_cast(result); +} + +jboolean NATIVE_METHOD(markerSetDrawOrder)(JNIEnv* env, jobject obj, jlong markerID, + jint drawOrder) { + auto* map = androidMapFromJava(env, obj); + + auto result = map->markerSetDrawOrder(markerID, drawOrder); + return static_cast(result); +} + +void NATIVE_METHOD(markerRemoveAll)(JNIEnv* env, jobject obj) { + auto* map = androidMapFromJava(env, obj); + map->markerRemoveAll(); +} + + +void NATIVE_METHOD(setDebugFlag)(JNIEnv* env, jobject obj, jint flag, jboolean on) { + Tangram::setDebugFlag(static_cast(flag), on); +} + +void NATIVE_METHOD(useCachedGlState)(JNIEnv* env, jobject obj, jboolean use) { + auto* map = androidMapFromJava(env, obj); + map->useCachedGlState(use); +} + +void NATIVE_METHOD(setDefaultBackgroundColor)(JNIEnv* env, jobject obj, + jfloat r, jfloat g, jfloat b) { + auto* map = androidMapFromJava(env, obj); + map->setDefaultBackgroundColor(r, g, b); +} + +jlong NATIVE_METHOD(addClientDataSource)(JNIEnv* env, jobject obj, + jstring name, jboolean generateCentroid) { + auto* map = androidMapFromJava(env, obj); + + auto sourceName = JniHelpers::stringFromJavaString(env, name); + auto source = std::make_shared(map->getPlatform(), + sourceName, "", + generateCentroid); + map->addTileSource(source); + return reinterpret_cast(source.get()); +} + +void NATIVE_METHOD(removeClientDataSource)(JNIEnv* env, jobject obj, jlong sourcePtr) { + auto* map = androidMapFromJava(env, obj); + auto* clientDataSource = reinterpret_cast(sourcePtr); + map->removeTileSource(*clientDataSource); +} + +} // extern "C" + +} // namespace Tangram diff --git a/platforms/android/tangram/src/main/cpp/androidPlatform.cpp b/platforms/android/tangram/src/main/cpp/androidPlatform.cpp deleted file mode 100644 index d7594df425..0000000000 --- a/platforms/android/tangram/src/main/cpp/androidPlatform.cpp +++ /dev/null @@ -1,474 +0,0 @@ -#include "androidPlatform.h" - -#include "jniThreadBinding.h" - -#include "data/properties.h" -#include "data/propertyItem.h" -#include "log.h" -#include "map.h" -#include "util/url.h" - -#ifndef GL_GLEXT_PROTOTYPES -#define GL_GLEXT_PROTOTYPES 1 -#endif - -#include -#include -#include -#include -#include -#include -#include // dlopen, dlsym -#include -#include -#include -#include -#include - -#ifdef TANGRAM_MBTILES_DATASOURCE -#include "sqlite3ndk.h" -#endif - -PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT = 0; -PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT = 0; -PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT = 0; - -#define TANGRAM_JNI_VERSION JNI_VERSION_1_6 - -namespace Tangram { - -/* Followed the following document for JavaVM tips when used with native threads - * http://android.wooyd.org/JNIExample/#NWD1sCYeT-I - * http://developer.android.com/training/articles/perf-jni.html and - * http://www.ibm.com/developerworks/library/j-jni/ - * http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html - */ - -static JavaVM* jvm = nullptr; -// JNI Env bound on androids render thread (our native main thread) -static jmethodID requestRenderMethodID = 0; -static jmethodID setRenderModeMethodID = 0; -static jmethodID startUrlRequestMID = 0; -static jmethodID cancelUrlRequestMID = 0; -static jmethodID getFontFilePath = 0; -static jmethodID getFontFallbackFilePath = 0; -static jmethodID featurePickCallbackMID = 0; -static jmethodID labelPickCallbackMID = 0; -static jmethodID markerPickCallbackMID = 0; -static jmethodID sceneReadyCallbackMID = 0; -static jmethodID cameraAnimationCallbackMID = 0; - -static jclass hashmapClass = nullptr; -static jmethodID hashmapInitMID = 0; -static jmethodID hashmapPutMID = 0; - -static bool glExtensionsLoaded = false; - - -void AndroidPlatform::bindJniEnvToThread(JNIEnv* jniEnv) { - jniEnv->GetJavaVM(&jvm); -} - -jint AndroidPlatform::jniOnLoad(JavaVM* javaVM) { - // JNI OnLoad is invoked once when the native library is loaded so this is a good place to cache - // any method or class IDs that we'll need. - - jvm = javaVM; - JNIEnv* jniEnv = nullptr; - if (javaVM->GetEnv(reinterpret_cast(&jniEnv), TANGRAM_JNI_VERSION) != JNI_OK) { - return -1; - } - - jclass tangramClass = jniEnv->FindClass("com/mapzen/tangram/MapController"); - startUrlRequestMID = jniEnv->GetMethodID(tangramClass, "startUrlRequest", "(Ljava/lang/String;J)V"); - cancelUrlRequestMID = jniEnv->GetMethodID(tangramClass, "cancelUrlRequest", "(J)V"); - getFontFilePath = jniEnv->GetMethodID(tangramClass, "getFontFilePath", "(Ljava/lang/String;)Ljava/lang/String;"); - getFontFallbackFilePath = jniEnv->GetMethodID(tangramClass, "getFontFallbackFilePath", "(II)Ljava/lang/String;"); - requestRenderMethodID = jniEnv->GetMethodID(tangramClass, "requestRender", "()V"); - setRenderModeMethodID = jniEnv->GetMethodID(tangramClass, "setRenderMode", "(I)V"); - sceneReadyCallbackMID = jniEnv->GetMethodID(tangramClass, "sceneReadyCallback", "(IILjava/lang/String;Ljava/lang/String;)V"); - cameraAnimationCallbackMID = jniEnv->GetMethodID(tangramClass, "cameraAnimationCallback", "(Z)V"); - featurePickCallbackMID = jniEnv->GetMethodID(tangramClass, "featurePickCallback", "(Ljava/util/Map;FF)V"); - labelPickCallbackMID = jniEnv->GetMethodID(tangramClass, "labelPickCallback", "(Ljava/util/Map;FFIDD)V"); - markerPickCallbackMID = jniEnv->GetMethodID(tangramClass, "markerPickCallback", "(JFFDD)V"); - - // We need a reference to the class object later to invoke the constructor. FindClass produces a - // local reference that may not be valid later, so create a global reference to the class. - hashmapClass = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("java/util/HashMap")); - hashmapInitMID = jniEnv->GetMethodID(hashmapClass, "", "()V"); - hashmapPutMID = jniEnv->GetMethodID(hashmapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); - - return TANGRAM_JNI_VERSION; -} - -void AndroidPlatform::jniOnUnload(JavaVM *javaVM) { - JNIEnv* jniEnv = nullptr; - if (javaVM->GetEnv(reinterpret_cast(&jniEnv), TANGRAM_JNI_VERSION) != JNI_OK) { - return; - } - jniEnv->DeleteGlobalRef(hashmapClass); - hashmapClass = nullptr; - - jvm = nullptr; -} - -std::string stringFromJString(JNIEnv* jniEnv, jstring string) { - auto length = jniEnv->GetStringLength(string); - std::u16string chars(length, char16_t()); - if(!chars.empty()) { - jniEnv->GetStringRegion(string, 0, length, reinterpret_cast(&chars[0])); - } - return std::wstring_convert, char16_t>().to_bytes(chars); -} - -jstring jstringFromString(JNIEnv* jniEnv, const std::string& string) { - const auto emptyu16 = u""; - auto chars = std::wstring_convert, char16_t>().from_bytes(string); - auto s = reinterpret_cast(chars.empty() ? emptyu16 : chars.data()); - return jniEnv->NewString(s, chars.length()); -} - -void logMsg(const char* fmt, ...) { - - va_list args; - va_start(args, fmt); - __android_log_vprint(ANDROID_LOG_DEBUG, "Tangram", fmt, args); - va_end(args); - -} - - -AndroidPlatform::AndroidPlatform(JNIEnv* _jniEnv, jobject _assetManager, jobject _tangramInstance) - : m_jniWorker(jvm) { - - m_tangramInstance = _jniEnv->NewWeakGlobalRef(_tangramInstance); - - m_assetManager = AAssetManager_fromJava(_jniEnv, _assetManager); - - if (m_assetManager == nullptr) { - LOGE("Could not obtain Asset Manager reference"); - return; - } - -#ifdef TANGRAM_MBTILES_DATASOURCE - sqlite3_ndk_init(m_assetManager); -#endif -} - -void AndroidPlatform::shutdown() { - Platform::shutdown(); - m_jniWorker.stop(); -} - -std::string AndroidPlatform::fontPath(const std::string& _family, const std::string& _weight, const std::string& _style) const { - - JniThreadBinding jniEnv(jvm); - - std::string key = _family + "_" + _weight + "_" + _style; - - jstring jkey = jstringFromString(jniEnv, key); - jstring returnStr = (jstring) jniEnv->CallObjectMethod(m_tangramInstance, getFontFilePath, jkey); - - auto resultStr = stringFromJString(jniEnv, returnStr); - jniEnv->DeleteLocalRef(returnStr); - jniEnv->DeleteLocalRef(jkey); - - return resultStr; -} - -void AndroidPlatform::requestRender() const { - m_jniWorker.enqueue([&](JNIEnv *jniEnv) { - jniEnv->CallVoidMethod(m_tangramInstance, requestRenderMethodID); - }); -} - -std::vector AndroidPlatform::systemFontFallbacksHandle() const { - JniThreadBinding jniEnv(jvm); - - std::vector handles; - - int importance = 0; - int weightHint = 400; - - auto fontFallbackPath = [&](int _importance, int _weightHint) { - - jstring returnStr = (jstring) jniEnv->CallObjectMethod(m_tangramInstance, - getFontFallbackFilePath, _importance, - _weightHint); - - auto resultStr = stringFromJString(jniEnv, returnStr); - jniEnv->DeleteLocalRef(returnStr); - - return resultStr; - }; - - std::string fallbackPath = fontFallbackPath(importance, weightHint); - - while (!fallbackPath.empty()) { - handles.emplace_back(Url(fallbackPath)); - - fallbackPath = fontFallbackPath(++importance, weightHint); - } - - return handles; -} - -FontSourceHandle AndroidPlatform::systemFont(const std::string& _name, const std::string& _weight, - const std::string& _face) const { - std::string path = fontPath(_name, _weight, _face); - - if (path.empty()) { return {}; } - - auto data = bytesFromFile(path.c_str()); - - return FontSourceHandle([data]() { return data; }); -} - -void AndroidPlatform::setContinuousRendering(bool _isContinuous) { - Platform::setContinuousRendering(_isContinuous); - - JniThreadBinding jniEnv(jvm); - - jniEnv->CallVoidMethod(m_tangramInstance, setRenderModeMethodID, _isContinuous ? 1 : 0); -} - -bool AndroidPlatform::bytesFromAssetManager(const char* _path, std::function _allocator) const { - - AAsset* asset = AAssetManager_open(m_assetManager, _path, AASSET_MODE_UNKNOWN); - if (asset == nullptr) { - LOGW("Failed to open asset at path: %s", _path); - return false; - } - - size_t size = AAsset_getLength(asset); - unsigned char* data = reinterpret_cast(_allocator(size)); - - int read = AAsset_read(asset, data, size); - if (read <= 0) { - LOGW("Failed to read asset at path: %s", _path); - } - AAsset_close(asset); - - return read > 0; -} - -std::vector AndroidPlatform::bytesFromFile(const Url& url) const { - std::vector data; - - auto allocator = [&](size_t size) { - data.resize(size); - return data.data(); - }; - - auto path = url.path(); - - if (url.scheme() == "asset") { - // The asset manager doesn't like paths starting with '/'. - if (!path.empty() && path.front() == '/') { - path = path.substr(1); - } - bytesFromAssetManager(path.c_str(), allocator); - } else { - Platform::bytesFromFileSystem(path.c_str(), allocator); - } - - return data; -} - -bool AndroidPlatform::startUrlRequestImpl(const Url& _url, const UrlRequestHandle _request, UrlRequestId& _id) { - - // If the requested URL does not use HTTP or HTTPS, retrieve it asynchronously. - if (!_url.hasHttpScheme()) { - m_fileWorker.enqueue([=](){ - UrlResponse response; - response.content = bytesFromFile(_url); - onUrlResponse(_request, std::move(response)); - }); - return false; - } - - // We can use UrlRequestHandle to cancel requests. MapController handles the - // mapping between UrlRequestHandle and request object - _id = _request; - - m_jniWorker.enqueue([=](JNIEnv *jniEnv) { - jlong jRequestHandle = static_cast(_request); - - // Make sure no one changed UrlRequestHandle from being uint64_t, - // so that it's safe to convert to jlong and back. - static_assert(sizeof(jlong) == sizeof(UrlRequestHandle), "Who changed UrlRequestHandle?!"); - static_assert(static_cast(std::numeric_limits::max()) == - static_cast(std::numeric_limits::max()), - "Cannot convert jlong to UrlRequestHandle!"); - - jstring jUrl = jstringFromString(jniEnv, _url.string()); - - // Call the MapController method to start the URL request. - jniEnv->CallVoidMethod(m_tangramInstance, startUrlRequestMID, jUrl, jRequestHandle); - jniEnv->DeleteLocalRef(jUrl); - }); - - return true; -} - -void AndroidPlatform::cancelUrlRequestImpl(const UrlRequestId _id) { - - m_jniWorker.enqueue([=](JNIEnv *jniEnv) { - - jlong jRequestHandle = static_cast(_id); - - jniEnv->CallVoidMethod(m_tangramInstance, cancelUrlRequestMID, jRequestHandle); - }); -} - -void AndroidPlatform::onUrlComplete(JNIEnv* _jniEnv, jlong _jRequestHandle, jbyteArray _jBytes, jstring _jError) { - // Start building a response object. - UrlResponse response; - - // If the request was successful, we will receive a non-null byte array. - if (_jBytes != nullptr) { - size_t length = _jniEnv->GetArrayLength(_jBytes); - response.content.resize(length); - _jniEnv->GetByteArrayRegion(_jBytes, 0, length, reinterpret_cast(response.content.data())); - // TODO: Can we use a DirectByteBuffer to transfer data with fewer copies? - } - - // If the request was unsuccessful, we will receive a non-null error string. - std::string error; - if (_jError != nullptr) { - error = stringFromJString(_jniEnv, _jError); - response.error = error.c_str(); - } - - // Handle callbacks on worker thread to not block Java side. - // (The calling thread has probably also other work to do) - m_fileWorker.enqueue([this, _jRequestHandle, r = std::move(response)]() mutable { - UrlRequestHandle requestHandle = static_cast(_jRequestHandle); - - onUrlResponse(requestHandle, std::move(r)); - }); -} - -void setCurrentThreadPriority(int priority) { - setpriority(PRIO_PROCESS, 0, priority); -} - - - -void initGLExtensions() { - if (glExtensionsLoaded) { - return; - } - - void* libhandle = dlopen("libGLESv2.so", RTLD_LAZY); - - glBindVertexArrayOESEXT = (PFNGLBINDVERTEXARRAYOESPROC) dlsym(libhandle, "glBindVertexArrayOES"); - glDeleteVertexArraysOESEXT = (PFNGLDELETEVERTEXARRAYSOESPROC) dlsym(libhandle, "glDeleteVertexArraysOES"); - glGenVertexArraysOESEXT = (PFNGLGENVERTEXARRAYSOESPROC) dlsym(libhandle, "glGenVertexArraysOES"); - - glExtensionsLoaded = true; -} - -AndroidMap::AndroidMap(JNIEnv* _jniEnv, jobject _assetManager, jobject _tangramInstance) - : Map(std::make_unique(_jniEnv, _assetManager, _tangramInstance)) { - - m_tangramInstance = _jniEnv->NewWeakGlobalRef(_tangramInstance); - - setSceneReadyListener([this](Tangram::SceneID id, const Tangram::SceneError* sceneError) { - JniThreadBinding jniEnv(jvm); - - jint jErrorType = -1; - jstring jUpdatePath = nullptr; - jstring jUpdateValue = nullptr; - - if (sceneError) { - jUpdatePath = jstringFromString(jniEnv, sceneError->update.path); - jUpdateValue = jstringFromString(jniEnv, sceneError->update.value); - jErrorType = (jint)sceneError->error; - } - - jniEnv->CallVoidMethod(m_tangramInstance, sceneReadyCallbackMID, id, jErrorType, jUpdatePath, jUpdateValue); - }); - - setCameraAnimationListener([this](bool finished) { - JniThreadBinding jniEnv(jvm); - - jniEnv->CallVoidMethod(m_tangramInstance, cameraAnimationCallbackMID, finished); - }); -} - -void AndroidMap::pickFeature(float posX, float posY) { - - pickFeatureAt(posX, posY, [this](const auto* featurePickResult) { - JniThreadBinding jniEnv(jvm); - - float x = 0.f, y = 0.f; - jobject hashmap = nullptr; - - if (featurePickResult) { - x = featurePickResult->position[0]; - y = featurePickResult->position[1]; - - hashmap = jniEnv->NewObject(hashmapClass, hashmapInitMID); - const auto& properties = featurePickResult->properties; - for (const auto& item : properties->items()) { - jstring jkey = jstringFromString(jniEnv, item.key); - jstring jvalue = jstringFromString(jniEnv, properties->asString(item.value)); - jniEnv->CallObjectMethod(hashmap, hashmapPutMID, jkey, jvalue); - } - } - jniEnv->CallVoidMethod(m_tangramInstance, featurePickCallbackMID, hashmap, x, y); - }); -} - -void AndroidMap::pickLabel(float posX, float posY) { - - pickLabelAt(posX, posY, [this](const auto* labelPickResult){ - JniThreadBinding jniEnv(jvm); - - float x = 0.f, y = 0.f; - double lng = 0., lat = 0.; - int type = 0; - jobject hashmap = nullptr; - - if (labelPickResult) { - x = labelPickResult->touchItem.position[0]; - y = labelPickResult->touchItem.position[1]; - lng = labelPickResult->coordinates.longitude; - lat = labelPickResult->coordinates.latitude; - type = labelPickResult->type; - - hashmap = jniEnv->NewObject(hashmapClass, hashmapInitMID); - const auto& properties = labelPickResult->touchItem.properties; - for (const auto& item : properties->items()) { - jstring jkey = jstringFromString(jniEnv, item.key); - jstring jvalue = jstringFromString(jniEnv, properties->asString(item.value)); - jniEnv->CallObjectMethod(hashmap, hashmapPutMID, jkey, jvalue); - } - } - jniEnv->CallVoidMethod(m_tangramInstance, labelPickCallbackMID, hashmap, x, y, type, lng, lat); - }); -} - -void AndroidMap::pickMarker(float posX, float posY) { - - pickMarkerAt(posX, posY, [this](const auto* markerPickResult) { - JniThreadBinding jniEnv(jvm); - - float x = 0.f, y = 0.f; - double lng = 0., lat = 0.; - jlong markerID = 0; - - if (markerPickResult) { - x = markerPickResult->position[0]; - y = markerPickResult->position[1]; - lng = markerPickResult->coordinates.longitude; - lat = markerPickResult->coordinates.latitude; - markerID = markerPickResult->id; - } - - jniEnv->CallVoidMethod(m_tangramInstance, markerPickCallbackMID, markerID, x, y, lng, lat); - }); -} - -} // namespace Tangram diff --git a/platforms/android/tangram/src/main/cpp/androidPlatform.h b/platforms/android/tangram/src/main/cpp/androidPlatform.h deleted file mode 100644 index b89f3c3065..0000000000 --- a/platforms/android/tangram/src/main/cpp/androidPlatform.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include "map.h" -#include "platform.h" -#include "jniWorker.h" -#include "util/asyncWorker.h" - - -#include -#include -#include -#include -#include -#include -#include - -namespace Tangram { - -struct LabelPickResult; -struct FeaturePickResult; -struct MarkerPickResult; -class Map; -struct SceneUpdate; -struct SceneError; -using SceneID = int32_t; - -std::string stringFromJString(JNIEnv* jniEnv, jstring string); -jstring jstringFromString(JNIEnv* jniEnv, const std::string& string); - - -class AndroidPlatform : public Platform { - -public: - - AndroidPlatform(JNIEnv* _jniEnv, jobject _assetManager, jobject _tangramInstance); - void shutdown() override; - void requestRender() const override; - void setContinuousRendering(bool _isContinuous) override; - FontSourceHandle systemFont(const std::string& _name, const std::string& _weight, const std::string& _face) const override; - std::vector systemFontFallbacksHandle() const override; - bool startUrlRequestImpl(const Url& _url, const UrlRequestHandle _request, UrlRequestId& _id) override; - void cancelUrlRequestImpl(const UrlRequestId _id) override; - - void onUrlComplete(JNIEnv* jniEnv, jlong jRequestHandle, jbyteArray jBytes, jstring jError); - - static void bindJniEnvToThread(JNIEnv* jniEnv); - static jint jniOnLoad(JavaVM* javaVM); - static void jniOnUnload(JavaVM* javaVM); - -private: - - std::vector bytesFromFile(const Url& _url) const; - bool bytesFromAssetManager(const char* _path, std::function _allocator) const; - std::string fontPath(const std::string& _family, const std::string& _weight, const std::string& _style) const; - std::string fontFallbackPath(int _importance, int _weightHint) const; - - jobject m_tangramInstance; - AAssetManager* m_assetManager; - - mutable JniWorker m_jniWorker; - AsyncWorker m_fileWorker; -}; - -class AndroidMap : public Map { -public: - AndroidMap(JNIEnv* _jniEnv, jobject _assetManager, jobject _tangramInstance); - void pickFeature(float posX, float posY); - void pickMarker(float posX, float posY); - void pickLabel(float posX, float posY); - - - jobject m_tangramInstance; -}; - - -} // namespace Tangram diff --git a/platforms/android/tangram/src/main/cpp/jniExports.cpp b/platforms/android/tangram/src/main/cpp/jniExports.cpp deleted file mode 100644 index dab7a8fc36..0000000000 --- a/platforms/android/tangram/src/main/cpp/jniExports.cpp +++ /dev/null @@ -1,626 +0,0 @@ -#include "androidPlatform.h" -#include "data/clientDataSource.h" -#include "map.h" - -#include -#include - -using namespace Tangram; - - -std::vector unpackSceneUpdates(JNIEnv* jniEnv, jobjectArray updateStrings) { - int nUpdateStrings = (updateStrings == NULL)? 0 : jniEnv->GetArrayLength(updateStrings); - - std::vector sceneUpdates; - for (int i = 0; i < nUpdateStrings;) { - jstring path = (jstring) (jniEnv->GetObjectArrayElement(updateStrings, i++)); - jstring value = (jstring) (jniEnv->GetObjectArrayElement(updateStrings, i++)); - sceneUpdates.emplace_back(stringFromJString(jniEnv, path), stringFromJString(jniEnv, value)); - jniEnv->DeleteLocalRef(path); - jniEnv->DeleteLocalRef(value); - } - return sceneUpdates; -} - -extern "C" { - -JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { - return AndroidPlatform::jniOnLoad(vm); -} - -JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) { - AndroidPlatform::jniOnUnload(vm); -} - - -#define FUNC(CLASS, NAME) JNIEXPORT JNICALL Java_com_mapzen_tangram_ ## CLASS ## _native ## NAME - -#define auto_map(ptr) assert(ptr); auto map = reinterpret_cast(mapPtr) -#define auto_source(ptr) assert(ptr); auto source = reinterpret_cast(ptr) - - -#define MapRenderer(NAME) FUNC(MapRenderer, NAME) - -jint MapRenderer(Update)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat dt) { - auto_map(mapPtr); - auto result = map->update(dt); - return static_cast(result.flags); -} - -void MapRenderer(Render)(JNIEnv* jniEnv, jobject obj, jlong mapPtr) { - auto_map(mapPtr); - map->render(); -} - -void MapRenderer(SetupGL)(JNIEnv* jniEnv, jobject obj, jlong mapPtr) { - AndroidPlatform::bindJniEnvToThread(jniEnv); - auto_map(mapPtr); - map->setupGL(); -} - -void MapRenderer(Resize)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jint width, jint height) { - auto_map(mapPtr); - map->resize(width, height); -} - -void MapRenderer(CaptureSnapshot)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jintArray buffer) { - auto_map(mapPtr); - jint* ptr = jniEnv->GetIntArrayElements(buffer, NULL); - unsigned int* data = reinterpret_cast(ptr); - map->captureSnapshot(data); - jniEnv->ReleaseIntArrayElements(buffer, ptr, JNI_ABORT); -} - - -#define MapController(NAME) FUNC(MapController, NAME) - -void MapController(GetCameraPosition)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jdoubleArray lonLat, - jfloatArray zoomRotationTilt) { - auto_map(mapPtr); - jdouble* pos = jniEnv->GetDoubleArrayElements(lonLat, NULL); - jfloat* zrt = jniEnv->GetFloatArrayElements(zoomRotationTilt, NULL); - - auto camera = map->getCameraPosition(); - pos[0] = camera.longitude; - pos[1] = camera.latitude; - zrt[0] = camera.zoom; - zrt[1] = camera.rotation; - zrt[2] = camera.tilt; - - jniEnv->ReleaseDoubleArrayElements(lonLat, pos, 0); - jniEnv->ReleaseFloatArrayElements(zoomRotationTilt, zrt, 0); -} - -void MapController(UpdateCameraPosition)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, - jint set, jdouble lon, jdouble lat, - jfloat zoom, jfloat zoomBy, - jfloat rotation, jfloat rotateBy, - jfloat tilt, jfloat tiltBy, - jdouble b1lon, jdouble b1lat, - jdouble b2lon, jdouble b2lat, - jintArray jpad, jfloat duration, jint ease) { - auto_map(mapPtr); - - CameraUpdate update; - update.set = set; - - update.lngLat = LngLat{lon,lat}; - update.zoom = zoom; - update.zoomBy = zoomBy; - update.rotation = rotation; - update.rotationBy = rotateBy; - update.tilt = tilt; - update.tiltBy = tiltBy; - update.bounds = std::array{{LngLat{b1lon, b1lat}, LngLat{b2lon, b2lat}}}; - if (jpad != NULL) { - jint* jpadArray = jniEnv->GetIntArrayElements(jpad, NULL); - update.padding = EdgePadding{jpadArray[0], jpadArray[1], jpadArray[2], jpadArray[3]}; - jniEnv->ReleaseIntArrayElements(jpad, jpadArray, JNI_ABORT); - } - map->updateCameraPosition(update, duration, static_cast(ease)); -} - -void MapController(GetEnclosingCameraPosition)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, - jdouble aLng, jdouble aLat, - jdouble bLng, jdouble bLat, - jintArray jpad, jdoubleArray lngLatZoom) { - auto_map(mapPtr); - - EdgePadding padding; - if (jpad != NULL) { - jint* jpadArray = jniEnv->GetIntArrayElements(jpad, NULL); - padding = EdgePadding(jpadArray[0], jpadArray[1], jpadArray[2], jpadArray[3]); - jniEnv->ReleaseIntArrayElements(jpad, jpadArray, JNI_ABORT); - } - CameraPosition camera = map->getEnclosingCameraPosition(LngLat{aLng,aLat}, LngLat{bLng,bLat}, padding); - jdouble* arr = jniEnv->GetDoubleArrayElements(lngLatZoom, NULL); - arr[0] = camera.longitude; - arr[1] = camera.latitude; - arr[2] = camera.zoom; - jniEnv->ReleaseDoubleArrayElements(lngLatZoom, arr, 0); -} - -void MapController(FlyTo)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jdouble lon, jdouble lat, - jfloat zoom, jfloat duration, jfloat speed) { - auto_map(mapPtr); - - CameraPosition camera = map->getCameraPosition(); - camera.longitude = lon; - camera.latitude = lat; - camera.zoom = zoom; - map->flyTo(camera, duration, speed); -} - -void MapController(CancelCameraAnimation)(JNIEnv* jniEnv, jobject obj, jlong mapPtr) { - auto_map(mapPtr); - map->cancelCameraAnimation(); -} - -jboolean MapController(ScreenPositionToLngLat)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, - jdoubleArray coordinates) { - auto_map(mapPtr); - - jdouble* arr = jniEnv->GetDoubleArrayElements(coordinates, NULL); - bool result = map->screenPositionToLngLat(arr[0], arr[1], &arr[0], &arr[1]); - jniEnv->ReleaseDoubleArrayElements(coordinates, arr, 0); - return static_cast(result); -} - -jboolean MapController(LngLatToScreenPosition)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, - jdoubleArray coordinates, jboolean clipToViewport) { - auto_map(mapPtr); - - jdouble* arr = jniEnv->GetDoubleArrayElements(coordinates, NULL); - bool result = map->lngLatToScreenPosition(arr[0], arr[1], &arr[0], &arr[1], clipToViewport); - jniEnv->ReleaseDoubleArrayElements(coordinates, arr, 0); - return static_cast(result); -} - -jlong MapController(Init)(JNIEnv* jniEnv, jobject tangramInstance, jobject assetManager) { - auto map = new Tangram::AndroidMap(jniEnv, assetManager, tangramInstance); - return reinterpret_cast(map); -} - -void MapController(Dispose)(JNIEnv* jniEnv, jobject tangramInstance, jlong mapPtr) { - auto_map(mapPtr); - delete map; -} - -void MapController(Shutdown)(JNIEnv* jniEnv, jobject tangramInstance, jlong mapPtr) { - auto_map(mapPtr); - map->getPlatform().shutdown(); -} - -jint MapController(LoadScene)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jstring path, - jobjectArray updateStrings) { - auto_map(mapPtr); - - auto cPath = stringFromJString(jniEnv, path); - - auto sceneUpdates = unpackSceneUpdates(jniEnv, updateStrings); - Url sceneUrl = Url(cPath).resolved("asset:///"); - jint sceneId = map->loadScene(sceneUrl.string(), false, sceneUpdates); - - return sceneId; -} - -jint MapController(LoadSceneAsync)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jstring path, - jobjectArray updateStrings) { - auto_map(mapPtr); - - auto cPath = stringFromJString(jniEnv, path); - - auto sceneUpdates = unpackSceneUpdates(jniEnv, updateStrings); - Url sceneUrl = Url(cPath).resolved("asset:///"); - jint sceneId = map->loadSceneAsync(sceneUrl.string(), false, sceneUpdates); - - return sceneId; - - -} - -jint MapController(LoadSceneYaml)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jstring yaml, jstring path, - jobjectArray updateStrings) { - auto_map(mapPtr); - - auto cYaml = stringFromJString(jniEnv, yaml); - auto cPath = stringFromJString(jniEnv, path); - - auto sceneUpdates = unpackSceneUpdates(jniEnv, updateStrings); - Url sceneUrl = Url(cPath).resolved("asset:///"); - jint sceneId = map->loadSceneYaml(cYaml, sceneUrl.string(), false, sceneUpdates); - - return sceneId; -} - -jint MapController(LoadSceneYamlAsync)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jstring yaml, jstring path, - jobjectArray updateStrings) { - auto_map(mapPtr); - - auto cYaml = stringFromJString(jniEnv, yaml); - auto cPath = stringFromJString(jniEnv, path); - - auto sceneUpdates = unpackSceneUpdates(jniEnv, updateStrings); - Url sceneUrl = Url(cPath).resolved("asset:///"); - jint sceneId = map->loadSceneYamlAsync(cYaml, sceneUrl.string(), false, sceneUpdates); - - return sceneId; -} - -void MapController(SetPixelScale)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat scale) { - auto_map(mapPtr); - map->setPixelScale(scale); -} - -void MapController(SetCameraType)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jint type) { - auto_map(mapPtr); - map->setCameraType(type); -} - -jint MapController(GetCameraType)(JNIEnv* jniEnv, jobject obj, jlong mapPtr) { - auto_map(mapPtr); - return map->getCameraType(); -} - -jfloat MapController(GetMinZoom)(JNIEnv* jniEnv, jobject obj, jlong mapPtr) { - auto_map(mapPtr); - return map->getMinZoom(); -} - -void MapController(SetMinZoom)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat minZoom) { - auto_map(mapPtr); - map->setMinZoom(minZoom); -} - -jfloat MapController(GetMaxZoom)(JNIEnv* jniEnv, jobject obj, jlong mapPtr) { - auto_map(mapPtr); - return map->getMaxZoom(); -} - -void MapController(SetMaxZoom)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat maxZoom) { - auto_map(mapPtr); - map->setMaxZoom(maxZoom); -} - -void MapController(HandleTapGesture)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat posX, jfloat posY) { - auto_map(mapPtr); - map->handleTapGesture(posX, posY); -} - -void MapController(HandleDoubleTapGesture)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat posX, jfloat posY) { - auto_map(mapPtr); - map->handleDoubleTapGesture(posX, posY); -} - -void MapController(HandlePanGesture)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat startX, jfloat startY, - jfloat endX, jfloat endY) { - auto_map(mapPtr); - map->handlePanGesture(startX, startY, endX, endY); -} - -void MapController(HandleFlingGesture)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat posX, jfloat posY, - jfloat velocityX, jfloat velocityY) { - auto_map(mapPtr); - map->handleFlingGesture(posX, posY, velocityX, velocityY); -} - -void MapController(HandlePinchGesture)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat posX, jfloat posY, - jfloat scale, jfloat velocity) { - auto_map(mapPtr); - map->handlePinchGesture(posX, posY, scale, velocity); -} - -void MapController(HandleRotateGesture)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat posX, jfloat posY, - jfloat rotation) { - auto_map(mapPtr); - map->handleRotateGesture(posX, posY, rotation); -} - -void MapController(HandleShoveGesture)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat distance) { - auto_map(mapPtr); - map->handleShoveGesture(distance); -} - -void MapController(OnUrlComplete)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong requestHandle, - jbyteArray fetchedBytes, jstring errorString) { - auto_map(mapPtr); - - auto& platform = static_cast(map->getPlatform()); - platform.onUrlComplete(jniEnv, requestHandle, fetchedBytes, errorString); -} - -void MapController(SetPickRadius)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat radius) { - auto_map(mapPtr); - map->setPickRadius(radius); -} - -void MapController(PickFeature)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat posX, jfloat posY) { - auto_map(mapPtr); - map->pickFeature(posX, posY); - -} - -void MapController(PickMarker)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat posX, jfloat posY) { - auto_map(mapPtr); - map->pickMarker(posX, posY); -} - -void MapController(PickLabel)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jfloat posX, jfloat posY) { - auto_map(mapPtr); - map->pickLabel(posX, posY); -} - -// NOTE unsigned int to jlong for precision... else we can do jint return -jlong MapController(MarkerAdd)(JNIEnv* jniEnv, jobject obj, jlong mapPtr) { - auto_map(mapPtr); - auto markerID = map->markerAdd(); - return static_cast(markerID); -} - -jboolean MapController(MarkerRemove)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong markerID) { - auto_map(mapPtr); - auto result = map->markerRemove(static_cast(markerID)); - return static_cast(result); -} - -jboolean MapController(MarkerSetStylingFromString)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong markerID, - jstring styling) { - auto_map(mapPtr); - - auto styleString = stringFromJString(jniEnv, styling); - auto result = map->markerSetStylingFromString(static_cast(markerID), styleString.c_str()); - return static_cast(result); -} - -jboolean MapController(MarkerSetStylingFromPath)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong markerID, - jstring path) { - auto_map(mapPtr); - - auto pathString = stringFromJString(jniEnv, path); - auto result = map->markerSetStylingFromPath(static_cast(markerID), pathString.c_str()); - return static_cast(result); -} - -jboolean MapController(MarkerSetBitmap)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong markerID, - jobject jbitmap, jfloat density) { - auto_map(mapPtr); - - AndroidBitmapInfo bitmapInfo; - if (AndroidBitmap_getInfo(jniEnv, jbitmap, &bitmapInfo) != ANDROID_BITMAP_RESULT_SUCCESS) { - return static_cast(false); - } - if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { - // TODO: Add different conversion functions for other formats. - return static_cast(false); - } - uint32_t* pixelInput; - if (AndroidBitmap_lockPixels(jniEnv, jbitmap, (void**)&pixelInput) != ANDROID_BITMAP_RESULT_SUCCESS) { - return static_cast(false); - } - int width = bitmapInfo.width; - int height = bitmapInfo.height; - uint32_t* pixelOutput = new uint32_t[height * width]; - int i = 0; - for (int row = 0; row < height; row++) { - // Flips image upside-down - int flippedRow = (height - 1 - row) * width; - for (int col = 0; col < width; col++) { - uint32_t pixel = pixelInput[i++]; - // Undo alpha pre-multiplication. - auto rgba = reinterpret_cast(&pixel); - int a = rgba[3]; - if (a != 0) { - auto alphaInv = 255.f/a; - rgba[0] = static_cast(rgba[0] * alphaInv); - rgba[1] = static_cast(rgba[1] * alphaInv); - rgba[2] = static_cast(rgba[2] * alphaInv); - } - pixelOutput[flippedRow + col] = pixel; - } - } - AndroidBitmap_unlockPixels(jniEnv, jbitmap); - auto result = map->markerSetBitmap(static_cast(markerID), width, height, pixelOutput, density); - delete[] pixelOutput; - return static_cast(result); -} - -jboolean MapController(MarkerSetPoint)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong markerID, - jdouble lng, jdouble lat) { - auto_map(mapPtr); - - auto result = map->markerSetPoint(static_cast(markerID), Tangram::LngLat(lng, lat)); - return static_cast(result); -} - -jboolean MapController(MarkerSetPointEased)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong markerID, - jdouble lng, jdouble lat, jfloat duration, jint ease) { - auto_map(mapPtr); - - auto result = map->markerSetPointEased(static_cast(markerID), - Tangram::LngLat(lng, lat), duration, - static_cast(ease)); - return static_cast(result); -} - -jboolean MapController(MarkerSetPolyline)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong markerID, - jdoubleArray jcoordinates, jint count) { - auto_map(mapPtr); - - if (!jcoordinates || count == 0) { return static_cast(false); } - - auto* coordinates = jniEnv->GetDoubleArrayElements(jcoordinates, NULL); - std::vector polyline; - polyline.reserve(static_cast(count)); - - for (size_t i = 0; i < count; ++i) { - polyline.emplace_back(coordinates[2 * i], coordinates[2 * i + 1]); - } - - auto result = map->markerSetPolyline(static_cast(markerID), polyline.data(), count); - return static_cast(result); -} - -jboolean MapController(MarkerSetPolygon)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong markerID, - jdoubleArray jcoordinates, jintArray jcounts, jint rings) { - auto_map(mapPtr); - - if (!jcoordinates || !jcounts || rings == 0) { return static_cast(false); } - - auto* coordinates = jniEnv->GetDoubleArrayElements(jcoordinates, NULL); - auto* counts = jniEnv->GetIntArrayElements(jcounts, NULL); - - std::vector polygonCoords; - - int coordsCount = 0; - for (int i = 0; i < rings; i++) { - int ringCount = *(counts+i); - for (int j = 0; j < ringCount; j++) { - polygonCoords.emplace_back(coordinates[coordsCount + 2 * j], - coordinates[coordsCount + 2 * j + 1]); - } - coordsCount += ringCount; - } - - auto result = map->markerSetPolygon(static_cast(markerID), - polygonCoords.data(), counts, rings); - - return static_cast(result); -} - -jboolean MapController(MarkerSetVisible)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong markerID, - jboolean visible) { - auto_map(mapPtr); - - auto result = map->markerSetVisible(static_cast(markerID), visible); - return static_cast(result); -} - -jboolean MapController(MarkerSetDrawOrder)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong markerID, - jint drawOrder) { - auto_map(mapPtr); - - auto result = map->markerSetDrawOrder(markerID, drawOrder); - return static_cast(result); -} - -void MapController(MarkerRemoveAll)(JNIEnv* jniEnv, jobject obj, jlong mapPtr) { - auto_map(mapPtr); - map->markerRemoveAll(); -} - - -void MapController(SetDebugFlag)(JNIEnv* jniEnv, jobject obj, jint flag, jboolean on) { - Tangram::setDebugFlag(static_cast(flag), on); -} - -void MapController(UseCachedGlState)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jboolean use) { - auto_map(mapPtr); - map->useCachedGlState(use); -} - -void MapController(OnLowMemory)(JNIEnv* jnienv, jobject obj, jlong mapPtr) { - auto_map(mapPtr); - map->onMemoryWarning(); -} - -void MapController(SetDefaultBackgroundColor)(JNIEnv* jnienv, jobject obj, jlong mapPtr, - jfloat r, jfloat g, jfloat b) { - auto_map(mapPtr); - map->setDefaultBackgroundColor(r, g, b); -} - -jlong MapController(AddClientDataSource)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, - jstring name, jboolean generateCentroid) { - auto_map(mapPtr); - - auto sourceName = stringFromJString(jniEnv, name); - auto source = std::make_shared(map->getPlatform(), - sourceName, "", - generateCentroid); - map->addTileSource(source); - return reinterpret_cast(source.get()); -} - -void MapController(RemoveClientDataSource)(JNIEnv* jniEnv, jobject obj, jlong mapPtr, jlong sourcePtr) { - auto_map(mapPtr); - auto_source(sourcePtr); - map->removeTileSource(*source); -} - -#define MapData(NAME) FUNC(MapData, NAME) - -void MapData(AddFeature)(JNIEnv* jniEnv, jobject obj, jlong sourcePtr, jdoubleArray jcoordinates, - jintArray jrings, jobjectArray jproperties) { - - auto_source(sourcePtr); - - int nPoints = jniEnv->GetArrayLength(jcoordinates) / 2; - int nRings = (jrings == NULL) ? 0 : jniEnv->GetArrayLength(jrings); - int nProperties = (jproperties == NULL) ? 0 : jniEnv->GetArrayLength(jproperties) / 2; - - Properties properties; - - for (int i = 0; i < nProperties; ++i) { - jstring jkey = (jstring) (jniEnv->GetObjectArrayElement(jproperties, 2 * i)); - jstring jvalue = (jstring) (jniEnv->GetObjectArrayElement(jproperties, 2 * i + 1)); - auto key = stringFromJString(jniEnv, jkey); - auto value = stringFromJString(jniEnv, jvalue); - properties.set(key, value); - jniEnv->DeleteLocalRef(jkey); - jniEnv->DeleteLocalRef(jvalue); - } - - auto* coordinates = jniEnv->GetDoubleArrayElements(jcoordinates, NULL); - - if (nRings > 0) { - // If rings are defined, this is a polygon feature. - auto* rings = jniEnv->GetIntArrayElements(jrings, NULL); - Tangram::ClientDataSource::PolygonBuilder builder; - builder.beginPolygon(static_cast(nRings)); - int offset = 0; - for (int j = 0; j < nRings; j++) { - int nPointsInRing = rings[j]; - builder.beginRing(static_cast(nPointsInRing)); - for (size_t i = 0; i < nPointsInRing; i++) { - builder.addPoint(LngLat(coordinates[2 * (offset + i)], coordinates[2 * (offset + i) + 1])); - } - offset += nPointsInRing; - } - source->addPolygonFeature(std::move(properties), std::move(builder)); - jniEnv->ReleaseIntArrayElements(jrings, rings, JNI_ABORT); - } else if (nPoints > 1) { - // If no rings defined but multiple points, this is a polyline feature. - Tangram::ClientDataSource::PolylineBuilder builder; - builder.beginPolyline(static_cast(nPoints)); - for (size_t i = 0; i < nPoints; i++) { - builder.addPoint(LngLat(coordinates[2 * i], coordinates[2 * i + 1])); - } - source->addPolylineFeature(std::move(properties), std::move(builder)); - } else { - // This is a point feature. - source->addPointFeature(std::move(properties), LngLat(coordinates[0], coordinates[1])); - } - - jniEnv->ReleaseDoubleArrayElements(jcoordinates, coordinates, JNI_ABORT); -} - -void MapData(AddGeoJson)(JNIEnv* jniEnv, jobject obj, jlong sourcePtr, jstring geojson) { - auto_source(sourcePtr); - - auto data = stringFromJString(jniEnv, geojson); - source->addData(data); -} - -void MapData(GenerateTiles)(JNIEnv* jniEnv, jobject obj, jlong sourcePtr) { - auto_source(sourcePtr); - - source->generateTiles(); -} - -void MapData(ClearFeatures)(JNIEnv* jniEnv, jobject obj, jlong sourcePtr) { - auto_source(sourcePtr); - - source->clearFeatures(); -} - -} // extern "C" diff --git a/platforms/android/tangram/src/main/java/com/mapzen/tangram/MapController.java b/platforms/android/tangram/src/main/java/com/mapzen/tangram/MapController.java index a39073f1cd..be813fabed 100644 --- a/platforms/android/tangram/src/main/java/com/mapzen/tangram/MapController.java +++ b/platforms/android/tangram/src/main/java/com/mapzen/tangram/MapController.java @@ -152,17 +152,14 @@ protected MapController(@NonNull Context context) { markers = new LongSparseArray<>(); // Get configuration info from application - displayMetrics = context.getResources().getDisplayMetrics(); - assetManager = context.getAssets(); + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + AssetManager assetManager = context.getAssets(); // Parse font file description //FontConfig.init(); - mapPointer = nativeInit(assetManager); - if (mapPointer <= 0) { - throw new RuntimeException("Unable to create a native Map object! There may be insufficient memory available."); - } - nativeSetPixelScale(mapPointer, displayMetrics.density); + nativeMap = new NativeMap(this, assetManager); + nativeMap.setPixelScale(displayMetrics.density); } /** @@ -188,7 +185,6 @@ void UIThreadInit(@NonNull final GLViewHolder viewHolder, @Nullable final HttpHa // Set up MapView this.viewHolder = viewHolder; viewHolder.setRenderer(mapRenderer); - isGLRendererSet = true; viewHolder.setRenderMode(GLViewHolder.RenderMode.RENDER_WHEN_DIRTY); touchInput = new TouchInput(context); @@ -210,10 +206,9 @@ void UIThreadInit(@NonNull final GLViewHolder viewHolder, @Nullable final HttpHa * If client code extends MapController and overrides this method, then it must call super.dispose() */ protected synchronized void dispose() { - if (mapPointer == 0) { return; } Log.e("TANGRAM", ">>> dispose"); - nativeShutdown(mapPointer); + nativeMap.shutdown(); Log.e("TANGRAM", "<<< http requests: " + httpRequestHandles.size()); for (MapData mapData : clientTileSources.values()) { @@ -236,11 +231,10 @@ protected synchronized void dispose() { labelPickListener = null; markerPickListener = null; cameraAnimationCallback = null; - frameCaptureCallback = null; // Prevent any calls to native functions - except dispose. - final long pointer = mapPointer; - mapPointer = 0; + final NativeMap disposingNativeMap = nativeMap; + nativeMap = null; // NOTE: It is possible for the MapView held by a ViewGroup to be removed, calling detachFromWindow which // stops the Render Thread associated with GLSurfaceView, possibly resulting in leaks from render thread @@ -249,7 +243,7 @@ protected synchronized void dispose() { // // Since all gl resources will be freed when GLSurfaceView is deleted this is safe until // we support sharing gl contexts. - nativeDispose(pointer); + disposingNativeMap.dispose(); Log.e("TANGRAM", "<<< disposed"); } @@ -299,9 +293,8 @@ public int loadSceneFileAsync(final String path) { * {@link SceneLoadListener#onSceneReady(int sceneId, SceneError sceneError)} when loading is complete. */ public int loadSceneFile(final String path, @Nullable final List sceneUpdates) { - checkPointer(mapPointer); final String[] updateStrings = bundleSceneUpdates(sceneUpdates); - final int sceneId = nativeLoadScene(mapPointer, path, updateStrings); + final int sceneId = nativeMap.loadScene(path, updateStrings); removeAllMarkers(); requestRender(); return sceneId; @@ -318,9 +311,8 @@ public int loadSceneFile(final String path, @Nullable final List sc * {@link SceneLoadListener#onSceneReady(int sceneId, SceneError sceneError)} when loading is complete. */ public int loadSceneFileAsync(final String path, @Nullable final List sceneUpdates) { - checkPointer(mapPointer); final String[] updateStrings = bundleSceneUpdates(sceneUpdates); - final int sceneId = nativeLoadSceneAsync(mapPointer, path, updateStrings); + final int sceneId = nativeMap.loadSceneAsync(path, updateStrings); removeAllMarkers(); requestRender(); return sceneId; @@ -338,9 +330,8 @@ public int loadSceneFileAsync(final String path, @Nullable final List sceneUpdates) { - checkPointer(mapPointer); final String[] updateStrings = bundleSceneUpdates(sceneUpdates); - final int sceneId = nativeLoadSceneYaml(mapPointer, yaml, resourceRoot, updateStrings); + final int sceneId = nativeMap.loadSceneYaml(yaml, resourceRoot, updateStrings); removeAllMarkers(); requestRender(); return sceneId; @@ -358,9 +349,8 @@ public int loadSceneYaml(final String yaml, final String resourceRoot, */ public int loadSceneYamlAsync(final String yaml, final String resourceRoot, @Nullable final List sceneUpdates) { - checkPointer(mapPointer); final String[] updateStrings = bundleSceneUpdates(sceneUpdates); - final int sceneId = nativeLoadSceneYamlAsync(mapPointer, yaml, resourceRoot, updateStrings); + final int sceneId = nativeMap.loadSceneYamlAsync(yaml, resourceRoot, updateStrings); removeAllMarkers(); requestRender(); return sceneId; @@ -412,7 +402,6 @@ public void updateCameraPosition(@NonNull final CameraUpdate update, final int d * @param cb Callback that will run when the animation is finished or canceled */ public void updateCameraPosition(@NonNull final CameraUpdate update, final int duration, @NonNull final EaseType ease, @Nullable final CameraAnimationCallback cb) { - checkPointer(mapPointer); if (duration > 0) { setMapRegionState(MapRegionChangeState.ANIMATING); } else { @@ -420,7 +409,7 @@ public void updateCameraPosition(@NonNull final CameraUpdate update, final int d } setPendingCameraAnimationCallback(cb); final float seconds = duration / 1000.f; - nativeUpdateCameraPosition(mapPointer, update.set, update.longitude, update.latitude, update.zoom, + nativeMap.updateCameraPosition(update.set, update.longitude, update.latitude, update.zoom, update.zoomBy, update.rotation, update.rotationBy, update.tilt, update.tiltBy, update.boundsLon1, update.boundsLat1, update.boundsLon2, update.boundsLat2, update.padding, seconds, ease.ordinal()); @@ -458,7 +447,6 @@ public void flyToCameraPosition(@NonNull final CameraPosition position, @Nullabl } private void flyToCameraPosition(@NonNull final CameraPosition position, final int duration, @Nullable final CameraAnimationCallback callback, final float speed) { - checkPointer(mapPointer); if (duration == 0) { setMapRegionState(MapRegionChangeState.JUMPING); } else { @@ -466,7 +454,7 @@ private void flyToCameraPosition(@NonNull final CameraPosition position, final i } setPendingCameraAnimationCallback(callback); final float seconds = duration / 1000.f; - nativeFlyTo(mapPointer, position.longitude, position.latitude, position.zoom, seconds, speed); + nativeMap.flyTo(position.longitude, position.latitude, position.zoom, seconds, speed); } private void setPendingCameraAnimationCallback(final CameraAnimationCallback callback) { @@ -509,10 +497,9 @@ public CameraPosition getCameraPosition() { */ @NonNull public CameraPosition getCameraPosition(@NonNull final CameraPosition out) { - checkPointer(mapPointer); final double[] pos = { 0, 0 }; final float[] zrt = { 0, 0, 0 }; - nativeGetCameraPosition(mapPointer, pos, zrt); + nativeMap.getCameraPosition(pos, zrt); out.longitude = pos[0]; out.latitude = pos[1]; out.zoom = zrt[0]; @@ -525,8 +512,7 @@ public CameraPosition getCameraPosition(@NonNull final CameraPosition out) { * Cancel current camera animation */ public void cancelCameraAnimation() { - checkPointer(mapPointer); - nativeCancelCameraAnimation(mapPointer); + nativeMap.cancelCameraAnimation(); } /** @@ -551,9 +537,9 @@ public CameraPosition getEnclosingCameraPosition(@NonNull LngLat sw, @NonNull Ln */ @NonNull public CameraPosition getEnclosingCameraPosition(@NonNull LngLat sw, @NonNull LngLat ne, @NonNull Rect padding, @NonNull final CameraPosition out) { - int pad[] = new int[]{padding.left, padding.top, padding.right, padding.bottom}; - double lngLatZoom[] = new double[3]; - nativeGetEnclosingCameraPosition(mapPointer, sw.longitude, sw.latitude, ne.longitude, ne.latitude, pad, lngLatZoom); + int[] pad = new int[]{padding.left, padding.top, padding.right, padding.bottom}; + double[] lngLatZoom = new double[3]; + nativeMap.getEnclosingCameraPosition(sw.longitude, sw.latitude, ne.longitude, ne.latitude, pad, lngLatZoom); out.longitude = lngLatZoom[0]; out.latitude = lngLatZoom[1]; out.zoom = (float)lngLatZoom[2]; @@ -567,8 +553,7 @@ public CameraPosition getEnclosingCameraPosition(@NonNull LngLat sw, @NonNull Ln * @param type A {@code CameraType} */ public void setCameraType(@NonNull final CameraType type) { - checkPointer(mapPointer); - nativeSetCameraType(mapPointer, type.ordinal()); + nativeMap.setCameraType(type.ordinal()); } /** @@ -576,8 +561,8 @@ public void setCameraType(@NonNull final CameraType type) { * @return A {@code CameraType} */ public CameraType getCameraType() { - checkPointer(mapPointer); - return CameraType.values()[nativeGetCameraType(mapPointer)]; + int nativeCameraType = nativeMap.getCameraType(); + return CameraType.values()[nativeCameraType]; } /** @@ -585,8 +570,7 @@ public CameraType getCameraType() { * @return The zoom level */ public float getMinimumZoomLevel() { - checkPointer(mapPointer); - return nativeGetMinZoom(mapPointer); + return nativeMap.getMinZoom(); } /** @@ -597,8 +581,7 @@ public float getMinimumZoomLevel() { * @param minimumZoom The zoom level */ public void setMinimumZoomLevel(float minimumZoom) { - checkPointer(mapPointer); - nativeSetMinZoom(mapPointer, minimumZoom); + nativeMap.setMinZoom(minimumZoom); } /** @@ -606,8 +589,7 @@ public void setMinimumZoomLevel(float minimumZoom) { * @return The zoom level */ public float getMaximumZoomLevel() { - checkPointer(mapPointer); - return nativeGetMaxZoom(mapPointer); + return nativeMap.getMaxZoom(); } /** @@ -618,8 +600,7 @@ public float getMaximumZoomLevel() { * @param maximumZoom The zoom level */ public void setMaximumZoomLevel(float maximumZoom) { - checkPointer(mapPointer); - nativeSetMaxZoom(mapPointer, maximumZoom); + nativeMap.setMaxZoom(maximumZoom); } /** @@ -630,9 +611,8 @@ public void setMaximumZoomLevel(float maximumZoom) { */ @Nullable public LngLat screenPositionToLngLat(@NonNull final PointF screenPosition) { - checkPointer(mapPointer); final double[] tmp = { screenPosition.x, screenPosition.y }; - if (nativeScreenPositionToLngLat(mapPointer, tmp)) { + if (nativeMap.screenPositionToLngLat(tmp)) { return new LngLat(tmp[0], tmp[1]); } return null; @@ -660,9 +640,8 @@ public PointF lngLatToScreenPosition(@NonNull final LngLat lngLat) { * @return True if the resulting point is inside the viewport, otherwise false. */ public boolean lngLatToScreenPosition(@NonNull final LngLat lngLat, @NonNull final PointF screenPositionOut, boolean clipToViewport) { - checkPointer(mapPointer); final double[] tmp = { lngLat.longitude, lngLat.latitude }; - boolean insideViewport = nativeLngLatToScreenPosition(mapPointer, tmp, clipToViewport); + boolean insideViewport = nativeMap.lngLatToScreenPosition(tmp, clipToViewport); screenPositionOut.set((float)tmp[0], (float)tmp[1]); return insideViewport; } @@ -696,8 +675,7 @@ public MapData addDataLayer(final String name, final boolean generateCentroid) { if (mapData != null) { return mapData; } - checkPointer(mapPointer); - final long pointer = nativeAddClientDataSource(mapPointer, name, generateCentroid); + final long pointer = nativeMap.addClientDataSource(name, generateCentroid); if (pointer <= 0) { throw new RuntimeException("Unable to create new data source"); } @@ -712,9 +690,8 @@ public MapData addDataLayer(final String name, final boolean generateCentroid) { */ void removeDataLayer(@NonNull final MapData mapData) { clientTileSources.remove(mapData.name); - checkPointer(mapPointer); checkPointer(mapData.pointer); - nativeRemoveClientDataSource(mapPointer, mapData.pointer); + nativeMap.removeClientDataSource(mapData.pointer); } /** @@ -780,7 +757,7 @@ public boolean onPanBegin() { @Override public boolean onPan(final float startX, final float startY, final float endX, final float endY) { setMapRegionState(MapRegionChangeState.JUMPING); - nativeHandlePanGesture(mapPointer, startX, startY, endX, endY); + nativeMap.handlePanGesture(startX, startY, endX, endY); return true; } @@ -792,7 +769,7 @@ public boolean onPanEnd() { @Override public boolean onFling(final float posX, final float posY, final float velocityX, final float velocityY) { - nativeHandleFlingGesture(mapPointer, posX, posY, velocityX, velocityY); + nativeMap.handleFlingGesture(posX, posY, velocityX, velocityY); return true; } @@ -818,7 +795,7 @@ public boolean onRotateBegin() { @Override public boolean onRotate(final float x, final float y, final float rotation) { setMapRegionState(MapRegionChangeState.JUMPING); - nativeHandleRotateGesture(mapPointer, x, y, rotation); + nativeMap.handleRotateGesture(x, y, rotation); return true; } @@ -844,7 +821,7 @@ public boolean onScaleBegin() { @Override public boolean onScale(final float x, final float y, final float scale, final float velocity) { setMapRegionState(MapRegionChangeState.JUMPING); - nativeHandlePinchGesture(mapPointer, x, y, scale, velocity); + nativeMap.handlePinchGesture(x, y, scale, velocity); return true; } @@ -870,7 +847,7 @@ public boolean onShoveBegin() { @Override public boolean onShove(final float distance) { setMapRegionState(MapRegionChangeState.JUMPING); - nativeHandleShoveGesture(mapPointer, distance); + nativeMap.handleShoveGesture(distance); return true; } @@ -887,8 +864,7 @@ public boolean onShoveEnd() { * @param radius The radius in dp (density-independent pixels). */ public void setPickRadius(final float radius) { - checkPointer(mapPointer); - nativeSetPickRadius(mapPointer, radius); + nativeMap.setPickRadius(radius); } /** @@ -951,8 +927,7 @@ public void run() { */ public void pickFeature(final float posX, final float posY) { if (featurePickListener != null) { - checkPointer(mapPointer); - nativePickFeature(mapPointer, posX, posY); + nativeMap.pickFeature(posX, posY); } } @@ -964,8 +939,7 @@ public void pickFeature(final float posX, final float posY) { */ public void pickLabel(final float posX, final float posY) { if (labelPickListener != null) { - checkPointer(mapPointer); - nativePickLabel(mapPointer, posX, posY); + nativeMap.pickLabel(posX, posY); } } @@ -977,8 +951,7 @@ public void pickLabel(final float posX, final float posY) { */ public void pickMarker(final float posX, final float posY) { if (markerPickListener != null) { - checkPointer(mapPointer); - nativePickMarker(mapPointer, posX, posY); + nativeMap.pickMarker(posX, posY); } } @@ -989,8 +962,7 @@ public void pickMarker(final float posX, final float posY) { */ @NonNull public Marker addMarker() { - checkPointer(mapPointer); - final long markerId = nativeMarkerAdd(mapPointer); + final long markerId = nativeMap.markerAdd(); final Marker marker = new Marker(viewHolder.getView().getContext(), markerId, this); markers.put(markerId, marker); @@ -1014,18 +986,16 @@ public boolean removeMarker(@NonNull final Marker marker) { * @return whether or not the marker was removed */ public boolean removeMarker(final long markerId) { - checkPointer(mapPointer); checkId(markerId); markers.remove(markerId); - return nativeMarkerRemove(mapPointer, markerId); + return nativeMap.markerRemove(markerId); } /** * Remove all the {@link Marker} objects from the map. */ public void removeAllMarkers() { - checkPointer(mapPointer); - nativeMarkerRemoveAll(mapPointer); + nativeMap.markerRemoveAll(); // Invalidate all markers so their ids are unusable for (int i = 0; i < markers.size(); i++) { @@ -1090,7 +1060,7 @@ public void queueEvent(@NonNull final Runnable r) { * @param on True to activate the feature, false to deactivate */ public void setDebugFlag(@NonNull final DebugFlag flag, final boolean on) { - nativeSetDebugFlag(flag.ordinal(), on); + nativeMap.setDebugFlag(flag.ordinal(), on); } /** @@ -1099,8 +1069,7 @@ public void setDebugFlag(@NonNull final DebugFlag flag, final boolean on) { * @param use Whether to use a cached OpenGL state; false by default */ public void useCachedGlState(final boolean use) { - checkPointer(mapPointer); - nativeUseCachedGlState(mapPointer, use); + nativeMap.useCachedGlState(use); } /** @@ -1110,16 +1079,14 @@ public void useCachedGlState(final boolean use) { * @param blue blue component of the background color */ public void setDefaultBackgroundColor(final float red, final float green, final float blue) { - checkPointer(mapPointer); - nativeSetDefaultBackgroundColor(mapPointer, red, green, blue); + nativeMap.setDefaultBackgroundColor(red, green, blue); } // Package private methods // ======================= void onLowMemory() { - checkPointer(mapPointer); - nativeOnLowMemory(mapPointer); + nativeMap.onLowMemory(); } void checkPointer(final long ptr) { @@ -1154,59 +1121,50 @@ private String[] bundleSceneUpdates(@Nullable final List sceneUpdat } boolean setMarkerStylingFromString(final long markerId, final String styleString) { - checkPointer(mapPointer); checkId(markerId); - return nativeMarkerSetStylingFromString(mapPointer, markerId, styleString); + return nativeMap.markerSetStylingFromString(markerId, styleString); } boolean setMarkerStylingFromPath(final long markerId, final String path) { - checkPointer(mapPointer); checkId(markerId); - return nativeMarkerSetStylingFromPath(mapPointer, markerId, path); + return nativeMap.markerSetStylingFromPath(markerId, path); } boolean setMarkerBitmap(final long markerId, Bitmap bitmap, float density) { - checkPointer(mapPointer); checkId(markerId); - return nativeMarkerSetBitmap(mapPointer, markerId, bitmap, density); + return nativeMap.markerSetBitmap(markerId, bitmap, density); } boolean setMarkerPoint(final long markerId, final double lng, final double lat) { - checkPointer(mapPointer); checkId(markerId); - return nativeMarkerSetPoint(mapPointer, markerId, lng, lat); + return nativeMap.markerSetPoint(markerId, lng, lat); } boolean setMarkerPointEased(final long markerId, final double lng, final double lat, final int duration, @NonNull final EaseType ease) { - checkPointer(mapPointer); checkId(markerId); final float seconds = duration / 1000.f; - return nativeMarkerSetPointEased(mapPointer, markerId, lng, lat, seconds, ease.ordinal()); + return nativeMap.markerSetPointEased(markerId, lng, lat, seconds, ease.ordinal()); } boolean setMarkerPolyline(final long markerId, final double[] coordinates, final int count) { - checkPointer(mapPointer); checkId(markerId); - return nativeMarkerSetPolyline(mapPointer, markerId, coordinates, count); + return nativeMap.markerSetPolyline(markerId, coordinates, count); } boolean setMarkerPolygon(final long markerId, final double[] coordinates, final int[] rings, final int count) { - checkPointer(mapPointer); checkId(markerId); - return nativeMarkerSetPolygon(mapPointer, markerId, coordinates, rings, count); + return nativeMap.markerSetPolygon(markerId, coordinates, rings, count); } boolean setMarkerVisible(final long markerId, final boolean visible) { - checkPointer(mapPointer); checkId(markerId); - return nativeMarkerSetVisible(mapPointer, markerId, visible); + return nativeMap.markerSetVisible(markerId, visible); } boolean setMarkerDrawOrder(final long markerId, final int drawOrder) { - checkPointer(mapPointer); checkId(markerId); - return nativeMarkerSetDrawOrder(mapPointer, markerId, drawOrder); + return nativeMap.markerSetDrawOrder(markerId, drawOrder); } @@ -1229,16 +1187,16 @@ void startUrlRequest(@NonNull final String url, final long requestHandle) { public void onFailure(@Nullable final IOException e) { if (httpRequestHandles.remove(requestHandle) == null) { return; } String msg = (e == null) ? "" : e.getMessage(); - nativeOnUrlComplete(mapPointer, requestHandle, null, msg); + nativeMap.onUrlComplete(requestHandle, null, msg); } @Override public void onResponse(final int code, @Nullable final byte[] rawDataBytes) { if (httpRequestHandles.remove(requestHandle) == null) { return; } if (code >= 200 && code < 300) { - nativeOnUrlComplete(mapPointer, requestHandle, rawDataBytes, null); + nativeMap.onUrlComplete(requestHandle, rawDataBytes, null); } else { - nativeOnUrlComplete(mapPointer, requestHandle, null, + nativeMap.onUrlComplete(requestHandle, null, "Unexpected response code: " + code + " for URL: " + url); } } @@ -1246,7 +1204,7 @@ public void onResponse(final int code, @Nullable final byte[] rawDataBytes) { @Override public void onCancel() { if (httpRequestHandles.remove(requestHandle) == null) { return; } - nativeOnUrlComplete(mapPointer, requestHandle, null, null); + nativeMap.onUrlComplete(requestHandle, null, null); } }; @@ -1360,93 +1318,29 @@ String getFontFallbackFilePath(final int importance, final int weightHint) { return FontConfig.getFontFallback(importance, weightHint); } + // Native map + // ========== + + NativeMap nativeMap; + MapChangeListener mapChangeListener; + // Private members // =============== - long mapPointer; private MapRenderer mapRenderer; private GLViewHolder viewHolder; private MapRegionChangeState currentState = MapRegionChangeState.IDLE; - private AssetManager assetManager; private TouchInput touchInput; - private DisplayMetrics displayMetrics = new DisplayMetrics(); private HttpHandler httpHandler; - private final Map httpRequestHandles = Collections.synchronizedMap(new HashMap()); - MapChangeListener mapChangeListener; + private final Map httpRequestHandles = Collections.synchronizedMap(new HashMap()); private FeaturePickListener featurePickListener; private SceneLoadListener sceneLoadListener; private LabelPickListener labelPickListener; private MarkerPickListener markerPickListener; - private FrameCaptureCallback frameCaptureCallback; - private boolean frameCaptureAwaitCompleteView; private Map clientTileSources; private LongSparseArray markers; private Handler uiThreadHandler; private CameraAnimationCallback cameraAnimationCallback; private CameraAnimationCallback pendingCameraAnimationCallback; private final Object cameraAnimationCallbackLock = new Object(); - private boolean isGLRendererSet = false; - - // Native methods - // ============== - - private synchronized native void nativeOnLowMemory(long mapPtr); - private synchronized native long nativeInit(AssetManager assetManager); - private synchronized native void nativeDispose(long mapPtr); - private synchronized native void nativeShutdown(long mapPtr); - private synchronized native int nativeLoadScene(long mapPtr, String path, String[] updateStrings); - private synchronized native int nativeLoadSceneAsync(long mapPtr, String path, String[] updateStrings); - private synchronized native int nativeLoadSceneYaml(long mapPtr, String yaml, String resourceRoot, String[] updateStrings); - private synchronized native int nativeLoadSceneYamlAsync(long mapPtr, String yaml, String resourceRoot, String[] updateStrings); - - private synchronized native void nativeGetCameraPosition(long mapPtr, double[] lonLatOut, float[] zoomRotationTiltOut); - private synchronized native void nativeUpdateCameraPosition(long mapPtr, int set, double lon, double lat, float zoom, float zoomBy, - float rotation, float rotateBy, float tilt, float tiltBy, - double b1lon, double b1lat, double b2lon, double b2lat, int[] padding, - float duration, int ease); - private synchronized native void nativeFlyTo(long mapPtr, double lon, double lat, float zoom, float duration, float speed); - private synchronized native void nativeGetEnclosingCameraPosition(long mapPtr, double aLng, double aLat, double bLng, double bLat, int[] buffer, double[] lngLatZoom); - private synchronized native void nativeCancelCameraAnimation(long mapPtr); - private synchronized native boolean nativeScreenPositionToLngLat(long mapPtr, double[] coordinates); - private synchronized native boolean nativeLngLatToScreenPosition(long mapPtr, double[] coordinates, boolean clipToViewport); - private synchronized native void nativeSetPixelScale(long mapPtr, float scale); - private synchronized native void nativeSetCameraType(long mapPtr, int type); - private synchronized native int nativeGetCameraType(long mapPtr); - private synchronized native float nativeGetMinZoom(long mapPtr); - private synchronized native void nativeSetMinZoom(long mapPtr, float minZoom); - private synchronized native float nativeGetMaxZoom(long mapPtr); - private synchronized native void nativeSetMaxZoom(long mapPtr, float maxZoom); - private synchronized native void nativeHandleTapGesture(long mapPtr, float posX, float posY); - private synchronized native void nativeHandleDoubleTapGesture(long mapPtr, float posX, float posY); - private synchronized native void nativeHandlePanGesture(long mapPtr, float startX, float startY, float endX, float endY); - private synchronized native void nativeHandleFlingGesture(long mapPtr, float posX, float posY, float velocityX, float velocityY); - private synchronized native void nativeHandlePinchGesture(long mapPtr, float posX, float posY, float scale, float velocity); - private synchronized native void nativeHandleRotateGesture(long mapPtr, float posX, float posY, float rotation); - private synchronized native void nativeHandleShoveGesture(long mapPtr, float distance); - private synchronized native void nativeSetPickRadius(long mapPtr, float radius); - private synchronized native void nativePickFeature(long mapPtr, float posX, float posY); - private synchronized native void nativePickLabel(long mapPtr, float posX, float posY); - private synchronized native void nativePickMarker(long mapPtr, float posX, float posY); - private synchronized native long nativeMarkerAdd(long mapPtr); - private synchronized native boolean nativeMarkerRemove(long mapPtr, long markerID); - private synchronized native boolean nativeMarkerSetStylingFromString(long mapPtr, long markerID, String styling); - private synchronized native boolean nativeMarkerSetStylingFromPath(long mapPtr, long markerID, String path); - private synchronized native boolean nativeMarkerSetBitmap(long mapPtr, long markerID, Bitmap bitmap, float density); - private synchronized native boolean nativeMarkerSetPoint(long mapPtr, long markerID, double lng, double lat); - private synchronized native boolean nativeMarkerSetPointEased(long mapPtr, long markerID, double lng, double lat, float duration, int ease); - private synchronized native boolean nativeMarkerSetPolyline(long mapPtr, long markerID, double[] coordinates, int count); - private synchronized native boolean nativeMarkerSetPolygon(long mapPtr, long markerID, double[] coordinates, int[] rings, int count); - private synchronized native boolean nativeMarkerSetVisible(long mapPtr, long markerID, boolean visible); - private synchronized native boolean nativeMarkerSetDrawOrder(long mapPtr, long markerID, int drawOrder); - private synchronized native void nativeMarkerRemoveAll(long mapPtr); - private synchronized native void nativeUseCachedGlState(long mapPtr, boolean use); - private synchronized native void nativeSetDefaultBackgroundColor(long mapPtr, float r, float g, float b); - - private synchronized native long nativeAddClientDataSource(long mapPtr, String name, boolean generateCentroid); - private synchronized native void nativeRemoveClientDataSource(long mapPtr, long sourcePtr); - - private synchronized native void nativeSetDebugFlag(int flag, boolean on); - - private native void nativeOnUrlComplete(long mapPtr, long requestHandle, byte[] rawDataBytes, String errorMessage); - } diff --git a/platforms/android/tangram/src/main/java/com/mapzen/tangram/MapRenderer.java b/platforms/android/tangram/src/main/java/com/mapzen/tangram/MapRenderer.java index 9f0669c7ec..06a5a9def8 100644 --- a/platforms/android/tangram/src/main/java/com/mapzen/tangram/MapRenderer.java +++ b/platforms/android/tangram/src/main/java/com/mapzen/tangram/MapRenderer.java @@ -15,7 +15,7 @@ class MapRenderer implements GLSurfaceView.Renderer { MapRenderer(MapController mapController, Handler uiThreadHandler) { this.uiThreadHandler = uiThreadHandler; this.map = mapController; - this.mapPointer = mapController.mapPointer; + this.nativeMap = mapController.nativeMap; } // GLSurfaceView.Renderer methods @@ -34,20 +34,14 @@ public void onDrawFrame(final GL10 gl) { final float delta = (newTime - time) / 1000000000.0f; time = newTime; - if (mapPointer <= 0) { - // No native instance is initialized, so stop here. This can happen during Activity - // shutdown when the map has been disposed but the View hasn't been destroyed yet. - return; - } - boolean mapViewComplete; boolean isCameraEasing; boolean isAnimating; synchronized(map) { - int state = nativeUpdate(mapPointer, delta); + int state = nativeMap.update(delta); - nativeRender(mapPointer); + nativeMap.render(); mapViewComplete = (state == VIEW_COMPLETE); isCameraEasing = (state & VIEW_CHANGING) != 0; @@ -93,34 +87,21 @@ public void run() { @Override public void onSurfaceChanged(final GL10 gl, final int width, final int height) { - if (mapPointer <= 0) { - // No native instance is initialized, so stop here. This can happen during Activity - // shutdown when the map has been disposed but the View hasn't been destroyed yet. - return; - } - synchronized (map) { - nativeResize(mapPointer, width, height); + nativeMap.resize(width, height); } } @Override public void onSurfaceCreated(final GL10 gl, final EGLConfig config) { - if (mapPointer <= 0) { - // No native instance is initialized, so stop here. This can happen during Activity - // shutdown when the map has been disposed but the View hasn't been destroyed yet. - return; - } - synchronized (map) { - nativeSetupGL(mapPointer); + nativeMap.setupGL(); } } void captureFrame(MapController.FrameCaptureCallback callback, boolean waitForCompleteView) { frameCaptureCallback = callback; frameCaptureAwaitCompleteView = waitForCompleteView; - } @NonNull @@ -130,11 +111,11 @@ private Bitmap capture() { final int w = view.getWidth(); final int h = view.getHeight(); - final int b[] = new int[w * h]; - final int bt[] = new int[w * h]; + final int[] b = new int[w * h]; + final int[] bt = new int[w * h]; synchronized (map) { - nativeCaptureSnapshot(mapPointer, b); + nativeMap.captureSnapshot(b); } for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { @@ -151,18 +132,11 @@ private Bitmap capture() { private final Handler uiThreadHandler; private final MapController map; - private final long mapPointer; + private final NativeMap nativeMap; private long time = System.nanoTime(); private boolean isPrevCameraEasing = false; private boolean isPrevMapViewComplete = false; - - private native void nativeSetupGL(long mapPtr); - private native void nativeResize(long mapPtr, int width, int height); - private native int nativeUpdate(long mapPtr, float dt); - private native void nativeRender(long mapPtr); - private native void nativeCaptureSnapshot(long mapPtr, int[] buffer); - private MapController.FrameCaptureCallback frameCaptureCallback; private boolean frameCaptureAwaitCompleteView; diff --git a/platforms/android/tangram/src/main/java/com/mapzen/tangram/NativeMap.java b/platforms/android/tangram/src/main/java/com/mapzen/tangram/NativeMap.java new file mode 100644 index 0000000000..643ed7c8d7 --- /dev/null +++ b/platforms/android/tangram/src/main/java/com/mapzen/tangram/NativeMap.java @@ -0,0 +1,81 @@ +package com.mapzen.tangram; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; + +class NativeMap { + + NativeMap(MapController mapController, AssetManager assetManager) { + nativePointer = init(mapController, assetManager); + if (nativePointer <= 0) { + throw new RuntimeException("Unable to create a native Map object! There may be insufficient memory available."); + } + } + + native long init(MapController mapController, AssetManager assetManager); + native void dispose(); + native void shutdown(); + native void onLowMemory(); + native int loadScene(String path, String[] updateStrings); + native int loadSceneAsync(String path, String[] updateStrings); + native int loadSceneYaml(String yaml, String resourceRoot, String[] updateStrings); + native int loadSceneYamlAsync(String yaml, String resourceRoot, String[] updateStrings); + + native void setupGL(); + native void resize(int width, int height); + native int update(float dt); + native void render(); + native void captureSnapshot(int[] buffer); + + native void getCameraPosition(double[] lonLatOut, float[] zoomRotationTiltOut); + native void updateCameraPosition(int set, double lon, double lat, float zoom, float zoomBy, + float rotation, float rotateBy, float tilt, float tiltBy, + double b1lon, double b1lat, double b2lon, double b2lat, int[] padding, + float duration, int ease); + native void flyTo(double lon, double lat, float zoom, float duration, float speed); + native void getEnclosingCameraPosition(double aLng, double aLat, double bLng, double bLat, int[] buffer, double[] lngLatZoom); + native void cancelCameraAnimation(); + native boolean screenPositionToLngLat(double[] coordinates); + native boolean lngLatToScreenPosition(double[] coordinates, boolean clipToViewport); + native void setPixelScale(float scale); + native void setCameraType(int type); + native int getCameraType(); + native float getMinZoom(); + native void setMinZoom(float minZoom); + native float getMaxZoom(); + native void setMaxZoom(float maxZoom); + native void handleTapGesture(float posX, float posY); + native void handleDoubleTapGesture(float posX, float posY); + native void handlePanGesture(float startX, float startY, float endX, float endY); + native void handleFlingGesture(float posX, float posY, float velocityX, float velocityY); + native void handlePinchGesture(float posX, float posY, float scale, float velocity); + native void handleRotateGesture(float posX, float posY, float rotation); + native void handleShoveGesture(float distance); + native void setPickRadius(float radius); + native void pickFeature(float posX, float posY); + native void pickLabel(float posX, float posY); + native void pickMarker(float posX, float posY); + native long markerAdd(); + native boolean markerRemove(long markerID); + native boolean markerSetStylingFromString(long markerID, String styling); + native boolean markerSetStylingFromPath(long markerID, String path); + native boolean markerSetBitmap(long markerID, Bitmap bitmap, float density); + native boolean markerSetPoint(long markerID, double lng, double lat); + native boolean markerSetPointEased(long markerID, double lng, double lat, float duration, int ease); + native boolean markerSetPolyline(long markerID, double[] coordinates, int count); + native boolean markerSetPolygon(long markerID, double[] coordinates, int[] rings, int count); + native boolean markerSetVisible(long markerID, boolean visible); + native boolean markerSetDrawOrder(long markerID, int drawOrder); + native void markerRemoveAll(); + native void useCachedGlState(boolean use); + native void setDefaultBackgroundColor(float r, float g, float b); + + native long addClientDataSource(String name, boolean generateCentroid); + native void removeClientDataSource(long sourcePtr); + + native void setDebugFlag(int flag, boolean on); + + native void onUrlComplete(long requestHandle, byte[] rawDataBytes, String errorMessage); + + private final long nativePointer; +} From dd948613daf9f3aa74a631fae0ad92d4ccb53eae Mon Sep 17 00:00:00 2001 From: Matt Blair Date: Mon, 14 Sep 2020 02:11:57 -0400 Subject: [PATCH 3/3] missing newline --- platforms/android/tangram/src/main/cpp/JniOnLoad.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/android/tangram/src/main/cpp/JniOnLoad.cpp b/platforms/android/tangram/src/main/cpp/JniOnLoad.cpp index 9d673da3b7..e497f9b047 100644 --- a/platforms/android/tangram/src/main/cpp/JniOnLoad.cpp +++ b/platforms/android/tangram/src/main/cpp/JniOnLoad.cpp @@ -24,4 +24,4 @@ extern "C" JNIEXPORT void JNI_OnUnload(JavaVM* javaVM, void*) { } Tangram::AndroidMap::jniOnUnload(javaVM, jniEnv); -} \ No newline at end of file +}