Rename Gradle targets Android (#2226)

This commit is contained in:
Bart Louwers
2024-04-02 23:15:50 +02:00
committed by GitHub
parent 37170ed61c
commit 9f8ae7a98b
1081 changed files with 132 additions and 370 deletions

View File

@@ -0,0 +1,4 @@
dependency-graph-mapbox-libraries.png
src/test/resources/mobile-event*
local.properties

View File

@@ -0,0 +1,8 @@
# MapLibre Native for Android
This is the API documentation for the Android API of [MapLibre Native](https://github.com/maplibre/maplibre-native).
## See Also
- [Getting Started Guide](https://maplibre.org/maplibre-native/docs/book/android/getting-started-guide.html)
- [Source code Android API](https://github.com/maplibre/maplibre-native/tree/main/platform/android)

View File

@@ -0,0 +1,168 @@
import org.jetbrains.dokka.gradle.DokkaTask
plugins {
id 'org.jmailen.kotlinter'
id 'org.jetbrains.dokka' version '1.7.20'
}
apply plugin: 'com.android.library'
apply plugin: "com.jaredsburrows.license"
apply plugin: 'kotlin-android'
apply from: "${rootDir}/gradle/native-build.gradle"
dependencies {
lintChecks project(":MapLibreAndroidLint")
api dependenciesList.mapboxJavaGeoJSON
api dependenciesList.mapboxAndroidGestures
implementation dependenciesList.mapboxJavaTurf
implementation dependenciesList.supportAnnotations
implementation dependenciesList.supportFragmentV4
implementation dependenciesList.okhttp3
implementation dependenciesList.timber
implementation dependenciesList.interpolator
testImplementation dependenciesList.junit
testImplementation dependenciesList.mockito
testImplementation dependenciesList.mockk
testImplementation dependenciesList.robolectric
testImplementation dependenciesList.commonsIO
testImplementation dependenciesList.assertjcore
androidTestImplementation dependenciesList.testRunner
androidTestImplementation dependenciesList.testRules
}
tasks.withType(DokkaTask.class) {
moduleName.set("MapLibre Native for Android")
dokkaSourceSets {
configureEach {
includes.from("README.md")
}
}
}
android {
compileSdkVersion androidVersions.compileSdkVersion
buildToolsVersion androidSdkVersions.buildToolsVersion
defaultConfig {
minSdkVersion androidVersions.minSdkVersion
targetSdkVersion androidVersions.targetSdkVersion
buildConfigField "String", "GIT_REVISION_SHORT", String.format("\"%s\"", getGitRevision())
buildConfigField "String", "GIT_REVISION", String.format("\"%s\"", getGitRevision(false))
buildConfigField "String", "MAPBOX_SDK_IDENTIFIER", String.format("\"%s\"", "mapbox-maps-android")
buildConfigField "String", "MAPBOX_SDK_VERSION", String.format("\"%s\"", project.VERSION_NAME)
buildConfigField "String", "MAPBOX_VERSION_STRING", String.format("\"Mapbox/%s\"", project.VERSION_NAME)
buildConfigField "String", "MAPBOX_EVENTS_USER_AGENT", String.format("\"mapbox-maps-android/%s\"", project.VERSION_NAME)
consumerProguardFiles 'proguard-rules.pro'
externalNativeBuild {
cmake {
arguments "-DMLN_LEGACY_RENDERER=ON", "-DMLN_DRAWABLE_RENDERER=OFF"
}
}
}
flavorDimensions += "renderer"
productFlavors {
create("legacy") {
dimension = "renderer"
externalNativeBuild {
cmake {
arguments "-DMLN_LEGACY_RENDERER=ON", "-DMLN_DRAWABLE_RENDERER=OFF"
}
}
}
create("drawable") {
dimension = "renderer"
externalNativeBuild {
cmake {
arguments "-DMLN_LEGACY_RENDERER=OFF", "-DMLN_DRAWABLE_RENDERER=ON"
}
}
}
}
// build native libraries
List nativeTargets = ["maplibre"]
if (project.hasProperty("mapbox.with_test")) {
nativeTargets.add("mbgl-test")
}
if (project.hasProperty("mapbox.with_benchmark")) {
nativeTargets.add("mbgl-benchmark")
}
nativeBuild(nativeTargets)
// avoid naming conflicts, force usage of prefix
resourcePrefix 'maplibre_'
sourceSets {
// limit amount of exposed library resources
main.res.srcDirs += 'src/main/res-public'
}
testOptions {
unitTests {
returnDefaultValues true
// Roboelectric 4.0 required config
// http://robolectric.org/migrating/#migrating-to-40
includeAndroidResources = true
}
}
buildTypes {
debug {
testCoverageEnabled false
jniDebuggable true
debuggable true
}
}
namespace 'org.maplibre.android'
lint {
checkAllWarnings true
disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission', 'WrongThreadInterprocedural'
warningsAsErrors false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
licenseReport {
generateHtmlReport = false
generateJsonReport = true
copyHtmlReportToAssets = false
copyJsonReportToAssets = false
}
def static getGitRevision(shortRev = true) {
def cmd = shortRev ? "git rev-parse --short HEAD" : "git rev-parse HEAD"
def proc = cmd.execute()
def ref = proc.text.trim()
return ref
}
configurations {
all*.exclude group: 'commons-logging', module: 'commons-logging'
all*.exclude group: 'commons-collections', module: 'commons-collections'
}
apply from: "${rootDir}/gradle/gradle-javadoc.gradle"
apply from: "${rootDir}/gradle/gradle-checkstyle.gradle"
apply from: "${rootDir}/gradle/gradle-dependencies-graph.gradle"
apply from: "${rootDir}/gradle/gradle-update-vendor-modules.gradle"
apply from: "${rootDir}/gradle/android-nitpick.gradle"
apply from: "${rootDir}/gradle/gradle-publish.gradle"
// intentionally disabled
// apply from: "${rootDir}/gradle/jacoco-report.gradle"

View File

@@ -0,0 +1,9 @@
VERSION_NAME=11.0.0-pre4
# Only build native dependencies for the current ABI
# See https://code.google.com/p/android/issues/detail?id=221098#c20
android.buildOnlyTargetAbi=true
# Roboelectric 4.0 required config for < Android Studio v3.3.0
# http://robolectric.org/migrating/#migrating-to-40
android.enableUnitTestBinaryResources=true

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<lint>
<!-- Ignore errors caused by OkHTTP -->
<issue id="InvalidPackage" severity="ignore">
<ignore path="*/okio-1.2.0.jar" />
</issue>
</lint>

View File

@@ -0,0 +1,49 @@
# By default, the flags in this file are appended to flags specified
# in ../sdk/tools/proguard/proguard-android.txt,
# contents of this file will be appended into proguard-android.txt
-keepattributes Signature, *Annotation*, EnclosingMethod
# Reflection on classes from native code
-keep class com.google.gson.JsonArray { *; }
-keep class com.google.gson.JsonElement { *; }
-keep class com.google.gson.JsonObject { *; }
-keep class com.google.gson.JsonPrimitive { *; }
-dontnote com.google.gson.**
# dontnote for keeps the entry point x but not the descriptor class y
-dontnote org.maplibre.android.maps.MapLibreMap$OnFpsChangedListener
-dontnote org.maplibre.android.style.layers.PropertyValue
-dontnote org.maplibre.android.maps.MapLibreMap
-dontnote org.maplibre.android.maps.MapLibreMapOptions
-dontnote org.maplibre.android.log.LoggerDefinition
# config for okhttp 3.11.0, https://github.com/square/okhttp/pull/3354
-dontwarn javax.annotation.**
-dontnote okhttp3.internal.**
-dontwarn org.codehaus.**
# config for mapbox-sdk-geojson:3.0.1
-keep class com.mapbox.geojson.** { *; }
-dontwarn com.google.auto.value.**
# config for additional notes
-dontnote org.robolectric.Robolectric
-dontnote libcore.io.Memory
-dontnote com.google.protobuf.**
-dontnote android.net.**
-dontnote org.apache.http.**
# config for mapbox-sdk-services
# while we don't include this dependency directly
# a large amount of users combine it with our SDK
# we aren't able to provide a proguard config in that project (jar vs aar)
-dontwarn com.sun.xml.internal.ws.spi.db.*
# okhttp
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.conscrypt.*
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE

View File

@@ -0,0 +1,219 @@
cmake_minimum_required(VERSION 3.10)
add_subdirectory(../../../../../
../../../../../${ANDROID_ABI})
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
target_compile_options(mbgl-compiler-options INTERFACE -frtti)
add_library(maplibre SHARED
${PROJECT_SOURCE_DIR}/main.cpp
${PROJECT_SOURCE_DIR}/jni_native.cpp
${PROJECT_SOURCE_DIR}/jni_native.hpp
${PROJECT_SOURCE_DIR}/file_source.cpp
${PROJECT_SOURCE_DIR}/file_source.hpp
${PROJECT_SOURCE_DIR}/http_file_source.cpp
${PROJECT_SOURCE_DIR}/i18n/collator.cpp
${PROJECT_SOURCE_DIR}/i18n/collator_jni.hpp
${PROJECT_SOURCE_DIR}/i18n/number_format.cpp
${PROJECT_SOURCE_DIR}/i18n/number_format_jni.hpp
${PROJECT_SOURCE_DIR}/logger.cpp
${PROJECT_SOURCE_DIR}/logger.hpp
${PROJECT_SOURCE_DIR}/logging_android.cpp
${PROJECT_SOURCE_DIR}/text/local_glyph_rasterizer.cpp
${PROJECT_SOURCE_DIR}/text/local_glyph_rasterizer_jni.hpp
${PROJECT_SOURCE_DIR}/android_renderer_backend.cpp
${PROJECT_SOURCE_DIR}/android_renderer_backend.hpp
${PROJECT_SOURCE_DIR}/android_renderer_frontend.cpp
${PROJECT_SOURCE_DIR}/android_renderer_frontend.hpp
${PROJECT_SOURCE_DIR}/annotation/marker.cpp
${PROJECT_SOURCE_DIR}/annotation/marker.hpp
${PROJECT_SOURCE_DIR}/annotation/multi_point.hpp
${PROJECT_SOURCE_DIR}/annotation/polygon.cpp
${PROJECT_SOURCE_DIR}/annotation/polygon.hpp
${PROJECT_SOURCE_DIR}/annotation/polyline.cpp
${PROJECT_SOURCE_DIR}/annotation/polyline.hpp
${PROJECT_SOURCE_DIR}/asset_manager.hpp
${PROJECT_SOURCE_DIR}/asset_manager_file_source.cpp
${PROJECT_SOURCE_DIR}/asset_manager_file_source.hpp
${PROJECT_SOURCE_DIR}/connectivity_listener.cpp
${PROJECT_SOURCE_DIR}/connectivity_listener.hpp
${PROJECT_SOURCE_DIR}/conversion/collection.cpp
${PROJECT_SOURCE_DIR}/conversion/collection.hpp
${PROJECT_SOURCE_DIR}/conversion/color.cpp
${PROJECT_SOURCE_DIR}/conversion/color.hpp
${PROJECT_SOURCE_DIR}/conversion/constant.cpp
${PROJECT_SOURCE_DIR}/conversion/constant.hpp
${PROJECT_SOURCE_DIR}/conversion/conversion.hpp
${PROJECT_SOURCE_DIR}/geojson/feature.cpp
${PROJECT_SOURCE_DIR}/geojson/feature.hpp
${PROJECT_SOURCE_DIR}/geojson/feature_collection.cpp
${PROJECT_SOURCE_DIR}/geojson/feature_collection.hpp
${PROJECT_SOURCE_DIR}/geojson/geometry.cpp
${PROJECT_SOURCE_DIR}/geojson/geometry.hpp
${PROJECT_SOURCE_DIR}/geojson/geometry_collection.cpp
${PROJECT_SOURCE_DIR}/geojson/geometry_collection.hpp
${PROJECT_SOURCE_DIR}/geojson/line_string.cpp
${PROJECT_SOURCE_DIR}/geojson/line_string.hpp
${PROJECT_SOURCE_DIR}/geojson/multi_line_string.cpp
${PROJECT_SOURCE_DIR}/geojson/multi_line_string.hpp
${PROJECT_SOURCE_DIR}/geojson/multi_point.cpp
${PROJECT_SOURCE_DIR}/geojson/multi_point.hpp
${PROJECT_SOURCE_DIR}/geojson/multi_polygon.cpp
${PROJECT_SOURCE_DIR}/geojson/multi_polygon.hpp
${PROJECT_SOURCE_DIR}/geojson/point.cpp
${PROJECT_SOURCE_DIR}/geojson/point.hpp
${PROJECT_SOURCE_DIR}/geojson/polygon.cpp
${PROJECT_SOURCE_DIR}/geojson/polygon.hpp
${PROJECT_SOURCE_DIR}/geojson/util.hpp
${PROJECT_SOURCE_DIR}/geometry/lat_lng.cpp
${PROJECT_SOURCE_DIR}/geometry/lat_lng.hpp
${PROJECT_SOURCE_DIR}/geometry/lat_lng_bounds.cpp
${PROJECT_SOURCE_DIR}/geometry/lat_lng_bounds.hpp
${PROJECT_SOURCE_DIR}/geometry/lat_lng_quad.cpp
${PROJECT_SOURCE_DIR}/geometry/lat_lng_quad.hpp
${PROJECT_SOURCE_DIR}/geometry/projected_meters.cpp
${PROJECT_SOURCE_DIR}/geometry/projected_meters.hpp
${PROJECT_SOURCE_DIR}/graphics/pointf.cpp
${PROJECT_SOURCE_DIR}/graphics/pointf.hpp
${PROJECT_SOURCE_DIR}/graphics/rectf.cpp
${PROJECT_SOURCE_DIR}/graphics/rectf.hpp
${PROJECT_SOURCE_DIR}/gson/json_array.cpp
${PROJECT_SOURCE_DIR}/gson/json_array.hpp
${PROJECT_SOURCE_DIR}/gson/json_element.cpp
${PROJECT_SOURCE_DIR}/gson/json_element.hpp
${PROJECT_SOURCE_DIR}/gson/json_object.cpp
${PROJECT_SOURCE_DIR}/gson/json_object.hpp
${PROJECT_SOURCE_DIR}/gson/json_primitive.cpp
${PROJECT_SOURCE_DIR}/gson/json_primitive.hpp
${PROJECT_SOURCE_DIR}/java/util.cpp
${PROJECT_SOURCE_DIR}/java/util.hpp
${PROJECT_SOURCE_DIR}/java_types.cpp
${PROJECT_SOURCE_DIR}/java_types.hpp
${PROJECT_SOURCE_DIR}/java/util.cpp
${PROJECT_SOURCE_DIR}/java/util.hpp
${PROJECT_SOURCE_DIR}/java_types.cpp
${PROJECT_SOURCE_DIR}/java_types.hpp
${PROJECT_SOURCE_DIR}/maplibre.cpp
${PROJECT_SOURCE_DIR}/maplibre.hpp
${PROJECT_SOURCE_DIR}/map/camera_position.cpp
${PROJECT_SOURCE_DIR}/map/camera_position.hpp
${PROJECT_SOURCE_DIR}/map/image.cpp
${PROJECT_SOURCE_DIR}/map/image.hpp
${PROJECT_SOURCE_DIR}/map_renderer.cpp
${PROJECT_SOURCE_DIR}/map_renderer.hpp
${PROJECT_SOURCE_DIR}/map_renderer_runnable.cpp
${PROJECT_SOURCE_DIR}/map_renderer_runnable.hpp
${PROJECT_SOURCE_DIR}/native_map_view.cpp
${PROJECT_SOURCE_DIR}/native_map_view.hpp
${PROJECT_SOURCE_DIR}/offline/offline_manager.cpp
${PROJECT_SOURCE_DIR}/offline/offline_manager.hpp
${PROJECT_SOURCE_DIR}/offline/offline_region.cpp
${PROJECT_SOURCE_DIR}/offline/offline_region.hpp
${PROJECT_SOURCE_DIR}/offline/offline_region_definition.cpp
${PROJECT_SOURCE_DIR}/offline/offline_region_definition.hpp
${PROJECT_SOURCE_DIR}/offline/offline_region_error.cpp
${PROJECT_SOURCE_DIR}/offline/offline_region_error.hpp
${PROJECT_SOURCE_DIR}/offline/offline_region_status.cpp
${PROJECT_SOURCE_DIR}/offline/offline_region_status.hpp
${PROJECT_SOURCE_DIR}/snapshotter/map_snapshot.cpp
${PROJECT_SOURCE_DIR}/snapshotter/map_snapshot.hpp
${PROJECT_SOURCE_DIR}/snapshotter/map_snapshotter.cpp
${PROJECT_SOURCE_DIR}/snapshotter/map_snapshotter.hpp
${PROJECT_SOURCE_DIR}/style/android_conversion.hpp
${PROJECT_SOURCE_DIR}/style/conversion/filter.cpp
${PROJECT_SOURCE_DIR}/style/conversion/filter.hpp
${PROJECT_SOURCE_DIR}/style/conversion/position.cpp
${PROJECT_SOURCE_DIR}/style/conversion/position.hpp
${PROJECT_SOURCE_DIR}/style/conversion/property_expression.hpp
${PROJECT_SOURCE_DIR}/style/conversion/property_value.hpp
${PROJECT_SOURCE_DIR}/style/conversion/transition_options.cpp
${PROJECT_SOURCE_DIR}/style/conversion/transition_options.hpp
${PROJECT_SOURCE_DIR}/style/conversion/url_or_tileset.cpp
${PROJECT_SOURCE_DIR}/style/conversion/url_or_tileset.hpp
${PROJECT_SOURCE_DIR}/style/formatted.cpp
${PROJECT_SOURCE_DIR}/style/formatted.hpp
${PROJECT_SOURCE_DIR}/style/formatted_section.cpp
${PROJECT_SOURCE_DIR}/style/formatted_section.hpp
${PROJECT_SOURCE_DIR}/style/layers/background_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/background_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/circle_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/circle_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/custom_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/custom_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/fill_extrusion_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/fill_extrusion_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/fill_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/fill_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/heatmap_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/heatmap_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/hillshade_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/hillshade_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/layer_manager.cpp
${PROJECT_SOURCE_DIR}/style/layers/layer_manager.hpp
${PROJECT_SOURCE_DIR}/style/layers/line_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/line_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/raster_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/raster_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/symbol_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/symbol_layer.hpp
${PROJECT_SOURCE_DIR}/style/layers/location_indicator_layer.cpp
${PROJECT_SOURCE_DIR}/style/layers/location_indicator_layer.hpp
${PROJECT_SOURCE_DIR}/style/light.cpp
${PROJECT_SOURCE_DIR}/style/light.hpp
${PROJECT_SOURCE_DIR}/style/position.cpp
${PROJECT_SOURCE_DIR}/style/position.hpp
${PROJECT_SOURCE_DIR}/style/sources/custom_geometry_source.cpp
${PROJECT_SOURCE_DIR}/style/sources/custom_geometry_source.hpp
${PROJECT_SOURCE_DIR}/style/sources/geojson_source.cpp
${PROJECT_SOURCE_DIR}/style/sources/geojson_source.hpp
${PROJECT_SOURCE_DIR}/style/sources/image_source.cpp
${PROJECT_SOURCE_DIR}/style/sources/image_source.hpp
${PROJECT_SOURCE_DIR}/style/sources/raster_dem_source.cpp
${PROJECT_SOURCE_DIR}/style/sources/raster_dem_source.hpp
${PROJECT_SOURCE_DIR}/style/sources/raster_source.cpp
${PROJECT_SOURCE_DIR}/style/sources/raster_source.hpp
${PROJECT_SOURCE_DIR}/style/sources/source.cpp
${PROJECT_SOURCE_DIR}/style/sources/source.hpp
${PROJECT_SOURCE_DIR}/style/sources/unknown_source.cpp
${PROJECT_SOURCE_DIR}/style/sources/unknown_source.hpp
${PROJECT_SOURCE_DIR}/style/sources/vector_source.cpp
${PROJECT_SOURCE_DIR}/style/sources/vector_source.hpp
${PROJECT_SOURCE_DIR}/style/transition_options.cpp
${PROJECT_SOURCE_DIR}/style/transition_options.hpp
${PROJECT_SOURCE_DIR}/style/value.cpp
${PROJECT_SOURCE_DIR}/style/value.hpp
${PROJECT_SOURCE_DIR}/util/default_style.cpp
${PROJECT_SOURCE_DIR}/util/tile_server_options.cpp
${PROJECT_SOURCE_DIR}/util/default_style.hpp
${PROJECT_SOURCE_DIR}/util/tile_server_options.hpp)
target_include_directories(
maplibre
PRIVATE ${PROJECT_SOURCE_DIR}
../../../../../src
../../../src
../../../../default/include)
target_compile_options(maplibre
PRIVATE $<$<CONFIG:Release>:-Oz>
$<$<CONFIG:Release>:-Qunused-arguments>
$<$<CONFIG:Release>:-flto>
$<$<CONFIG:Release>:-fvisibility=hidden>
$<$<CONFIG:Release>:-fvisibility-inlines-hidden>)
target_link_libraries(maplibre
PRIVATE $<$<CONFIG:Release>:-Oz>
$<$<CONFIG:Release>:-Wl,--icf=all>
$<$<CONFIG:Release>:-Wl,--gc-sections>
$<$<CONFIG:Release>:-flto>
$<$<CONFIG:Release>:-fuse-ld=gold>
Mapbox::Base::jni.hpp
mbgl-core
mbgl-vendor-unique_resource)
install(TARGETS maplibre LIBRARY DESTINATION lib)

View File

@@ -0,0 +1,78 @@
#include "android_renderer_backend.hpp"
#include <mbgl/gfx/backend_scope.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/renderable_resource.hpp>
#include <EGL/egl.h>
#include <cassert>
namespace mbgl {
namespace android {
class AndroidGLRenderableResource final : public mbgl::gl::RenderableResource {
public:
AndroidGLRenderableResource(AndroidRendererBackend& backend_)
: backend(backend_) {}
void bind() override {
assert(gfx::BackendScope::exists());
backend.setFramebufferBinding(0);
backend.setViewport(0, 0, backend.getSize());
}
void swap() override { backend.swap(); }
private:
AndroidRendererBackend& backend;
};
AndroidRendererBackend::AndroidRendererBackend()
: gl::RendererBackend(gfx::ContextMode::Unique),
mbgl::gfx::Renderable({64, 64}, std::make_unique<AndroidGLRenderableResource>(*this)) {}
AndroidRendererBackend::~AndroidRendererBackend() = default;
gl::ProcAddress AndroidRendererBackend::getExtensionFunctionPointer(const char* name) {
assert(gfx::BackendScope::exists());
return eglGetProcAddress(name);
}
void AndroidRendererBackend::updateViewPort() {
assert(gfx::BackendScope::exists());
setViewport(0, 0, size);
}
void AndroidRendererBackend::resizeFramebuffer(int width, int height) {
size = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
}
PremultipliedImage AndroidRendererBackend::readFramebuffer() {
assert(gfx::BackendScope::exists());
return gl::RendererBackend::readFramebuffer(size);
}
void AndroidRendererBackend::updateAssumedState() {
assumeFramebufferBinding(0);
assumeViewport(0, 0, size);
}
void AndroidRendererBackend::markContextLost() {
if (context) {
getContext<gl::Context>().setCleanupOnDestruction(false);
}
}
void AndroidRendererBackend::setSwapBehavior(SwapBehaviour swapBehaviour_) {
swapBehaviour = swapBehaviour_;
}
void AndroidRendererBackend::swap() {
if (swapBehaviour == SwapBehaviour::Flush) {
static_cast<gl::Context&>(getContext()).finish();
}
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,47 @@
#pragma once
#include <mbgl/gfx/renderable.hpp>
#include <mbgl/gl/renderer_backend.hpp>
namespace mbgl {
namespace android {
class AndroidRendererBackend : public gl::RendererBackend, public mbgl::gfx::Renderable {
public:
AndroidRendererBackend();
~AndroidRendererBackend() override;
void updateViewPort();
// Ensures the current context is not cleaned up when destroyed
void markContextLost();
void resizeFramebuffer(int width, int height);
PremultipliedImage readFramebuffer();
void setSwapBehavior(SwapBehaviour swapBehaviour);
void swap();
private:
SwapBehaviour swapBehaviour = SwapBehaviour::NoFlush;
// mbgl::gfx::RendererBackend implementation
public:
mbgl::gfx::Renderable& getDefaultRenderable() override { return *this; }
protected:
void activate() override {
// no-op
}
void deactivate() override {
// no-op
}
// mbgl::gl::RendererBackend implementation
protected:
mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) override;
void updateAssumedState() override;
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,137 @@
#include "android_renderer_frontend.hpp"
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/renderer/renderer.hpp>
#include <mbgl/renderer/renderer_observer.hpp>
#include <mbgl/util/async_task.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/geojson.hpp>
#include "android_renderer_backend.hpp"
namespace mbgl {
namespace android {
// Forwards RendererObserver signals to the given
// Delegate RendererObserver on the given RunLoop
class ForwardingRendererObserver : public RendererObserver {
public:
ForwardingRendererObserver(util::RunLoop& mapRunLoop, RendererObserver& delegate_)
: mailbox(std::make_shared<Mailbox>(mapRunLoop)),
delegate(delegate_, mailbox) {}
~ForwardingRendererObserver() { mailbox->close(); }
void onInvalidate() override { delegate.invoke(&RendererObserver::onInvalidate); }
void onResourceError(std::exception_ptr err) override { delegate.invoke(&RendererObserver::onResourceError, err); }
void onWillStartRenderingMap() override { delegate.invoke(&RendererObserver::onWillStartRenderingMap); }
void onWillStartRenderingFrame() override { delegate.invoke(&RendererObserver::onWillStartRenderingFrame); }
void onDidFinishRenderingFrame(RenderMode mode,
bool repaintNeeded,
bool placementChanged,
double frameEncodingTime,
double frameRenderingTime) override {
void (RendererObserver::*f)(
RenderMode, bool, bool, double, double) = &RendererObserver::onDidFinishRenderingFrame;
delegate.invoke(f, mode, repaintNeeded, placementChanged, frameEncodingTime, frameRenderingTime);
}
void onDidFinishRenderingMap() override { delegate.invoke(&RendererObserver::onDidFinishRenderingMap); }
void onStyleImageMissing(const std::string& id, const StyleImageMissingCallback& done) override {
delegate.invoke(&RendererObserver::onStyleImageMissing, id, done);
}
void onRemoveUnusedStyleImages(const std::vector<std::string>& ids) override {
delegate.invoke(&RendererObserver::onRemoveUnusedStyleImages, ids);
}
private:
std::shared_ptr<Mailbox> mailbox;
ActorRef<RendererObserver> delegate;
};
AndroidRendererFrontend::AndroidRendererFrontend(MapRenderer& mapRenderer_)
: mapRenderer(mapRenderer_),
mapRunLoop(util::RunLoop::Get()),
updateAsyncTask(std::make_unique<util::AsyncTask>([this]() {
mapRenderer.update(std::move(updateParams));
mapRenderer.requestRender();
})) {}
AndroidRendererFrontend::~AndroidRendererFrontend() = default;
void AndroidRendererFrontend::reset() {
mapRenderer.reset();
}
void AndroidRendererFrontend::setObserver(RendererObserver& observer) {
assert(util::RunLoop::Get());
// Don't call the Renderer directly, but use MapRenderer#setObserver to make sure
// the Renderer may be re-initialised without losing the RendererObserver reference.
mapRenderer.setObserver(std::make_unique<ForwardingRendererObserver>(*mapRunLoop, observer));
}
void AndroidRendererFrontend::update(std::shared_ptr<UpdateParameters> params) {
updateParams = std::move(params);
updateAsyncTask->send();
}
void AndroidRendererFrontend::reduceMemoryUse() {
mapRenderer.actor().invoke(&Renderer::reduceMemoryUse);
}
std::vector<Feature> AndroidRendererFrontend::querySourceFeatures(const std::string& sourceID,
const SourceQueryOptions& options) const {
// Waits for the result from the orchestration thread and returns
return mapRenderer.actor().ask(&Renderer::querySourceFeatures, sourceID, options).get();
}
std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenBox& box,
const RenderedQueryOptions& options) const {
// Select the right overloaded method
std::vector<Feature> (Renderer::*fn)(const ScreenBox&, const RenderedQueryOptions&)
const = &Renderer::queryRenderedFeatures;
// Waits for the result from the orchestration thread and returns
return mapRenderer.actor().ask(fn, box, options).get();
}
std::vector<Feature> AndroidRendererFrontend::queryRenderedFeatures(const ScreenCoordinate& point,
const RenderedQueryOptions& options) const {
// Select the right overloaded method
std::vector<Feature> (Renderer::*fn)(const ScreenCoordinate&, const RenderedQueryOptions&)
const = &Renderer::queryRenderedFeatures;
// Waits for the result from the orchestration thread and returns
return mapRenderer.actor().ask(fn, point, options).get();
}
AnnotationIDs AndroidRendererFrontend::queryPointAnnotations(const ScreenBox& box) const {
// Waits for the result from the orchestration thread and returns
return mapRenderer.actor().ask(&Renderer::queryPointAnnotations, box).get();
}
AnnotationIDs AndroidRendererFrontend::queryShapeAnnotations(const ScreenBox& box) const {
// Waits for the result from the orchestration thread and returns
return mapRenderer.actor().ask(&Renderer::queryShapeAnnotations, box).get();
}
FeatureExtensionValue AndroidRendererFrontend::queryFeatureExtensions(
const std::string& sourceID,
const Feature& feature,
const std::string& extension,
const std::string& extensionField,
const std::optional<std::map<std::string, mbgl::Value>>& args) const {
return mapRenderer.actor()
.ask(&Renderer::queryFeatureExtensions, sourceID, feature, extension, extensionField, args)
.get();
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,65 @@
#pragma once
#include <mbgl/actor/actor.hpp>
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/renderer/renderer_frontend.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/run_loop.hpp>
#include <functional>
#include <memory>
#include <vector>
#include <string>
#include <mbgl/util/geojson.hpp>
#include "map_renderer.hpp"
namespace mbgl {
class RenderedQueryOptions;
class SourceQueryOptions;
namespace util {
class AsyncTask;
} // namespace util
namespace android {
class AndroidRendererFrontend : public RendererFrontend {
public:
AndroidRendererFrontend(MapRenderer&);
~AndroidRendererFrontend() override;
void reset() override;
void setObserver(RendererObserver&) override;
void update(std::shared_ptr<UpdateParameters>) override;
// Feature querying
std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate&, const RenderedQueryOptions&) const;
std::vector<Feature> queryRenderedFeatures(const ScreenBox&, const RenderedQueryOptions&) const;
std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const;
AnnotationIDs queryPointAnnotations(const ScreenBox& box) const;
AnnotationIDs queryShapeAnnotations(const ScreenBox& box) const;
// Feature extension query
FeatureExtensionValue queryFeatureExtensions(const std::string& sourceID,
const Feature& feature,
const std::string& extension,
const std::string& extensionField,
const std::optional<std::map<std::string, mbgl::Value>>& args) const;
// Memory
void reduceMemoryUse();
private:
MapRenderer& mapRenderer;
util::RunLoop* mapRunLoop;
std::unique_ptr<util::AsyncTask> updateAsyncTask;
std::shared_ptr<UpdateParameters> updateParams;
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,23 @@
#include "marker.hpp"
namespace mbgl {
namespace android {
mbgl::Point<double> Marker::getPosition(jni::JNIEnv& env, const jni::Object<Marker>& marker) {
static auto& javaClass = jni::Class<Marker>::Singleton(env);
static auto positionField = javaClass.GetField<jni::Object<LatLng>>(env, "position");
return LatLng::getGeometry(env, marker.Get(env, positionField));
}
std::string Marker::getIconId(jni::JNIEnv& env, const jni::Object<Marker>& marker) {
static auto& javaClass = jni::Class<Marker>::Singleton(env);
static auto iconIdField = javaClass.GetField<jni::String>(env, "iconId");
return jni::Make<std::string>(env, marker.Get(env, iconIdField));
}
void Marker::registerNative(jni::JNIEnv& env) {
jni::Class<Marker>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,25 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
#include <string>
#include "../geometry/lat_lng.hpp"
namespace mbgl {
namespace android {
class Marker : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "org/maplibre/android/annotations/Marker"; };
static mbgl::Point<double> getPosition(jni::JNIEnv&, const jni::Object<Marker>&);
static std::string getIconId(jni::JNIEnv&, const jni::Object<Marker>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,32 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
#include "../geometry/lat_lng.hpp"
#include "../java/util.hpp"
namespace mbgl {
namespace android {
class MultiPoint : protected mbgl::util::noncopyable {
protected:
template <class Geometry>
static Geometry toGeometry(JNIEnv& env, const jni::Object<java::util::List>& pointsList) {
auto jarray = java::util::List::toArray<LatLng>(env, pointsList);
std::size_t size = jarray.Length(env);
Geometry geometry;
geometry.reserve(size);
for (std::size_t i = 0; i < size; i++) {
geometry.push_back(LatLng::getGeometry(env, jarray.Get(env, i)));
}
return geometry;
}
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,38 @@
#include "polygon.hpp"
#include "../conversion/color.hpp"
namespace mbgl {
namespace android {
mbgl::FillAnnotation Polygon::toAnnotation(jni::JNIEnv& env, const jni::Object<Polygon>& polygon) {
static auto& javaClass = jni::Class<Polygon>::Singleton(env);
static auto points = javaClass.GetField<jni::Object<java::util::List>>(env, "points");
static auto holes = javaClass.GetField<jni::Object<java::util::List>>(env, "holes");
static auto alpha = javaClass.GetField<float>(env, "alpha");
static auto fillColor = javaClass.GetField<int>(env, "fillColor");
static auto strokeColor = javaClass.GetField<int>(env, "strokeColor");
mbgl::Polygon<double> geometry{MultiPoint::toGeometry<mbgl::LinearRing<double>>(env, polygon.Get(env, points))};
auto jHoleListsArray = java::util::List::toArray<java::util::List>(env, polygon.Get(env, holes));
std::size_t jHoleListsSize = jHoleListsArray.Length(env);
for (std::size_t i = 0; i < jHoleListsSize; i++) {
geometry.push_back(MultiPoint::toGeometry<mbgl::LinearRing<double>>(env, jHoleListsArray.Get(env, i)));
}
mbgl::FillAnnotation annotation{geometry};
annotation.opacity = polygon.Get(env, alpha);
annotation.color = *conversion::convert<mbgl::Color>(env, polygon.Get(env, fillColor));
annotation.outlineColor = *conversion::convert<mbgl::Color>(env, polygon.Get(env, strokeColor));
return annotation;
}
void Polygon::registerNative(jni::JNIEnv& env) {
jni::Class<Polygon>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,20 @@
#pragma once
#include <mbgl/annotation/annotation.hpp>
#include "multi_point.hpp"
namespace mbgl {
namespace android {
class Polygon : private MultiPoint {
public:
static constexpr auto Name() { return "org/maplibre/android/annotations/Polygon"; };
static mbgl::FillAnnotation toAnnotation(jni::JNIEnv&, const jni::Object<Polygon>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,29 @@
#include "polyline.hpp"
#include "../conversion/color.hpp"
namespace mbgl {
namespace android {
mbgl::LineAnnotation Polyline::toAnnotation(jni::JNIEnv& env, const jni::Object<Polyline>& polyline) {
static auto& javaClass = jni::Class<Polyline>::Singleton(env);
static auto points = javaClass.GetField<jni::Object<java::util::List>>(env, "points");
static auto alpha = javaClass.GetField<float>(env, "alpha");
static auto color = javaClass.GetField<int>(env, "color");
static auto width = javaClass.GetField<float>(env, "width");
mbgl::LineAnnotation annotation{MultiPoint::toGeometry<mbgl::LineString<double>>(env, polyline.Get(env, points))};
annotation.opacity = polyline.Get(env, alpha);
annotation.color = *conversion::convert<mbgl::Color>(env, polyline.Get(env, color));
annotation.width = polyline.Get(env, width);
return annotation;
}
void Polyline::registerNative(jni::JNIEnv& env) {
jni::Class<Polyline>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,20 @@
#pragma once
#include <mbgl/annotation/annotation.hpp>
#include "multi_point.hpp"
namespace mbgl {
namespace android {
class Polyline : private MultiPoint {
public:
static constexpr auto Name() { return "org/maplibre/android/annotations/Polyline"; };
static mbgl::LineAnnotation toAnnotation(jni::JNIEnv&, const jni::Object<Polyline>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,12 @@
#pragma once
namespace mbgl {
namespace android {
class AssetManager {
public:
static constexpr auto Name() { return "android/content/res/AssetManager"; };
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,101 @@
#include "asset_manager_file_source.hpp"
#include <mbgl/platform/settings.hpp>
#include <mbgl/storage/file_source_request.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/resource_options.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/thread.hpp>
#include <mbgl/util/url.hpp>
#include <mbgl/util/util.hpp>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
namespace mbgl {
class AssetManagerFileSource::Impl {
public:
Impl(ActorRef<Impl>,
AAssetManager* assetManager_,
const ResourceOptions resourceOptions_,
const ClientOptions clientOptions_)
: resourceOptions(resourceOptions_.clone()),
clientOptions(clientOptions_.clone()),
assetManager(assetManager_) {}
void request(const std::string& url, ActorRef<FileSourceRequest> req) {
// Note: AssetManager already prepends "assets" to the filename.
const std::string path = mbgl::util::percentDecode(url.substr(8));
Response response;
if (AAsset* asset = AAssetManager_open(assetManager, path.c_str(), AASSET_MODE_BUFFER)) {
response.data = std::make_shared<std::string>(reinterpret_cast<const char*>(AAsset_getBuffer(asset)),
AAsset_getLength64(asset));
AAsset_close(asset);
} else {
response.error = std::make_unique<Response::Error>(Response::Error::Reason::NotFound,
"Could not read asset");
}
req.invoke(&FileSourceRequest::setResponse, response);
}
void setResourceOptions(ResourceOptions options) { resourceOptions = options; }
ResourceOptions getResourceOptions() { return resourceOptions.clone(); }
void setClientOptions(ClientOptions options) { clientOptions = options; }
ClientOptions getClientOptions() { return clientOptions.clone(); }
private:
AAssetManager* assetManager;
ResourceOptions resourceOptions;
ClientOptions clientOptions;
};
AssetManagerFileSource::AssetManagerFileSource(jni::JNIEnv& env,
const jni::Object<android::AssetManager>& assetManager_,
const ResourceOptions resourceOptions,
const ClientOptions clientOptions)
: assetManager(jni::NewGlobal(env, assetManager_)),
impl(std::make_unique<util::Thread<Impl>>(
util::makeThreadPrioritySetter(platform::EXPERIMENTAL_THREAD_PRIORITY_FILE),
"AssetManagerFileSource",
AAssetManager_fromJava(&env, jni::Unwrap(assetManager.get())),
resourceOptions.clone(),
clientOptions.clone())) {}
AssetManagerFileSource::~AssetManagerFileSource() = default;
std::unique_ptr<AsyncRequest> AssetManagerFileSource::request(const Resource& resource, Callback callback) {
auto req = std::make_unique<FileSourceRequest>(std::move(callback));
impl->actor().invoke(&Impl::request, resource.url, req->actor());
return std::move(req);
}
bool AssetManagerFileSource::canRequest(const Resource& resource) const {
return 0 == resource.url.rfind(mbgl::util::ASSET_PROTOCOL, 0);
}
void AssetManagerFileSource::setResourceOptions(ResourceOptions options) {
impl->actor().invoke(&Impl::setResourceOptions, options.clone());
}
ResourceOptions AssetManagerFileSource::getResourceOptions() {
return impl->actor().ask(&Impl::getResourceOptions).get();
}
void AssetManagerFileSource::setClientOptions(ClientOptions options) {
impl->actor().invoke(&Impl::setClientOptions, options.clone());
}
ClientOptions AssetManagerFileSource::getClientOptions() {
return impl->actor().ask(&Impl::getClientOptions).get();
}
} // namespace mbgl

View File

@@ -0,0 +1,42 @@
#pragma once
#include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource_options.hpp>
#include <mbgl/util/client_options.hpp>
#include "asset_manager.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace util {
template <typename T>
class Thread;
} // namespace util
class AssetManagerFileSource : public FileSource {
public:
AssetManagerFileSource(jni::JNIEnv&,
const jni::Object<android::AssetManager>&,
const ResourceOptions,
const ClientOptions);
~AssetManagerFileSource() override;
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
bool canRequest(const Resource&) const override;
void setResourceOptions(ResourceOptions options) override;
ResourceOptions getResourceOptions() override;
void setClientOptions(ClientOptions options) override;
ClientOptions getClientOptions() override;
private:
class Impl;
jni::Global<jni::Object<android::AssetManager>> assetManager;
std::unique_ptr<util::Thread<Impl>> impl;
};
} // namespace mbgl

View File

@@ -0,0 +1,40 @@
#include "connectivity_listener.hpp"
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/logging.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
/**
* Invoked when the construction is initiated from the jvm
*/
ConnectivityListener::ConnectivityListener(jni::JNIEnv&) {}
ConnectivityListener::~ConnectivityListener() {}
void ConnectivityListener::onConnectivityStateChanged(jni::JNIEnv&, jni::jboolean connected) {
NetworkStatus::Set(connected ? NetworkStatus::Status::Online : NetworkStatus::Status::Offline);
}
void ConnectivityListener::registerNative(jni::JNIEnv& env) {
// Lookup the class
static auto& javaClass = jni::Class<ConnectivityListener>::Singleton(env);
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
// Register the peer
jni::RegisterNativePeer<ConnectivityListener>(
env,
javaClass,
"nativePtr",
std::make_unique<ConnectivityListener, JNIEnv&>,
"initialize",
"finalize",
METHOD(&ConnectivityListener::onConnectivityStateChanged, "nativeOnConnectivityStateChanged"));
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,27 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class ConnectivityListener : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "org/maplibre/android/net/NativeConnectivityListener"; };
static void registerNative(jni::JNIEnv&);
/*
* Called when a Java object was created from the jvm side
*/
ConnectivityListener(jni::JNIEnv&);
~ConnectivityListener();
void onConnectivityStateChanged(jni::JNIEnv&, jni::jboolean);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,32 @@
#include "collection.hpp"
#include "constant.hpp"
namespace mbgl {
namespace android {
namespace conversion {
std::vector<std::string> toVector(JNIEnv& env, const jni::Array<jni::String>& array) {
std::size_t len = array.Length(env);
std::vector<std::string> vector;
vector.reserve(len);
for (std::size_t i = 0; i < len; i++) {
vector.push_back(jni::Make<std::string>(env, array.Get(env, i)));
}
return vector;
}
jni::Local<jni::Array<jni::String>> toArray(JNIEnv& env, const std::vector<std::string>& vector) {
auto result = jni::Array<jni::String>::New(env, vector.size());
for (std::size_t i = 0; i < vector.size(); i++) {
result.Set(env, i, jni::Make<jni::String>(env, vector.at(i)));
}
return result;
}
} // namespace conversion
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,18 @@
#pragma once
#include "conversion.hpp"
#include <jni/jni.hpp>
#include <vector>
namespace mbgl {
namespace android {
namespace conversion {
std::vector<std::string> toVector(JNIEnv& env, const jni::Array<jni::String>& array);
jni::Local<jni::Array<jni::String>> toArray(JNIEnv& env, const std::vector<std::string>&);
} // namespace conversion
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,17 @@
#include "color.hpp"
namespace mbgl {
namespace android {
namespace conversion {
Result<mbgl::Color> Converter<mbgl::Color, int>::operator()(jni::JNIEnv&, const int& color) const {
float r = (color >> 16) & 0xFF;
float g = (color >> 8) & 0xFF;
float b = (color) & 0xFF;
float a = (color >> 24) & 0xFF;
return {mbgl::Color(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f)};
}
} // namespace conversion
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,18 @@
#pragma once
#include "conversion.hpp"
#include <mbgl/util/color.hpp>
namespace mbgl {
namespace android {
namespace conversion {
template <>
struct Converter<mbgl::Color, int> {
Result<mbgl::Color> operator()(jni::JNIEnv&, const int& color) const;
};
} // namespace conversion
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,86 @@
#include "constant.hpp"
#include "collection.hpp"
#include "../style/formatted.hpp"
#include <mbgl/util/string.hpp>
namespace mbgl {
namespace android {
namespace conversion {
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, bool>::operator()(jni::JNIEnv& env,
const bool& value) const {
return jni::Box(env, value ? jni::jni_true : jni::jni_false);
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, float>::operator()(jni::JNIEnv& env,
const float& value) const {
return jni::Box(env, value);
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, double>::operator()(jni::JNIEnv& env,
const double& value) const {
return jni::Box(env, value);
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, std::string>::operator()(
jni::JNIEnv& env, const std::string& value) const {
return jni::Make<jni::String>(env, value);
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, Color>::operator()(jni::JNIEnv& env,
const Color& value) const {
return jni::Make<jni::String>(env, value.stringify());
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, style::expression::Formatted>::operator()(
jni::JNIEnv& env, const style::expression::Formatted& value) const {
return Formatted::New(env, value);
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, std::vector<std::string>>::operator()(
jni::JNIEnv& env, const std::vector<std::string>& value) const {
auto result = jni::Array<jni::String>::New(env, value.size());
for (std::size_t i = 0; i < value.size(); i++) {
result.Set(env, i, jni::Make<jni::String>(env, value.at(i)));
}
return result;
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, std::vector<float>>::operator()(
jni::JNIEnv& env, const std::vector<float>& value) const {
auto result = jni::Array<jni::Float>::New(env, value.size());
for (std::size_t i = 0; i < value.size(); i++) {
result.Set(env, i, jni::Box(env, value.at(i)));
}
return result;
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, std::vector<double>>::operator()(
jni::JNIEnv& env, const std::vector<double>& value) const {
auto result = jni::Array<jni::Double>::New(env, value.size());
for (std::size_t i = 0; i < value.size(); i++) {
result.Set(env, i, jni::Box(env, value.at(i)));
}
return result;
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, style::expression::Image>::operator()(
jni::JNIEnv& env, const style::expression::Image& value) const {
return jni::Make<jni::String>(env, value.id());
}
Result<jni::Local<jni::Object<>>> Converter<jni::Local<jni::Object<>>, mbgl::style::Rotation>::operator()(
jni::JNIEnv& env, const mbgl::style::Rotation& value) const {
return jni::Box(env, value.getAngle());
}
} // namespace conversion
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,132 @@
#pragma once
#include "conversion.hpp"
#include <mbgl/util/color.hpp>
#include <mbgl/util/enum.hpp>
#include <mbgl/style/expression/formatted.hpp>
#include <mbgl/style/expression/image.hpp>
#include <jni/jni.hpp>
#include <string>
#include <array>
#include <vector>
#include <mbgl/style/rotation.hpp>
namespace mbgl {
namespace android {
namespace conversion {
template <>
struct Converter<jni::Local<jni::Object<>>, bool> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const bool& value) const;
};
template <>
struct Converter<jni::Local<jni::Object<>>, float> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const float& value) const;
};
template <>
struct Converter<jni::Local<jni::Object<>>, double> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const double& value) const;
};
/**
* All integrals. java is limited to 64 bit signed, so...
* TODO: use BigDecimal for > 64 / unsigned?
*/
template <typename T>
struct Converter<jni::Local<jni::Object<>>, T, typename std::enable_if<std::is_integral<T>::value>::type> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const T& value) const {
return jni::Box(env, jni::jlong(value));
}
};
// TODO: convert integral types to primitive jni types
template <>
struct Converter<jni::Local<jni::Object<>>, std::string> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const std::string& value) const;
};
template <>
struct Converter<jni::Local<jni::Object<>>, Color> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const Color& value) const;
};
template <>
struct Converter<jni::Local<jni::Object<>>, style::expression::Formatted> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const style::expression::Formatted& value) const;
};
template <std::size_t N>
struct Converter<jni::Local<jni::Object<>>, std::array<float, N>> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const std::array<float, N>& value) const {
std::vector<float> v;
for (const float& id : value) {
v.push_back(id);
}
return convert<jni::Local<jni::Object<>>, std::vector<float>>(env, v);
}
};
template <std::size_t N>
struct Converter<jni::Local<jni::Object<>>, std::array<double, N>> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const std::array<double, N>& value) const {
std::vector<double> v;
for (const double& id : value) {
v.push_back(id);
}
return convert<jni::Local<jni::Object<>>, std::vector<double>>(env, v);
}
};
template <>
struct Converter<jni::Local<jni::Object<>>, std::vector<std::string>> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const std::vector<std::string>& value) const;
};
template <>
struct Converter<jni::Local<jni::Object<>>, std::vector<float>> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const std::vector<float>& value) const;
};
template <>
struct Converter<jni::Local<jni::Object<>>, std::vector<double>> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const std::vector<double>& value) const;
};
template <class T>
struct Converter<jni::Local<jni::Object<>>, T, typename std::enable_if_t<std::is_enum<T>::value>> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const T& value) const {
return convert<jni::Local<jni::Object<>>, std::string>(env, Enum<T>::toString(value));
}
};
template <class T>
struct Converter<jni::Local<jni::Object<>>, std::vector<T>, typename std::enable_if_t<std::is_enum<T>::value>> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const std::vector<T>& value) const {
auto result = jni::Array<jni::String>::New(env, value.size());
for (std::size_t i = 0; i < value.size(); ++i) {
result.Set(env, i, jni::Make<jni::String>(env, Enum<T>::toString(value.at(i))));
}
return result;
}
};
template <>
struct Converter<jni::Local<jni::Object<>>, style::expression::Image> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const style::expression::Image& value) const;
};
template <>
struct Converter<jni::Local<jni::Object<>>, mbgl::style::Rotation> {
Result<jni::Local<jni::Object<>>> operator()(jni::JNIEnv& env, const mbgl::style::Rotation& value) const;
};
} // namespace conversion
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,50 @@
#pragma once
#include <mbgl/util/variant.hpp>
#include <jni/jni.hpp>
#include <string>
namespace mbgl {
namespace android {
namespace conversion {
struct Error {
std::string message;
};
template <class T>
class Result : private variant<T, Error> {
public:
using variant<T, Error>::variant;
explicit operator bool() const { return this->template is<T>(); }
T& operator*() {
assert(this->template is<T>());
return this->template get<T>();
}
const T& operator*() const {
assert(this->template is<T>());
return this->template get<T>();
}
const Error& error() const {
assert(this->template is<Error>());
return this->template get<Error>();
}
};
template <class T, class V, class Enable = void>
struct Converter;
template <class T, typename V, class... Args>
Result<T> convert(jni::JNIEnv& env, const V& value, Args&&... args) {
return Converter<T, V>()(env, value, std::forward<Args>(args)...);
}
} // namespace conversion
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,284 @@
#include "file_source.hpp"
#include "attach_env.hpp"
#include "maplibre.hpp"
#include <mbgl/actor/actor.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/storage/file_source_manager.hpp>
#include <mbgl/storage/resource_options.hpp>
#include <mbgl/storage/resource_transform.hpp>
#include <mbgl/util/client_options.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/storage/sqlite3.hpp>
#include "asset_manager_file_source.hpp"
namespace mbgl {
namespace android {
// FileSource //
FileSource::FileSource(jni::JNIEnv& _env,
const jni::String& apiKey,
const jni::String& _cachePath,
const jni::Object<TileServerOptions>& _options)
: activationCounter(std::optional<int>(1)) {
std::string path = jni::Make<std::string>(_env, _cachePath);
mapbox::sqlite::setTempPath(path);
mbgl::FileSourceManager::get()->registerFileSourceFactory(
mbgl::FileSourceType::Asset,
[](const mbgl::ResourceOptions& resourceOptions, const mbgl::ClientOptions& clientOptions) {
auto env{android::AttachEnv()};
std::unique_ptr<mbgl::FileSource> assetFileSource;
if (android::MapLibre::hasInstance(*env)) {
auto assetManager = android::MapLibre::getAssetManager(*env);
assetFileSource = std::make_unique<AssetManagerFileSource>(
*env, assetManager, resourceOptions.clone(), clientOptions.clone());
}
return assetFileSource;
});
auto tileServerOptions = TileServerOptions::getTileServerOptions(_env, _options);
resourceOptions.withTileServerOptions(tileServerOptions)
.withApiKey(apiKey ? jni::Make<std::string>(_env, apiKey) : "")
.withCachePath(path + DATABASE_FILE);
// Create a core file sources
// TODO: Split Android FileSource API to smaller interfaces
resourceLoader = mbgl::FileSourceManager::get()->getFileSource(
mbgl::FileSourceType::ResourceLoader, resourceOptions, clientOptions);
databaseSource = std::static_pointer_cast<mbgl::DatabaseFileSource>(std::shared_ptr<mbgl::FileSource>(
mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Database, resourceOptions, clientOptions)));
onlineSource = mbgl::FileSourceManager::get()->getFileSource(
mbgl::FileSourceType::Network, resourceOptions, clientOptions);
}
FileSource::~FileSource() {}
void FileSource::setTileServerOptions(jni::JNIEnv& _env, const jni::Object<TileServerOptions>& _options) {
auto tileServerOptions = TileServerOptions::getTileServerOptions(_env, _options);
resourceOptions.withTileServerOptions(tileServerOptions);
resourceLoader->setResourceOptions(resourceOptions.clone());
databaseSource->setResourceOptions(resourceOptions.clone());
onlineSource->setResourceOptions(resourceOptions.clone());
}
jni::Local<jni::String> FileSource::getApiKey(jni::JNIEnv& env) {
if (auto* token = onlineSource->getProperty(mbgl::API_KEY_KEY).getString()) {
return jni::Make<jni::String>(env, *token);
}
ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Online functionality is disabled.");
return jni::Make<jni::String>(env, "");
}
void FileSource::setApiKey(jni::JNIEnv& env, const jni::String& token) {
if (onlineSource) {
onlineSource->setProperty(mbgl::API_KEY_KEY, token ? jni::Make<std::string>(env, token) : "");
} else {
ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Online functionality is disabled.");
}
}
void FileSource::setAPIBaseUrl(jni::JNIEnv& env, const jni::String& url) {
if (onlineSource) {
onlineSource->setProperty(mbgl::API_BASE_URL_KEY, jni::Make<std::string>(env, url));
} else {
ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Online functionality is disabled.");
}
}
jni::Local<jni::String> FileSource::getAPIBaseUrl(jni::JNIEnv& env) {
if (auto* url = onlineSource->getProperty(mbgl::API_BASE_URL_KEY).getString()) {
return jni::Make<jni::String>(env, std::string(*url));
}
ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Online functionality is disabled.");
return jni::Make<jni::String>(env, "");
}
void FileSource::setResourceTransform(jni::JNIEnv& env,
const jni::Object<FileSource::ResourceTransformCallback>& transformCallback) {
// Core could be built without support for network resource provider.
if (!onlineSource) {
ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Online functionality is disabled.");
return;
}
if (transformCallback) {
auto global = jni::NewGlobal<jni::EnvAttachingDeleter>(env, transformCallback);
resourceTransform = std::make_unique<Actor<ResourceTransform::TransformCallback>>(
*Scheduler::GetCurrent(),
// Capture the ResourceTransformCallback object as a managed
// global into the lambda. It is released automatically when
// we're setting a new ResourceTransform in a subsequent call.
// Note: we're converting it to shared_ptr because this lambda
// is converted to a std::function, which requires copyability
// of its captured variables.
[callback = std::make_shared<decltype(global)>(std::move(global))](
mbgl::Resource::Kind kind, const std::string& url_, ResourceTransform::FinishedCallback cb) {
android::UniqueEnv _env = android::AttachEnv();
cb(FileSource::ResourceTransformCallback::onURL(*_env, *callback, int(kind), url_));
});
onlineSource->setResourceTransform(
{[actorRef = resourceTransform->self()](
Resource::Kind kind, const std::string& url, ResourceTransform::FinishedCallback cb) {
actorRef.invoke(&ResourceTransform::TransformCallback::operator(), kind, url, std::move(cb));
}});
} else {
// Reset the callback
resourceTransform.reset();
onlineSource->setResourceTransform({});
}
}
void FileSource::setResourceCachePath(jni::JNIEnv& env,
const jni::String& path,
const jni::Object<FileSource::ResourcesCachePathChangeCallback>& _callback) {
if (!databaseSource) {
ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Offline functionality is disabled.");
return;
}
if (pathChangeCallback) {
FileSource::ResourcesCachePathChangeCallback::onError(
env, _callback, jni::Make<jni::String>(env, "Another resources cache path change is in progress"));
return;
}
std::string newPath = jni::Make<std::string>(env, path);
mapbox::sqlite::setTempPath(newPath);
auto global = jni::NewGlobal<jni::EnvAttachingDeleter>(env, _callback);
pathChangeCallback = Scheduler::GetCurrent()->bindOnce(
[this, callback = std::make_shared<decltype(global)>(std::move(global)), newPath] {
android::UniqueEnv _env = android::AttachEnv();
FileSource::ResourcesCachePathChangeCallback::onSuccess(
*_env, *callback, jni::Make<jni::String>(*_env, newPath));
pathChangeCallback = {};
});
databaseSource->setDatabasePath(newPath + DATABASE_FILE, pathChangeCallback);
}
void FileSource::resume(jni::JNIEnv&) {
if (!resourceLoader) {
return;
}
activationCounter.value()++;
if (activationCounter == 1) {
resourceLoader->resume();
}
}
void FileSource::pause(jni::JNIEnv&) {
if (!resourceLoader) {
return;
}
activationCounter.value()--;
if (activationCounter == 0) {
resourceLoader->pause();
}
}
jni::jboolean FileSource::isResumed(jni::JNIEnv&) {
return (jboolean)(activationCounter > 0);
}
FileSource* FileSource::getNativePeer(jni::JNIEnv& env, const jni::Object<FileSource>& jFileSource) {
static auto& javaClass = jni::Class<FileSource>::Singleton(env);
static auto field = javaClass.GetField<jlong>(env, "nativePtr");
return reinterpret_cast<FileSource*>(jFileSource.Get(env, field));
}
mbgl::ResourceOptions FileSource::getSharedResourceOptions(jni::JNIEnv& env,
const jni::Object<FileSource>& jFileSource) {
FileSource* fileSource = FileSource::getNativePeer(env, jFileSource);
// Core could be compiled without support for any sources.
if (fileSource) {
return fileSource->resourceOptions.clone();
}
return {};
}
mbgl::ClientOptions FileSource::getSharedClientOptions(jni::JNIEnv& env, const jni::Object<FileSource>& jFileSource) {
FileSource* fileSource = FileSource::getNativePeer(env, jFileSource);
// Core could be compiled without support for any sources.
if (fileSource) {
return fileSource->clientOptions.clone();
}
return {};
}
// FileSource::ResourcesCachePathChangeCallback //
void FileSource::ResourcesCachePathChangeCallback::onSuccess(
jni::JNIEnv& env,
const jni::Object<FileSource::ResourcesCachePathChangeCallback>& callback,
const jni::String& path) {
static auto& javaClass = jni::Class<FileSource::ResourcesCachePathChangeCallback>::Singleton(env);
static auto method = javaClass.GetMethod<void(jni::String)>(env, "onSuccess");
callback.Call(env, method, path);
}
void FileSource::ResourcesCachePathChangeCallback::onError(
jni::JNIEnv& env,
const jni::Object<FileSource::ResourcesCachePathChangeCallback>& callback,
const jni::String& message) {
static auto& javaClass = jni::Class<FileSource::ResourcesCachePathChangeCallback>::Singleton(env);
static auto method = javaClass.GetMethod<void(jni::String)>(env, "onError");
callback.Call(env, method, message);
}
void FileSource::registerNative(jni::JNIEnv& env) {
// Ensure the classes are cached. If they're requested for the
// first time on a background thread, Android's class loader heuristics will fail.
// https://developer.android.com/training/articles/perf-jni#faq_FindClass
jni::Class<ResourceTransformCallback>::Singleton(env);
jni::Class<ResourcesCachePathChangeCallback>::Singleton(env);
static auto& javaClass = jni::Class<FileSource>::Singleton(env);
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
// Register the peer
jni::RegisterNativePeer<FileSource>(
env,
javaClass,
"nativePtr",
jni::MakePeer<FileSource, const jni::String&, const jni::String&, const jni::Object<TileServerOptions>&>,
"initialize",
"finalize",
METHOD(&FileSource::setTileServerOptions, "setTileServerOptions"),
METHOD(&FileSource::getApiKey, "getApiKey"),
METHOD(&FileSource::setApiKey, "setApiKey"),
METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"),
METHOD(&FileSource::getAPIBaseUrl, "getApiBaseUrl"),
METHOD(&FileSource::setResourceTransform, "setResourceTransform"),
METHOD(&FileSource::setResourceCachePath, "setResourceCachePath"),
METHOD(&FileSource::resume, "activate"),
METHOD(&FileSource::pause, "deactivate"),
METHOD(&FileSource::isResumed, "isActivated"));
}
// FileSource::ResourceTransformCallback //
std::string FileSource::ResourceTransformCallback::onURL(
jni::JNIEnv& env, const jni::Object<FileSource::ResourceTransformCallback>& callback, int kind, std::string url_) {
static auto& javaClass = jni::Class<FileSource::ResourceTransformCallback>::Singleton(env);
static auto method = javaClass.GetMethod<jni::String(jni::jint, jni::String)>(env, "onURL");
return jni::Make<std::string>(env, callback.Call(env, method, kind, jni::Make<jni::String>(env, url_)));
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,103 @@
#pragma once
#include <mbgl/storage/database_file_source.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/storage/resource_options.hpp>
#include <mbgl/storage/resource_transform.hpp>
#include "asset_manager.hpp"
#include <jni/jni.hpp>
#include "util/tile_server_options.hpp"
namespace mbgl {
template <typename T>
class Actor;
namespace android {
/**
* Peer class for the Android FileSource holder. Ensures that a single core
* FileSource of a ResourceLoader type is used.
*/
class FileSource {
public:
static constexpr auto Name() { return "org/maplibre/android/storage/FileSource"; };
struct ResourceTransformCallback {
static constexpr auto Name() {
return "org/maplibre/android/storage/"
"FileSource$ResourceTransformCallback";
}
static std::string onURL(jni::JNIEnv&,
const jni::Object<FileSource::ResourceTransformCallback>&,
int,
std::string);
};
struct ResourcesCachePathChangeCallback {
static constexpr auto Name() {
return "org/maplibre/android/storage/"
"FileSource$ResourcesCachePathChangeCallback";
}
static void onSuccess(jni::JNIEnv&,
const jni::Object<FileSource::ResourcesCachePathChangeCallback>&,
const jni::String&);
static void onError(jni::JNIEnv&,
const jni::Object<FileSource::ResourcesCachePathChangeCallback>&,
const jni::String&);
};
FileSource(jni::JNIEnv&, const jni::String&, const jni::String&, const jni::Object<TileServerOptions>&);
~FileSource();
void setTileServerOptions(jni::JNIEnv& _env, const jni::Object<TileServerOptions>& _options);
jni::Local<jni::String> getApiKey(jni::JNIEnv&);
void setApiKey(jni::JNIEnv&, const jni::String&);
jni::Local<jni::String> getAPIBaseUrl(jni::JNIEnv&);
void setAPIBaseUrl(jni::JNIEnv&, const jni::String&);
void setResourceTransform(jni::JNIEnv&, const jni::Object<FileSource::ResourceTransformCallback>&);
void setResourceCachePath(jni::JNIEnv&,
const jni::String&,
const jni::Object<FileSource::ResourcesCachePathChangeCallback>&);
void resume(jni::JNIEnv&);
void pause(jni::JNIEnv&);
jni::jboolean isResumed(jni::JNIEnv&);
static FileSource* getNativePeer(jni::JNIEnv&, const jni::Object<FileSource>&);
static mbgl::ResourceOptions getSharedResourceOptions(jni::JNIEnv&, const jni::Object<FileSource>&);
static mbgl::ClientOptions getSharedClientOptions(jni::JNIEnv&, const jni::Object<FileSource>&);
static void registerNative(jni::JNIEnv&);
private:
const std::string DATABASE_FILE = "/mbgl-offline.db";
std::optional<int> activationCounter;
mbgl::ResourceOptions resourceOptions;
mbgl::ClientOptions clientOptions;
std::unique_ptr<Actor<ResourceTransform::TransformCallback>> resourceTransform;
std::function<void()> pathChangeCallback;
std::shared_ptr<mbgl::DatabaseFileSource> databaseSource;
std::shared_ptr<mbgl::FileSource> onlineSource;
std::shared_ptr<mbgl::FileSource> resourceLoader;
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,88 @@
#include "feature.hpp"
#include "geometry.hpp"
#include "../gson/json_object.hpp"
#include <mbgl/util/string.hpp>
namespace mbgl {
namespace android {
namespace geojson {
using namespace gson;
mbgl::GeoJSONFeature Feature::convert(jni::JNIEnv& env, const jni::Object<Feature>& jFeature) {
static auto& javaClass = jni::Class<Feature>::Singleton(env);
static auto id = javaClass.GetMethod<jni::String()>(env, "id");
static auto geometry = javaClass.GetMethod<jni::Object<Geometry>()>(env, "geometry");
static auto properties = javaClass.GetMethod<jni::Object<gson::JsonObject>()>(env, "properties");
auto jId = jFeature.Call(env, id);
using mbid = mapbox::feature::identifier;
return mbgl::GeoJSONFeature{Geometry::convert(env, jFeature.Call(env, geometry)),
JsonObject::convert(env, jFeature.Call(env, properties)),
jId ? mbid{jni::Make<std::string>(env, jId)} : mbid{mapbox::feature::null_value}};
}
/**
* Turn feature identifier into std::string
*/
class FeatureIdVisitor {
public:
template <class T>
std::string operator()(const T& i) const {
return util::toString(i);
}
std::string operator()(const std::string& i) const { return i; }
std::string operator()(const std::nullptr_t&) const { return {}; }
std::string operator()(const mapbox::feature::null_value_t&) const { return {}; }
};
jni::Local<jni::Object<Feature>> convertFeature(jni::JNIEnv& env, const mbgl::GeoJSONFeature& value) {
static auto& javaClass = jni::Class<Feature>::Singleton(env);
static auto method =
javaClass.GetStaticMethod<jni::Object<Feature>(jni::Object<Geometry>, jni::Object<JsonObject>, jni::String)>(
env, "fromGeometry");
return javaClass.Call(
env,
method,
Geometry::New(env, value.geometry),
JsonObject::New(env, value.properties),
jni::Make<jni::String>(env,
value.id.is<mbgl::NullValue>() ? std::string{} : value.id.match(FeatureIdVisitor())));
}
jni::Local<jni::Array<jni::Object<Feature>>> Feature::convert(jni::JNIEnv& env,
const std::vector<mbgl::Feature>& value) {
auto features = jni::Array<jni::Object<Feature>>::New(env, value.size());
for (size_t i = 0; i < value.size(); ++i) {
features.Set(env, i, convertFeature(env, static_cast<mbgl::GeoJSONFeature>(value.at(i))));
}
return features;
}
jni::Local<jni::Array<jni::Object<Feature>>> Feature::convert(jni::JNIEnv& env,
const std::vector<mbgl::GeoJSONFeature>& value) {
auto features = jni::Array<jni::Object<Feature>>::New(env, value.size());
for (size_t i = 0; i < value.size(); ++i) {
features.Set(env, i, convertFeature(env, value.at(i)));
}
return features;
}
void Feature::registerNative(jni::JNIEnv& env) {
jni::Class<Feature>::Singleton(env);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,24 @@
#pragma once
#include <mbgl/util/feature.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
class Feature {
public:
static constexpr auto Name() { return "com/mapbox/geojson/Feature"; };
static mbgl::GeoJSONFeature convert(jni::JNIEnv&, const jni::Object<Feature>&);
static jni::Local<jni::Array<jni::Object<Feature>>> convert(jni::JNIEnv&, const std::vector<mbgl::Feature>&);
static jni::Local<jni::Array<jni::Object<Feature>>> convert(jni::JNIEnv&, const std::vector<mbgl::GeoJSONFeature>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,39 @@
#include "feature_collection.hpp"
#include "feature.hpp"
namespace mbgl {
namespace android {
namespace geojson {
mbgl::FeatureCollection FeatureCollection::convert(jni::JNIEnv& env,
const jni::Object<FeatureCollection>& jCollection) {
auto collection = mbgl::FeatureCollection();
if (jCollection) {
auto jFeatureList = FeatureCollection::features(env, jCollection);
auto jFeatures = java::util::List::toArray<Feature>(env, jFeatureList);
auto size = size_t(jFeatures.Length(env));
collection.reserve(size);
for (size_t i = 0; i < size; i++) {
collection.push_back(Feature::convert(env, jFeatures.Get(env, i)));
}
}
return collection;
}
jni::Local<jni::Object<java::util::List>> FeatureCollection::features(
jni::JNIEnv& env, const jni::Object<FeatureCollection>& jCollection) {
static auto& javaClass = jni::Class<FeatureCollection>::Singleton(env);
static auto method = javaClass.GetMethod<jni::Object<java::util::List>()>(env, "features");
return jCollection.Call(env, method);
}
void FeatureCollection::registerNative(jni::JNIEnv& env) {
jni::Class<FeatureCollection>::Singleton(env);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,26 @@
#pragma once
#include "../java/util.hpp"
#include <mbgl/util/geojson.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
class FeatureCollection {
public:
static constexpr auto Name() { return "com/mapbox/geojson/FeatureCollection"; };
static mbgl::FeatureCollection convert(jni::JNIEnv&, const jni::Object<FeatureCollection>&);
static jni::Local<jni::Object<java::util::List>> features(jni::JNIEnv&, const jni::Object<FeatureCollection>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,97 @@
#include "geometry.hpp"
#include "point.hpp"
#include "multi_point.hpp"
#include "line_string.hpp"
#include "multi_line_string.hpp"
#include "polygon.hpp"
#include "multi_polygon.hpp"
#include "geometry_collection.hpp"
#include <string>
namespace mbgl {
namespace android {
namespace geojson {
/**
* Turn mapbox::geometry type into Java GeoJson Geometries
*/
class GeometryEvaluator {
public:
jni::JNIEnv &env;
jni::Local<jni::Object<Geometry>> operator()(const mbgl::EmptyGeometry &) const {
// FIXME: mapbox-java needs to have its own Empty type.
return GeometryCollection::New(env, {});
}
jni::Local<jni::Object<Geometry>> operator()(const mbgl::Point<double> &geometry) const {
return Point::New(env, geometry);
}
jni::Local<jni::Object<Geometry>> operator()(const mbgl::LineString<double> &geometry) const {
return LineString::New(env, geometry);
}
jni::Local<jni::Object<Geometry>> operator()(const mbgl::MultiLineString<double> &geometry) const {
return MultiLineString::New(env, geometry);
}
jni::Local<jni::Object<Geometry>> operator()(const mbgl::MultiPoint<double> &geometry) const {
return MultiPoint::New(env, geometry);
}
jni::Local<jni::Object<Geometry>> operator()(const mbgl::Polygon<double> &geometry) const {
return Polygon::New(env, geometry);
}
jni::Local<jni::Object<Geometry>> operator()(const mbgl::MultiPolygon<double> &geometry) const {
return MultiPolygon::New(env, geometry);
}
jni::Local<jni::Object<Geometry>> operator()(const mapbox::geometry::geometry_collection<double> &geometry) const {
return GeometryCollection::New(env, geometry);
}
};
jni::Local<jni::Object<Geometry>> Geometry::New(jni::JNIEnv &env, mbgl::Geometry<double> geometry) {
GeometryEvaluator evaluator{env};
return mbgl::Geometry<double>::visit(geometry, evaluator);
}
mbgl::Geometry<double> Geometry::convert(jni::JNIEnv &env, const jni::Object<Geometry> &jGeometry) {
auto type = Geometry::getType(env, jGeometry);
if (type == Point::Type()) {
return {Point::convert(env, jni::Cast(env, jni::Class<Point>::Singleton(env), jGeometry))};
} else if (type == MultiPoint::Type()) {
return {MultiPoint::convert(env, jni::Cast(env, jni::Class<MultiPoint>::Singleton(env), jGeometry))};
} else if (type == LineString::Type()) {
return {LineString::convert(env, jni::Cast(env, jni::Class<LineString>::Singleton(env), jGeometry))};
} else if (type == MultiLineString::Type()) {
return {MultiLineString::convert(env, jni::Cast(env, jni::Class<MultiLineString>::Singleton(env), jGeometry))};
} else if (type == Polygon::Type()) {
return {Polygon::convert(env, jni::Cast(env, jni::Class<Polygon>::Singleton(env), jGeometry))};
} else if (type == MultiPolygon::Type()) {
return {MultiPolygon::convert(env, jni::Cast(env, jni::Class<MultiPolygon>::Singleton(env), jGeometry))};
} else if (type == GeometryCollection::Type()) {
return {GeometryCollection::convert(env,
jni::Cast(env, jni::Class<GeometryCollection>::Singleton(env), jGeometry))};
}
throw std::runtime_error(std::string{"Unsupported GeoJSON type: "} + type);
}
std::string Geometry::getType(jni::JNIEnv &env, const jni::Object<Geometry> &jGeometry) {
static auto &javaClass = jni::Class<Geometry>::Singleton(env);
static auto method = javaClass.GetMethod<jni::String()>(env, "type");
return jni::Make<std::string>(env, jGeometry.Call(env, method));
}
void Geometry::registerNative(jni::JNIEnv &env) {
jni::Class<Geometry>::Singleton(env);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,28 @@
#pragma once
#include <mbgl/util/geometry.hpp>
#include "../java/util.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
class Geometry {
public:
static constexpr auto Name() { return "com/mapbox/geojson/Geometry"; };
static jni::Local<jni::Object<Geometry>> New(jni::JNIEnv&, mbgl::Geometry<double>);
static mbgl::Geometry<double> convert(jni::JNIEnv&, const jni::Object<Geometry>&);
static std::string getType(jni::JNIEnv&, const jni::Object<Geometry>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,50 @@
#include "geometry_collection.hpp"
#include "../java/util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
jni::Local<jni::Object<GeometryCollection>> GeometryCollection::New(
jni::JNIEnv& env, const mapbox::geometry::geometry_collection<double>& collection) {
// Create an array of geometries
auto jarray = jni::Array<jni::Object<Geometry>>::New(env, collection.size());
for (size_t i = 0; i < collection.size(); i++) {
jarray.Set(env, i, Geometry::New(env, collection.at(i)));
}
// create the GeometryCollection
static auto& javaClass = jni::Class<GeometryCollection>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<GeometryCollection>(jni::Object<java::util::List>)>(
env, "fromGeometries");
return javaClass.Call(env, method, java::util::Arrays::asList(env, jarray));
}
mapbox::geometry::geometry_collection<double> GeometryCollection::convert(
jni::JNIEnv& env, const jni::Object<GeometryCollection>& jCollection) {
// Get geometries
static auto& javaClass = jni::Class<GeometryCollection>::Singleton(env);
static auto getGeometries = javaClass.GetMethod<jni::Object<java::util::List>()>(env, "geometries");
// Turn into array
auto jarray = java::util::List::toArray<Geometry>(env, jCollection.Call(env, getGeometries));
// Convert each geometry
mapbox::geometry::geometry_collection<double> collection{};
auto size = jarray.Length(env);
for (jni::jsize i = 0; i < size; i++) {
collection.push_back(Geometry::convert(env, jarray.Get(env, i)));
}
return collection;
}
void GeometryCollection::registerNative(jni::JNIEnv& env) {
jni::Class<GeometryCollection>::Singleton(env);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,27 @@
#pragma once
#include "geometry.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
class GeometryCollection {
public:
using SuperTag = Geometry;
static constexpr auto Name() { return "com/mapbox/geojson/GeometryCollection"; };
static constexpr auto Type() { return "GeometryCollection"; };
static jni::Local<jni::Object<GeometryCollection>> New(jni::JNIEnv&,
const mapbox::geometry::geometry_collection<double>&);
static mapbox::geometry::geometry_collection<double> convert(jni::JNIEnv&, const jni::Object<GeometryCollection>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,58 @@
#include "line_string.hpp"
#include "point.hpp"
#include "util.hpp"
#include "../java/util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
jni::Local<jni::Object<LineString>> LineString::New(jni::JNIEnv& env, const mbgl::LineString<double>& lineString) {
static auto& javaClass = jni::Class<LineString>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<LineString>(jni::Object<java::util::List>)>(
env, "fromLngLats");
return javaClass.Call(env, method, asPointsList(env, lineString));
}
mapbox::geojson::line_string LineString::convert(jni::JNIEnv& env, const jni::Object<LineString>& jLineString) {
mapbox::geojson::line_string lineString;
if (jLineString) {
lineString = LineString::convert(env, LineString::coordinates(env, jLineString));
}
return lineString;
}
mapbox::geojson::line_string LineString::convert(jni::JNIEnv& env,
const jni::Object<java::util::List /*<Point>*/>& jPointList) {
mapbox::geojson::line_string lineString;
if (jPointList) {
auto jPointArray = java::util::List::toArray<Point>(env, jPointList);
auto size = jPointArray.Length(env);
lineString.reserve(size);
for (std::size_t i = 0; i < size; i++) {
lineString.push_back(Point::convert(env, jPointArray.Get(env, i)));
}
}
return lineString;
}
jni::Local<jni::Object<java::util::List>> LineString::coordinates(jni::JNIEnv& env,
const jni::Object<LineString>& jLineString) {
static auto& javaClass = jni::Class<LineString>::Singleton(env);
static auto method = javaClass.GetMethod<jni::Object<java::util::List>()>(env, "coordinates");
return jLineString.Call(env, method);
}
void LineString::registerNative(jni::JNIEnv& env) {
jni::Class<LineString>::Singleton(env);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,35 @@
#pragma once
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/util/noncopyable.hpp>
#include "geometry.hpp"
#include "../java/util.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
class LineString {
public:
using SuperTag = Geometry;
static constexpr auto Name() { return "com/mapbox/geojson/LineString"; };
static constexpr auto Type() { return "LineString"; };
static jni::Local<jni::Object<LineString>> New(jni::JNIEnv&, const mbgl::LineString<double>&);
static mapbox::geojson::line_string convert(jni::JNIEnv&, const jni::Object<LineString>&);
static mapbox::geojson::line_string convert(jni::JNIEnv&, const jni::Object<java::util::List /*<Point>*/>&);
static jni::Local<jni::Object<java::util::List>> coordinates(jni::JNIEnv&, const jni::Object<LineString>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,61 @@
#include "multi_line_string.hpp"
#include "line_string.hpp"
#include "util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
jni::Local<jni::Object<MultiLineString>> MultiLineString::New(jni::JNIEnv& env,
const mbgl::MultiLineString<double>& multiLineString) {
static auto& javaClass = jni::Class<MultiLineString>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<MultiLineString>(jni::Object<java::util::List>)>(
env, "fromLngLats");
return javaClass.Call(env, method, asPointsListsList(env, multiLineString));
}
mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv& env,
const jni::Object<MultiLineString>& jMultiLineString) {
mapbox::geojson::multi_line_string multiLineString;
if (jMultiLineString) {
multiLineString = MultiLineString::convert(env, MultiLineString::coordinates(env, jMultiLineString));
}
return multiLineString;
}
mapbox::geojson::multi_line_string MultiLineString::convert(
jni::JNIEnv& env, const jni::Object<java::util::List /*<java::util::List<Point>>*/>& jPointListsList) {
mapbox::geojson::multi_line_string multiLineString;
if (jPointListsList) {
auto jPositionListsArray = java::util::List::toArray<java::util::List>(env, jPointListsList);
auto size = jPositionListsArray.Length(env);
multiLineString.reserve(size);
for (std::size_t i = 0; i < size; i++) {
multiLineString.push_back(LineString::convert(env, jPositionListsArray.Get(env, i)));
}
}
return multiLineString;
}
jni::Local<jni::Object<java::util::List>> MultiLineString::coordinates(
jni::JNIEnv& env, const jni::Object<MultiLineString>& jLineString) {
static auto& javaClass = jni::Class<MultiLineString>::Singleton(env);
static auto method = javaClass.GetMethod<jni::Object<java::util::List>()>(env, "coordinates");
return jLineString.Call(env, method);
}
void MultiLineString::registerNative(jni::JNIEnv& env) {
jni::Class<MultiLineString>::Singleton(env);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,35 @@
#pragma once
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
#include "../java/util.hpp"
#include "geometry.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
class MultiLineString {
public:
using SuperTag = Geometry;
static constexpr auto Name() { return "com/mapbox/geojson/MultiLineString"; };
static constexpr auto Type() { return "MultiLineString"; };
static jni::Local<jni::Object<MultiLineString>> New(jni::JNIEnv&, const mbgl::MultiLineString<double>&);
static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, const jni::Object<MultiLineString>&);
static mapbox::geojson::multi_line_string convert(
jni::JNIEnv&, const jni::Object<java::util::List /*<java::util::List<Point>>*/>&);
static jni::Local<jni::Object<java::util::List>> coordinates(jni::JNIEnv&, const jni::Object<MultiLineString>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,43 @@
#include "multi_point.hpp"
#include "line_string.hpp"
#include "util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
jni::Local<jni::Object<MultiPoint>> MultiPoint::New(JNIEnv& env, const mbgl::MultiPoint<double>& multiPoint) {
static auto& javaClass = jni::Class<MultiPoint>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<MultiPoint>(jni::Object<java::util::List>)>(
env, "fromLngLats");
return javaClass.Call(env, method, asPointsList(env, multiPoint));
}
mapbox::geojson::multi_point MultiPoint::convert(jni::JNIEnv& env, const jni::Object<MultiPoint>& jMultiPoint) {
mapbox::geojson::multi_point multiPoint;
if (jMultiPoint) {
multiPoint = convertExplicit<mapbox::geojson::multi_point>(
LineString::convert(env, MultiPoint::coordinates(env, jMultiPoint)));
}
return multiPoint;
}
jni::Local<jni::Object<java::util::List>> MultiPoint::coordinates(jni::JNIEnv& env,
const jni::Object<MultiPoint>& jMultiPoint) {
static auto& javaClass = jni::Class<MultiPoint>::Singleton(env);
static auto method = javaClass.GetMethod<jni::Object<java::util::List>()>(env, "coordinates");
return jMultiPoint.Call(env, method);
}
void MultiPoint::registerNative(jni::JNIEnv& env) {
jni::Class<MultiPoint>::Singleton(env);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,33 @@
#pragma once
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/util/noncopyable.hpp>
#include "geometry.hpp"
#include "../java/util.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
class MultiPoint {
public:
using SuperTag = Geometry;
static constexpr auto Name() { return "com/mapbox/geojson/MultiPoint"; };
static constexpr auto Type() { return "MultiPoint"; };
static jni::Local<jni::Object<MultiPoint>> New(jni::JNIEnv&, const mbgl::MultiPoint<double>&);
static mapbox::geojson::multi_point convert(jni::JNIEnv&, const jni::Object<MultiPoint>&);
static jni::Local<jni::Object<java::util::List>> coordinates(jni::JNIEnv&, const jni::Object<MultiPoint>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,55 @@
#include "multi_polygon.hpp"
#include "polygon.hpp"
#include "util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
jni::Local<jni::Object<MultiPolygon>> MultiPolygon::New(JNIEnv& env, const mbgl::MultiPolygon<double>& multiPolygon) {
static auto& javaClass = jni::Class<MultiPolygon>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<MultiPolygon>(jni::Object<java::util::List>)>(
env, "fromLngLats");
auto jarray = jni::Array<jni::Object<java::util::List>>::New(env, multiPolygon.size());
for (size_t i = 0; i < multiPolygon.size(); i++) {
jarray.Set(env, i, asPointsListsList(env, multiPolygon.at(i)));
}
return javaClass.Call(env, method, java::util::Arrays::asList(env, jarray));
}
mapbox::geojson::multi_polygon MultiPolygon::convert(jni::JNIEnv& env, const jni::Object<MultiPolygon>& jMultiPolygon) {
mapbox::geojson::multi_polygon multiPolygon;
if (jMultiPolygon) {
auto jPointListsListList = MultiPolygon::coordinates(env, jMultiPolygon);
auto jPointListsListArray = java::util::List::toArray<java::util::List>(env, jPointListsListList);
auto size = jPointListsListArray.Length(env);
multiPolygon.reserve(size);
for (size_t i = 0; i < size; i++) {
multiPolygon.push_back(Polygon::convert(env, jPointListsListArray.Get(env, i)));
}
}
return multiPolygon;
}
jni::Local<jni::Object<java::util::List>> MultiPolygon::coordinates(jni::JNIEnv& env,
const jni::Object<MultiPolygon>& jPolygon) {
static auto& javaClass = jni::Class<MultiPolygon>::Singleton(env);
static auto method = javaClass.GetMethod<jni::Object<java::util::List>()>(env, "coordinates");
return jPolygon.Call(env, method);
}
void MultiPolygon::registerNative(jni::JNIEnv& env) {
jni::Class<MultiPolygon>::Singleton(env);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,32 @@
#pragma once
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
#include "../java/util.hpp"
#include "geometry.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
class MultiPolygon {
public:
using SuperTag = Geometry;
static constexpr auto Name() { return "com/mapbox/geojson/MultiPolygon"; };
static constexpr auto Type() { return "MultiPolygon"; };
static jni::Local<jni::Object<MultiPolygon>> New(jni::JNIEnv&, const mbgl::MultiPolygon<double>&);
static mapbox::geojson::multi_polygon convert(jni::JNIEnv&, const jni::Object<MultiPolygon>&);
static jni::Local<jni::Object<java::util::List>> coordinates(jni::JNIEnv&, const jni::Object<MultiPolygon>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,31 @@
#include "point.hpp"
namespace mbgl {
namespace android {
namespace geojson {
jni::Local<jni::Object<Point>> Point::New(jni::JNIEnv& env, const mbgl::Point<double>& point) {
static auto& javaClass = jni::Class<Point>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<Point>(jni::jdouble, jni::jdouble)>(env, "fromLngLat");
return javaClass.Call(env, method, point.x, point.y);
}
mbgl::Point<double> Point::convert(jni::JNIEnv& env, const jni::Object<Point>& jPoint) {
static auto& javaClass = jni::Class<Point>::Singleton(env);
static auto longitude = javaClass.GetMethod<jni::jdouble()>(env, "longitude");
static auto latitude = javaClass.GetMethod<jni::jdouble()>(env, "latitude");
if (!jPoint) {
return {};
}
return {jPoint.Call(env, longitude), jPoint.Call(env, latitude)};
}
void Point::registerNative(jni::JNIEnv& env) {
jni::Class<Point>::Singleton(env);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,27 @@
#pragma once
#include <mbgl/util/geometry.hpp>
#include "geometry.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
class Point {
public:
using SuperTag = Geometry;
static constexpr auto Name() { return "com/mapbox/geojson/Point"; };
static constexpr auto Type() { return "Point"; };
static jni::Local<jni::Object<Point>> New(jni::JNIEnv&, const mbgl::Point<double>&);
static mbgl::Point<double> convert(jni::JNIEnv&, const jni::Object<Point>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,56 @@
#include "polygon.hpp"
#include "multi_line_string.hpp"
#include "util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
jni::Local<jni::Object<Polygon>> Polygon::New(jni::JNIEnv& env, const mbgl::Polygon<double>& polygon) {
static auto& javaClass = jni::Class<Polygon>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<Polygon>(jni::Object<java::util::List>)>(env,
"fromLngLats");
return javaClass.Call(env, method, asPointsListsList(env, polygon));
}
mapbox::geojson::polygon Polygon::convert(jni::JNIEnv& env, const jni::Object<Polygon>& jPolygon) {
mapbox::geojson::polygon polygon;
if (jPolygon) {
polygon = Polygon::convert(env, Polygon::coordinates(env, jPolygon));
}
return polygon;
}
mapbox::geojson::polygon Polygon::convert(
jni::JNIEnv& env, const jni::Object<java::util::List /*<java::util::List<Point>>*/>& jPointListsList) {
mapbox::geojson::polygon polygon;
if (jPointListsList) {
auto multiLine = MultiLineString::convert(env, jPointListsList);
polygon.reserve(multiLine.size());
for (auto&& line : multiLine) {
polygon.emplace_back(convertExplicit<mapbox::geojson::linear_ring>(std::move(line)));
}
}
return polygon;
}
jni::Local<jni::Object<java::util::List>> Polygon::coordinates(jni::JNIEnv& env, const jni::Object<Polygon>& jPolygon) {
static auto& javaClass = jni::Class<Polygon>::Singleton(env);
static auto method = javaClass.GetMethod<jni::Object<java::util::List>()>(env, "coordinates");
return jPolygon.Call(env, method);
}
void Polygon::registerNative(jni::JNIEnv& env) {
jni::Class<Polygon>::Singleton(env);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,35 @@
#pragma once
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
#include "geometry.hpp"
#include "../java/util.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
class Polygon {
public:
using SuperTag = Geometry;
static constexpr auto Name() { return "com/mapbox/geojson/Polygon"; };
static constexpr auto Type() { return "Polygon"; };
static jni::Local<jni::Object<Polygon>> New(jni::JNIEnv &, const mbgl::Polygon<double> &);
static mapbox::geojson::polygon convert(jni::JNIEnv &, const jni::Object<Polygon> &);
static mapbox::geojson::polygon convert(jni::JNIEnv &,
const jni::Object<java::util::List /*<java::util::List<Point>>*/> &);
static jni::Local<jni::Object<java::util::List>> coordinates(jni::JNIEnv &, const jni::Object<Polygon> &);
static void registerNative(jni::JNIEnv &);
};
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,50 @@
#pragma once
#include "point.hpp"
#include <type_traits>
namespace mbgl {
namespace android {
namespace geojson {
// Clang 3.8 fails to implicitly convert matching types, so we'll have to do it explicitly.
template <typename To, typename From>
To convertExplicit(From&& src) {
static_assert(std::is_same<typename std::decay_t<From>::container_type, typename To::container_type>::value,
"container types do not match");
static_assert(std::is_rvalue_reference<From&&>::value, "argument must be rvalue reference");
return *reinterpret_cast<std::add_pointer_t<To>>(&src);
}
/**
* Geometry -> List<Point>
*/
template <class T>
static jni::Local<jni::Object<java::util::List>> asPointsList(jni::JNIEnv& env, const T& pointsList) {
auto jarray = jni::Array<jni::Object<Point>>::New(env, pointsList.size());
for (jni::jsize i = 0; i < pointsList.size(); i++) {
jarray.Set(env, i, Point::New(env, pointsList.at(i)));
}
return java::util::Arrays::asList(env, jarray);
}
/**
* Geometry -> List<List<Point>>
*/
template <class SHAPE>
static jni::Local<jni::Object<java::util::List>> asPointsListsList(JNIEnv& env, const SHAPE& value) {
auto jarray = jni::Array<jni::Object<java::util::List>>::New(env, value.size());
for (size_t i = 0; i < value.size(); i++) {
jarray.Set(env, i, asPointsList(env, value[i]));
}
return java::util::Arrays::asList(env, jarray);
}
} // namespace geojson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,29 @@
#include "lat_lng.hpp"
namespace mbgl {
namespace android {
jni::Local<jni::Object<LatLng>> LatLng::New(jni::JNIEnv& env, const mbgl::LatLng& latLng) {
static auto& javaClass = jni::Class<LatLng>::Singleton(env);
static auto constructor = javaClass.GetConstructor<double, double>(env);
return javaClass.New(env, constructor, latLng.latitude(), latLng.longitude());
}
mbgl::Point<double> LatLng::getGeometry(jni::JNIEnv& env, const jni::Object<LatLng>& latLng) {
static auto& javaClass = jni::Class<LatLng>::Singleton(env);
static auto latitudeField = javaClass.GetField<jni::jdouble>(env, "latitude");
static auto longitudeField = javaClass.GetField<jni::jdouble>(env, "longitude");
return mbgl::Point<double>(latLng.Get(env, longitudeField), latLng.Get(env, latitudeField));
}
mbgl::LatLng LatLng::getLatLng(jni::JNIEnv& env, const jni::Object<LatLng>& latLng) {
auto point = LatLng::getGeometry(env, latLng);
return mbgl::LatLng(point.y, point.x);
}
void LatLng::registerNative(jni::JNIEnv& env) {
jni::Class<LatLng>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,26 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/geometry.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class LatLng : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "org/maplibre/android/geometry/LatLng"; };
static jni::Local<jni::Object<LatLng>> New(jni::JNIEnv&, const mbgl::LatLng&);
static mbgl::Point<double> getGeometry(jni::JNIEnv&, const jni::Object<LatLng>&);
static mbgl::LatLng getLatLng(jni::JNIEnv&, const jni::Object<LatLng>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,30 @@
#include "lat_lng_bounds.hpp"
namespace mbgl {
namespace android {
jni::Local<jni::Object<LatLngBounds>> LatLngBounds::New(jni::JNIEnv& env, mbgl::LatLngBounds bounds) {
static auto& javaClass = jni::Class<LatLngBounds>::Singleton(env);
static auto constructor = javaClass.GetConstructor<double, double, double, double>(env);
return javaClass.New(env, constructor, bounds.north(), bounds.east(), bounds.south(), bounds.west());
}
mbgl::LatLngBounds LatLngBounds::getLatLngBounds(jni::JNIEnv& env, const jni::Object<LatLngBounds>& bounds) {
static auto& javaClass = jni::Class<LatLngBounds>::Singleton(env);
static auto swLatField = javaClass.GetField<jni::jdouble>(env, "latitudeSouth");
static auto swLonField = javaClass.GetField<jni::jdouble>(env, "longitudeWest");
static auto neLatField = javaClass.GetField<jni::jdouble>(env, "latitudeNorth");
static auto neLonField = javaClass.GetField<jni::jdouble>(env, "longitudeEast");
mbgl::LatLng sw = {bounds.Get(env, swLatField), bounds.Get(env, swLonField)};
mbgl::LatLng ne = {bounds.Get(env, neLatField), bounds.Get(env, neLonField)};
return mbgl::LatLngBounds::hull(sw, ne);
}
void LatLngBounds::registerNative(jni::JNIEnv& env) {
jni::Class<LatLngBounds>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,24 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/geometry.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class LatLngBounds : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "org/maplibre/android/geometry/LatLngBounds"; };
static jni::Local<jni::Object<LatLngBounds>> New(jni::JNIEnv&, mbgl::LatLngBounds);
static mbgl::LatLngBounds getLatLngBounds(jni::JNIEnv&, const jni::Object<LatLngBounds>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,38 @@
#include "lat_lng_quad.hpp"
#include "lat_lng.hpp"
namespace mbgl {
namespace android {
jni::Local<jni::Object<LatLngQuad>> LatLngQuad::New(jni::JNIEnv& env, std::array<mbgl::LatLng, 4> coordinates) {
static auto& javaClass = jni::Class<LatLngQuad>::Singleton(env);
static auto quadConstructor =
javaClass.GetConstructor<jni::Object<LatLng>, jni::Object<LatLng>, jni::Object<LatLng>, jni::Object<LatLng>>(
env);
return javaClass.New(env,
quadConstructor,
LatLng::New(env, coordinates[0]),
LatLng::New(env, coordinates[1]),
LatLng::New(env, coordinates[2]),
LatLng::New(env, coordinates[3]));
}
std::array<mbgl::LatLng, 4> LatLngQuad::getLatLngArray(jni::JNIEnv& env, const jni::Object<LatLngQuad>& quad) {
static auto& javaClass = jni::Class<LatLngQuad>::Singleton(env);
static auto topLeftField = javaClass.GetField<jni::Object<LatLng>>(env, "topLeft");
static auto topRightField = javaClass.GetField<jni::Object<LatLng>>(env, "topRight");
static auto bottomRightField = javaClass.GetField<jni::Object<LatLng>>(env, "bottomRight");
static auto bottomLeftField = javaClass.GetField<jni::Object<LatLng>>(env, "bottomLeft");
return std::array<mbgl::LatLng, 4>{{LatLng::getLatLng(env, quad.Get(env, topLeftField)),
LatLng::getLatLng(env, quad.Get(env, topRightField)),
LatLng::getLatLng(env, quad.Get(env, bottomRightField)),
LatLng::getLatLng(env, quad.Get(env, bottomLeftField))}};
}
void LatLngQuad::registerNative(jni::JNIEnv& env) {
jni::Class<LatLngQuad>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,25 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/geometry.hpp>
#include <jni/jni.hpp>
#include <array>
namespace mbgl {
namespace android {
class LatLngQuad : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "org/maplibre/android/geometry/LatLngQuad"; };
static jni::Local<jni::Object<LatLngQuad>> New(jni::JNIEnv&, std::array<mbgl::LatLng, 4>);
static std::array<mbgl::LatLng, 4> getLatLngArray(jni::JNIEnv&, const jni::Object<LatLngQuad>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,17 @@
#include "projected_meters.hpp"
namespace mbgl {
namespace android {
jni::Local<jni::Object<ProjectedMeters>> ProjectedMeters::New(jni::JNIEnv& env, double northing, double easting) {
static auto& javaClass = jni::Class<ProjectedMeters>::Singleton(env);
static auto constructor = javaClass.GetConstructor<double, double>(env);
return javaClass.New(env, constructor, northing, easting);
}
void ProjectedMeters::registerNative(jni::JNIEnv& env) {
jni::Class<ProjectedMeters>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,21 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geometry.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class ProjectedMeters : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "org/maplibre/android/geometry/ProjectedMeters"; };
static jni::Local<jni::Object<ProjectedMeters>> New(jni::JNIEnv&, double, double);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,25 @@
#include <mbgl/util/geo.hpp>
#include "pointf.hpp"
namespace mbgl {
namespace android {
jni::Local<jni::Object<PointF>> PointF::New(jni::JNIEnv& env, float x, float y) {
static auto& javaClass = jni::Class<PointF>::Singleton(env);
static auto constructor = javaClass.GetConstructor<float, float>(env);
return javaClass.New(env, constructor, x, y);
}
mbgl::ScreenCoordinate PointF::getScreenCoordinate(jni::JNIEnv& env, const jni::Object<PointF>& point) {
static auto& javaClass = jni::Class<PointF>::Singleton(env);
static auto xField = javaClass.GetField<jni::jfloat>(env, "x");
static auto yField = javaClass.GetField<jni::jfloat>(env, "y");
return mbgl::ScreenCoordinate{point.Get(env, xField), point.Get(env, yField)};
}
void PointF::registerNative(jni::JNIEnv& env) {
jni::Class<PointF>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,22 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class PointF : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "android/graphics/PointF"; };
static jni::Local<jni::Object<PointF>> New(jni::JNIEnv&, float, float);
static mbgl::ScreenCoordinate getScreenCoordinate(jni::JNIEnv&, const jni::Object<PointF>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,35 @@
#include "rectf.hpp"
namespace mbgl {
namespace android {
float RectF::getLeft(jni::JNIEnv& env, const jni::Object<RectF>& rectf) {
static auto& javaClass = jni::Class<RectF>::Singleton(env);
static auto field = javaClass.GetField<float>(env, "left");
return rectf.Get(env, field);
}
float RectF::getTop(jni::JNIEnv& env, const jni::Object<RectF>& rectf) {
static auto& javaClass = jni::Class<RectF>::Singleton(env);
static auto field = javaClass.GetField<float>(env, "top");
return rectf.Get(env, field);
}
float RectF::getRight(jni::JNIEnv& env, const jni::Object<RectF>& rectf) {
static auto& javaClass = jni::Class<RectF>::Singleton(env);
static auto field = javaClass.GetField<float>(env, "right");
return rectf.Get(env, field);
}
float RectF::getBottom(jni::JNIEnv& env, const jni::Object<RectF>& rectf) {
static auto& javaClass = jni::Class<RectF>::Singleton(env);
static auto field = javaClass.GetField<float>(env, "bottom");
return rectf.Get(env, field);
}
void RectF::registerNative(jni::JNIEnv& env) {
jni::Class<RectF>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,26 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class RectF : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "android/graphics/RectF"; };
static float getLeft(jni::JNIEnv&, const jni::Object<RectF>&);
static float getTop(jni::JNIEnv&, const jni::Object<RectF>&);
static float getRight(jni::JNIEnv&, const jni::Object<RectF>&);
static float getBottom(jni::JNIEnv&, const jni::Object<RectF>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,50 @@
#include "json_array.hpp"
#include "json_element.hpp"
namespace mbgl {
namespace android {
namespace gson {
jni::Local<jni::Object<JsonArray>> JsonArray::New(jni::JNIEnv& env, const std::vector<mbgl::Value>& values) {
static auto& javaClass = jni::Class<JsonArray>::Singleton(env);
static auto constructor = javaClass.GetConstructor(env);
static auto addMethod = javaClass.GetMethod<void(jni::Object<JsonElement>)>(env, "add");
auto jsonArray = javaClass.New(env, constructor);
for (const auto& v : values) {
jsonArray.Call(env, addMethod, JsonElement::New(env, v));
}
return jsonArray;
}
std::vector<mbgl::Value> JsonArray::convert(jni::JNIEnv& env, const jni::Object<JsonArray>& jsonArray) {
std::vector<mbgl::Value> values;
if (jsonArray) {
static auto& javaClass = jni::Class<JsonArray>::Singleton(env);
static auto getMethod = javaClass.GetMethod<jni::Object<JsonElement>(jni::jint)>(env, "get");
static auto sizeMethod = javaClass.GetMethod<jni::jint()>(env, "size");
int size = jsonArray.Call(env, sizeMethod);
values.reserve(uint(size));
for (int i = 0; i < size; i++) {
auto entry = jsonArray.Call(env, getMethod, i);
if (entry) {
values.push_back(JsonElement::convert(env, entry));
}
}
}
return values;
}
void JsonArray::registerNative(jni::JNIEnv& env) {
jni::Class<JsonArray>::Singleton(env);
}
} // namespace gson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,24 @@
#pragma once
#include "json_element.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace gson {
class JsonArray {
public:
using SuperTag = JsonElement;
static constexpr auto Name() { return "com/google/gson/JsonArray"; };
static jni::Local<jni::Object<JsonArray>> New(jni::JNIEnv&, const std::vector<mbgl::Value>&);
static std::vector<mbgl::Value> convert(JNIEnv&, const jni::Object<JsonArray>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace gson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,77 @@
#include "json_element.hpp"
#include "json_array.hpp"
#include "json_object.hpp"
#include "json_primitive.hpp"
namespace mbgl {
namespace android {
namespace gson {
jni::Local<jni::Object<JsonElement>> JsonElement::New(jni::JNIEnv& env, const mbgl::Value& value) {
static auto& primitive = jni::Class<JsonPrimitive>::Singleton(env);
static auto stringConstructor = primitive.GetConstructor<jni::String>(env);
static auto numberConstructor = primitive.GetConstructor<jni::Number>(env);
static auto booleanConstructor = primitive.GetConstructor<jni::Boolean>(env);
return value.match(
[&](const mbgl::NullValue&) { return jni::Local<jni::Object<JsonElement>>(); },
[&](const std::string& value) {
return primitive.New(env, stringConstructor, jni::Make<jni::String>(env, value));
},
[&](const double value) { return primitive.New(env, numberConstructor, jni::Box(env, value)); },
[&](const int64_t value) { return primitive.New(env, numberConstructor, jni::Box(env, value)); },
[&](const uint64_t value) {
return primitive.New(env, numberConstructor, jni::Box(env, int64_t(value))); // TODO: should use BigInteger
},
[&](const bool value) {
return primitive.New(env, booleanConstructor, jni::Box(env, value ? jni::jni_true : jni::jni_false));
},
[&](const std::vector<mbgl::Value>& values) { return JsonArray::New(env, values); },
[&](const mbgl::PropertyMap& values) { return JsonObject::New(env, values); });
}
mbgl::Value JsonElement::convert(jni::JNIEnv& env, const jni::Object<JsonElement>& jsonElement) {
if (!jsonElement) {
return mbgl::NullValue();
}
static auto& elementClass = jni::Class<JsonElement>::Singleton(env);
static auto isJsonObject = elementClass.GetMethod<jni::jboolean()>(env, "isJsonObject");
static auto isJsonArray = elementClass.GetMethod<jni::jboolean()>(env, "isJsonArray");
static auto isJsonPrimitive = elementClass.GetMethod<jni::jboolean()>(env, "isJsonPrimitive");
static auto& primitiveClass = jni::Class<JsonPrimitive>::Singleton(env);
static auto isBoolean = primitiveClass.GetMethod<jni::jboolean()>(env, "isBoolean");
static auto isString = primitiveClass.GetMethod<jni::jboolean()>(env, "isString");
static auto isNumber = primitiveClass.GetMethod<jni::jboolean()>(env, "isNumber");
static auto getAsBoolean = primitiveClass.GetMethod<jni::jboolean()>(env, "getAsBoolean");
static auto getAsString = primitiveClass.GetMethod<jni::String()>(env, "getAsString");
static auto getAsDouble = primitiveClass.GetMethod<jni::jdouble()>(env, "getAsDouble");
if (jsonElement.Call(env, isJsonPrimitive)) {
auto primitive = jni::Cast(env, primitiveClass, jsonElement);
if (primitive.Call(env, isBoolean)) {
return bool(primitive.Call(env, getAsBoolean));
} else if (primitive.Call(env, isNumber)) {
return primitive.Call(env, getAsDouble);
} else if (primitive.Call(env, isString)) {
return jni::Make<std::string>(env, primitive.Call(env, getAsString));
} else {
return mbgl::NullValue();
}
} else if (jsonElement.Call(env, isJsonObject)) {
return JsonObject::convert(env, jni::Cast(env, jni::Class<JsonObject>::Singleton(env), jsonElement));
} else if (jsonElement.Call(env, isJsonArray)) {
return JsonArray::convert(env, jni::Cast(env, jni::Class<JsonArray>::Singleton(env), jsonElement));
} else {
return mbgl::NullValue();
}
}
void JsonElement::registerNative(jni::JNIEnv& env) {
jni::Class<JsonElement>::Singleton(env);
}
} // namespace gson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,24 @@
#pragma once
#include <mbgl/util/feature.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace gson {
class JsonElement {
public:
using SuperTag = jni::ObjectTag;
static constexpr auto Name() { return "com/google/gson/JsonElement"; };
static jni::Local<jni::Object<JsonElement>> New(jni::JNIEnv&, const mbgl::Value&);
static mbgl::Value convert(JNIEnv&, const jni::Object<JsonElement>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace gson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,62 @@
#include "json_object.hpp"
#include "json_element.hpp"
#include "../java/util.hpp"
namespace mbgl {
namespace android {
namespace gson {
jni::Local<jni::Object<JsonObject>> JsonObject::New(jni::JNIEnv& env, const mbgl::PropertyMap& values) {
static auto& javaClass = jni::Class<JsonObject>::Singleton(env);
static auto constructor = javaClass.GetConstructor(env);
static auto addMethod = javaClass.GetMethod<void(jni::String, jni::Object<JsonElement>)>(env, "add");
auto jsonObject = javaClass.New(env, constructor);
for (auto& item : values) {
jsonObject.Call(env, addMethod, jni::Make<jni::String>(env, item.first), JsonElement::New(env, item.second));
}
return jsonObject;
}
template <typename F> // void (jni::String, jni::Object<gson::JsonElement>)
static void iterateEntrySet(jni::JNIEnv& env, const jni::Object<JsonObject>& jsonObject, F callback) {
// Get Set<Map.Entry<String, JsonElement>>
static auto& javaClass = jni::Class<JsonObject>::Singleton(env);
static auto method = javaClass.GetMethod<jni::Object<java::util::Set>()>(env, "entrySet");
auto entryArray = java::util::Set::toArray<java::util::Map::Entry>(env, jsonObject.Call(env, method));
size_t size = entryArray.Length(env);
for (size_t i = 0; i < size; i++) {
auto entry = entryArray.Get(env, i);
if (entry) {
callback(java::util::Map::Entry::getKey<jni::StringTag>(env, entry),
java::util::Map::Entry::getValue<gson::JsonElement>(env, entry));
}
}
}
mbgl::PropertyMap JsonObject::convert(jni::JNIEnv& env, const jni::Object<JsonObject>& jsonObject) {
mbgl::PropertyMap map;
if (jsonObject) {
iterateEntrySet(
env, jsonObject, [&map, &env](const jni::String& jId, const jni::Object<gson::JsonElement>& jsonElement) {
map[jni::Make<std::string>(env, jId)] = JsonElement::convert(env, jsonElement);
});
}
return map;
}
void JsonObject::registerNative(jni::JNIEnv& env) {
jni::Class<JsonObject>::Singleton(env);
}
} // namespace gson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,24 @@
#pragma once
#include "json_element.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace gson {
class JsonObject {
public:
using SuperTag = JsonElement;
static constexpr auto Name() { return "com/google/gson/JsonObject"; };
static jni::Local<jni::Object<JsonObject>> New(jni::JNIEnv&, const mbgl::PropertyMap&);
static mbgl::PropertyMap convert(JNIEnv&, const jni::Object<JsonObject>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace gson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,13 @@
#include "json_primitive.hpp"
namespace mbgl {
namespace android {
namespace gson {
void JsonPrimitive::registerNative(jni::JNIEnv &env) {
jni::Class<JsonPrimitive>::Singleton(env);
}
} // namespace gson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,21 @@
#pragma once
#include "json_element.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace gson {
class JsonPrimitive {
public:
using SuperTag = JsonElement;
static constexpr auto Name() { return "com/google/gson/JsonPrimitive"; };
static void registerNative(jni::JNIEnv&);
};
} // namespace gson
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,237 @@
#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/resource_options.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/util/client_options.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/async_request.hpp>
#include <mbgl/util/async_task.hpp>
#include <mbgl/util/http_header.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/util.hpp>
#include <jni/jni.hpp>
#include "attach_env.hpp"
namespace mbgl {
class HTTPFileSource::Impl {
public:
Impl(const ResourceOptions resourceOptions_, const ClientOptions clientOptions_)
: resourceOptions(resourceOptions_.clone()),
clientOptions(clientOptions_.clone()){};
android::UniqueEnv env{android::AttachEnv()};
void setResourceOptions(ResourceOptions options) { resourceOptions = options; };
ResourceOptions getResourceOptions() { return resourceOptions.clone(); };
void setClientOptions(ClientOptions options) { clientOptions = options; };
ClientOptions getClientOptions() { return clientOptions.clone(); };
private:
ResourceOptions resourceOptions;
ClientOptions clientOptions;
};
class HTTPRequest : public AsyncRequest {
public:
static constexpr auto Name() { return "org/maplibre/android/http/NativeHttpRequest"; };
HTTPRequest(jni::JNIEnv&, const Resource&, FileSource::Callback);
~HTTPRequest();
void onFailure(jni::JNIEnv&, int type, const jni::String& message);
void onResponse(jni::JNIEnv&,
int code,
const jni::String& etag,
const jni::String& modified,
const jni::String& cacheControl,
const jni::String& expires,
const jni::String& retryAfter,
const jni::String& xRateLimitReset,
const jni::Array<jni::jbyte>& body);
jni::Global<jni::Object<HTTPRequest>> javaRequest;
private:
Resource resource;
FileSource::Callback callback;
Response response;
util::AsyncTask async{[this] {
// Calling `callback` may result in deleting `this`. Copy data to temporaries first.
auto callback_ = callback;
auto response_ = response;
callback_(response_);
}};
static const int connectionError = 0;
static const int temporaryError = 1;
static const int permanentError = 2;
};
namespace android {
void RegisterNativeHTTPRequest(jni::JNIEnv& env) {
static auto& javaClass = jni::Class<HTTPRequest>::Singleton(env);
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
jni::RegisterNativePeer<HTTPRequest>(env,
javaClass,
"nativePtr",
METHOD(&HTTPRequest::onFailure, "nativeOnFailure"),
METHOD(&HTTPRequest::onResponse, "nativeOnResponse"));
}
} // namespace android
HTTPRequest::HTTPRequest(jni::JNIEnv& env, const Resource& resource_, FileSource::Callback callback_)
: resource(resource_),
callback(callback_) {
std::string etagStr;
std::string modifiedStr;
if (resource.priorEtag) {
etagStr = *resource.priorEtag;
} else if (resource.priorModified) {
modifiedStr = util::rfc1123(*resource.priorModified);
}
jni::UniqueLocalFrame frame = jni::PushLocalFrame(env, 10);
static auto& javaClass = jni::Class<HTTPRequest>::Singleton(env);
static auto constructor =
javaClass.GetConstructor<jni::jlong, jni::String, jni::String, jni::String, jni::jboolean>(env);
javaRequest = jni::NewGlobal(env,
javaClass.New(env,
constructor,
reinterpret_cast<jlong>(this),
jni::Make<jni::String>(env, resource.url),
jni::Make<jni::String>(env, etagStr),
jni::Make<jni::String>(env, modifiedStr),
(jboolean)(resource_.usage == Resource::Usage::Offline)));
}
HTTPRequest::~HTTPRequest() {
android::UniqueEnv env = android::AttachEnv();
static auto& javaClass = jni::Class<HTTPRequest>::Singleton(*env);
static auto cancel = javaClass.GetMethod<void()>(*env, "cancel");
javaRequest.Call(*env, cancel);
}
void HTTPRequest::onResponse(jni::JNIEnv& env,
int code,
const jni::String& etag,
const jni::String& modified,
const jni::String& cacheControl,
const jni::String& expires,
const jni::String& jRetryAfter,
const jni::String& jXRateLimitReset,
const jni::Array<jni::jbyte>& body) {
using Error = Response::Error;
if (etag) {
response.etag = jni::Make<std::string>(env, etag);
}
if (modified) {
response.modified = util::parseTimestamp(jni::Make<std::string>(env, modified).c_str());
}
if (cacheControl) {
const auto cc = http::CacheControl::parse(jni::Make<std::string>(env, cacheControl).c_str());
response.expires = cc.toTimePoint();
response.mustRevalidate = cc.mustRevalidate;
}
if (expires) {
response.expires = util::parseTimestamp(jni::Make<std::string>(env, expires).c_str());
}
if (code == 200) {
if (body) {
auto data = std::make_shared<std::string>(body.Length(env), char());
jni::GetArrayRegion(env, *body, 0, data->size(), reinterpret_cast<jbyte*>(&(*data)[0]));
response.data = data;
} else {
response.data = std::make_shared<std::string>();
}
} else if (code == 204 || (code == 404 && resource.kind == Resource::Kind::Tile)) {
response.noContent = true;
} else if (code == 304) {
response.notModified = true;
} else if (code == 404) {
response.error = std::make_unique<Error>(Error::Reason::NotFound, "HTTP status code 404");
} else if (code == 429) {
std::optional<std::string> retryAfter;
std::optional<std::string> xRateLimitReset;
if (jRetryAfter) {
retryAfter = jni::Make<std::string>(env, jRetryAfter);
}
if (jXRateLimitReset) {
xRateLimitReset = jni::Make<std::string>(env, jXRateLimitReset);
}
response.error = std::make_unique<Error>(
Error::Reason::RateLimit, "HTTP status code 429", http::parseRetryHeaders(retryAfter, xRateLimitReset));
} else if (code >= 500 && code < 600) {
response.error = std::make_unique<Error>(Error::Reason::Server,
std::string{"HTTP status code "} + util::toString(code));
} else {
response.error = std::make_unique<Error>(Error::Reason::Other,
std::string{"HTTP status code "} + util::toString(code));
}
async.send();
}
void HTTPRequest::onFailure(jni::JNIEnv& env, int type, const jni::String& message) {
std::string messageStr = jni::Make<std::string>(env, message);
using Error = Response::Error;
switch (type) {
case connectionError:
response.error = std::make_unique<Error>(Error::Reason::Connection, messageStr);
break;
case temporaryError:
response.error = std::make_unique<Error>(Error::Reason::Server, messageStr);
break;
default:
response.error = std::make_unique<Error>(Error::Reason::Other, messageStr);
}
async.send();
}
HTTPFileSource::HTTPFileSource(const ResourceOptions& resourceOptions, const ClientOptions& clientOptions)
: impl(std::make_unique<Impl>(resourceOptions.clone(), clientOptions.clone())) {}
HTTPFileSource::~HTTPFileSource() = default;
std::unique_ptr<AsyncRequest> HTTPFileSource::request(const Resource& resource, Callback callback) {
return std::make_unique<HTTPRequest>(*impl->env, resource, callback);
}
void HTTPFileSource::setResourceOptions(ResourceOptions options) {
impl->setResourceOptions(options.clone());
}
ResourceOptions HTTPFileSource::getResourceOptions() {
return impl->getResourceOptions();
}
void HTTPFileSource::setClientOptions(ClientOptions options) {
impl->setClientOptions(options.clone());
}
ClientOptions HTTPFileSource::getClientOptions() {
return impl->getClientOptions();
}
} // namespace mbgl

View File

@@ -0,0 +1,195 @@
#include <mbgl/i18n/collator.hpp>
#include <mbgl/text/language_tag.hpp>
#include <jni/jni.hpp>
#include "attach_env.hpp"
#include "collator_jni.hpp"
namespace mbgl {
namespace android {
void Collator::registerNative(jni::JNIEnv& env) {
jni::Class<Collator>::Singleton(env);
}
jni::Local<jni::Object<Collator>> Collator::getInstance(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
static auto& javaClass = jni::Class<Collator>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<Collator>(jni::Object<Locale>)>(env, "getInstance");
return javaClass.Call(env, method, locale);
}
void Collator::setStrength(jni::JNIEnv& env, const jni::Object<Collator>& collator, jni::jint strength) {
static auto& javaClass = jni::Class<Collator>::Singleton(env);
static auto method = javaClass.GetMethod<void(jni::jint)>(env, "setStrength");
collator.Call(env, method, strength);
}
jni::jint Collator::compare(jni::JNIEnv& env,
const jni::Object<Collator>& collator,
const jni::String& lhs,
const jni::String& rhs) {
static auto& javaClass = jni::Class<Collator>::Singleton(env);
static auto method = javaClass.GetMethod<jni::jint(jni::String, jni::String)>(env, "compare");
return collator.Call(env, method, lhs, rhs);
}
void StringUtils::registerNative(jni::JNIEnv& env) {
jni::Class<StringUtils>::Singleton(env);
}
jni::Local<jni::String> StringUtils::unaccent(jni::JNIEnv& env, const jni::String& value) {
static auto& javaClass = jni::Class<StringUtils>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::String(jni::String)>(env, "unaccent");
return javaClass.Call(env, method, value);
}
void Locale::registerNative(jni::JNIEnv& env) {
jni::Class<Locale>::Singleton(env);
}
/*
We would prefer to use for/toLanguageTag, but they're only available in API level 21+
jni::Object<Locale> Locale::forLanguageTag(jni::JNIEnv& env, jni::String languageTag) {
using Signature = jni::Object<Locale>(jni::String);
auto method = javaClass.GetStaticMethod<Signature>(env, "forLanguageTag");
return javaClass.Call(env, method, languageTag);
}
jni::String Locale::toLanguageTag(jni::JNIEnv& env, jni::Object<Locale> locale) {
using Signature = jni::String();
auto static method = javaClass.GetMethod<Signature>(env, "toLanguageTag");
return locale.Call(env, method);
}
*/
jni::Local<jni::String> Locale::getLanguage(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
static auto& javaClass = jni::Class<Locale>::Singleton(env);
static auto method = javaClass.GetMethod<jni::String()>(env, "getLanguage");
return locale.Call(env, method);
}
jni::Local<jni::String> Locale::getCountry(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
static auto& javaClass = jni::Class<Locale>::Singleton(env);
static auto method = javaClass.GetMethod<jni::String()>(env, "getCountry");
return locale.Call(env, method);
}
jni::Local<jni::Object<Locale>> Locale::getDefault(jni::JNIEnv& env) {
static auto& javaClass = jni::Class<Locale>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<Locale>()>(env, "getDefault");
return javaClass.Call(env, method);
}
jni::Local<jni::Object<Locale>> Locale::New(jni::JNIEnv& env, const jni::String& language) {
static auto& javaClass = jni::Class<Locale>::Singleton(env);
static auto constructor = javaClass.GetConstructor<jni::String>(env);
return javaClass.New(env, constructor, language);
}
jni::Local<jni::Object<Locale>> Locale::New(jni::JNIEnv& env, const jni::String& language, const jni::String& region) {
static auto& javaClass = jni::Class<Locale>::Singleton(env);
static auto constructor = javaClass.GetConstructor<jni::String, jni::String>(env);
return javaClass.New(env, constructor, language, region);
}
} // namespace android
namespace platform {
class Collator::Impl {
public:
Impl(bool caseSensitive_, bool diacriticSensitive_, std::optional<std::string> locale_)
: caseSensitive(caseSensitive_),
diacriticSensitive(diacriticSensitive_),
env(android::AttachEnv()) {
LanguageTag languageTag = locale_ ? LanguageTag::fromBCP47(*locale_) : LanguageTag();
if (!languageTag.language) {
locale = jni::NewGlobal(*env, android::Locale::getDefault(*env));
} else if (!languageTag.region) {
locale = jni::NewGlobal(*env,
android::Locale::New(*env, jni::Make<jni::String>(*env, *languageTag.language)));
} else {
locale = jni::NewGlobal(*env,
android::Locale::New(*env,
jni::Make<jni::String>(*env, *languageTag.language),
jni::Make<jni::String>(*env, *languageTag.region)));
}
collator = jni::NewGlobal(*env, android::Collator::getInstance(*env, locale));
if (!diacriticSensitive && !caseSensitive) {
android::Collator::setStrength(*env, collator, 0 /*PRIMARY*/);
} else if (diacriticSensitive && !caseSensitive) {
android::Collator::setStrength(*env, collator, 1 /*SECONDARY*/);
} else if (caseSensitive) {
// If we're case-sensitive and diacritic-sensitive, we use a
// case-sensitive collator and a fallback implementation of
// diacritic-insensitivity.
android::Collator::setStrength(*env, collator, 2 /*TERTIARY*/);
}
}
bool operator==(const Impl& other) const {
return caseSensitive == other.caseSensitive && diacriticSensitive == other.diacriticSensitive &&
resolvedLocale() == other.resolvedLocale();
}
int compare(const std::string& lhs, const std::string& rhs) const {
bool useUnaccent = !diacriticSensitive && caseSensitive;
// java.text.Collator doesn't support a
// diacritic-insensitive/case-sensitive collation order, so we have to
// compromise here. We use Android's case-sensitive Collator against
// strings that have been "unaccented" using non-locale-aware nunicode
// logic. Because of the difference in locale-awareness, this means
// turning on case-sensitivity can _potentially_ change compare results
// for strings that don't actually have any case differences.
jni::Local<jni::String> jlhs = useUnaccent
? android::StringUtils::unaccent(*env, jni::Make<jni::String>(*env, lhs))
: jni::Make<jni::String>(*env, lhs);
jni::Local<jni::String> jrhs = useUnaccent
? android::StringUtils::unaccent(*env, jni::Make<jni::String>(*env, rhs))
: jni::Make<jni::String>(*env, rhs);
jni::jint result = android::Collator::compare(*env, collator, jlhs, jrhs);
return result;
}
std::string resolvedLocale() const {
std::string language = jni::Make<std::string>(*env, android::Locale::getLanguage(*env, locale));
std::string region = jni::Make<std::string>(*env, android::Locale::getCountry(*env, locale));
std::optional<std::string> resultLanguage;
if (!language.empty()) resultLanguage = language;
std::optional<std::string> resultRegion;
if (!region.empty()) resultRegion = region;
return LanguageTag(resultLanguage, {}, resultRegion).toBCP47();
}
private:
bool caseSensitive;
bool diacriticSensitive;
android::UniqueEnv env;
jni::Global<jni::Object<android::Collator>> collator;
jni::Global<jni::Object<android::Locale>> locale;
};
Collator::Collator(bool caseSensitive, bool diacriticSensitive, const std::optional<std::string>& locale_)
: impl(std::make_shared<Impl>(caseSensitive, diacriticSensitive, locale_)) {}
bool Collator::operator==(const Collator& other) const {
return *impl == *(other.impl);
}
int Collator::compare(const std::string& lhs, const std::string& rhs) const {
return impl->compare(lhs, rhs);
}
std::string Collator::resolvedLocale() const {
return impl->resolvedLocale();
}
} // namespace platform
} // namespace mbgl

View File

@@ -0,0 +1,58 @@
#pragma once
#include <jni/jni.hpp>
/*
android::Collator and android::Locale are
the JNI wrappers of
java/text/Collator and java/util/Locale
mbgl::Collator is the portable interface
Both implementations are in collator.cpp
*/
namespace mbgl {
namespace android {
class Locale {
public:
static constexpr auto Name() { return "java/util/Locale"; };
/* Requires API level 21+ in order to support script/variant
static jni::Object<Locale> forLanguageTag(jni::JNIEnv&, jni::String);
static jni::String toLanguageTag(jni::JNIEnv&, jni::Object<Locale>);
*/
static jni::Local<jni::Object<Locale>> getDefault(jni::JNIEnv&);
static jni::Local<jni::String> getLanguage(jni::JNIEnv&, const jni::Object<Locale>&);
static jni::Local<jni::String> getCountry(jni::JNIEnv&, const jni::Object<Locale>&);
static jni::Local<jni::Object<Locale>> New(jni::JNIEnv&, const jni::String&);
static jni::Local<jni::Object<Locale>> New(jni::JNIEnv&, const jni::String&, const jni::String&);
static void registerNative(jni::JNIEnv&);
};
class Collator {
public:
static constexpr auto Name() { return "java/text/Collator"; };
static jni::Local<jni::Object<Collator>> getInstance(jni::JNIEnv&, const jni::Object<Locale>&);
static void setStrength(jni::JNIEnv&, const jni::Object<Collator>&, jni::jint);
static jni::jint compare(jni::JNIEnv&, const jni::Object<Collator>&, const jni::String&, const jni::String&);
static void registerNative(jni::JNIEnv&);
};
class StringUtils {
public:
static constexpr auto Name() { return "org/maplibre/android/utils/StringUtils"; };
static jni::Local<jni::String> unaccent(jni::JNIEnv&, const jni::String&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,88 @@
#include <mbgl/i18n/number_format.hpp>
#include <mbgl/text/language_tag.hpp>
#include <jni/jni.hpp>
#include "attach_env.hpp"
#include "number_format_jni.hpp"
namespace mbgl {
namespace android {
void NumberFormat::registerNative(jni::JNIEnv& env) {
jni::Class<NumberFormat>::Singleton(env);
}
jni::Local<jni::Object<NumberFormat>> NumberFormat::getInstance(jni::JNIEnv& env, const jni::Object<Locale>& locale) {
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<NumberFormat>(jni::Object<Locale>)>(env, "getInstance");
return javaClass.Call(env, method, locale);
}
jni::Local<jni::Object<NumberFormat>> NumberFormat::getCurrencyInstance(jni::JNIEnv& env,
const jni::Object<Locale>& locale) {
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
static auto method = javaClass.GetStaticMethod<jni::Object<NumberFormat>(jni::Object<Locale>)>(
env, "getCurrencyInstance");
return javaClass.Call(env, method, locale);
}
jni::Local<jni::String> NumberFormat::format(jni::JNIEnv& env,
const jni::Object<NumberFormat>& nf,
jni::jdouble number) {
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
static auto method = javaClass.GetMethod<jni::String(jni::jdouble)>(env, "format");
return nf.Call(env, method, number);
}
void NumberFormat::setMinimumFractionDigits(jni::JNIEnv& env, const jni::Object<NumberFormat>& nf, jni::jint value) {
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
static auto method = javaClass.GetMethod<void(jni::jint)>(env, "setMinimumFractionDigits");
return nf.Call(env, method, value);
}
void NumberFormat::setMaximumFractionDigits(jni::JNIEnv& env, const jni::Object<NumberFormat>& nf, jni::jint value) {
static auto& javaClass = jni::Class<NumberFormat>::Singleton(env);
static auto method = javaClass.GetMethod<void(jni::jint)>(env, "setMaximumFractionDigits");
return nf.Call(env, method, value);
}
} // namespace android
namespace platform {
std::string formatNumber(double number,
const std::string& localeId,
const std::string& currency,
uint8_t minFractionDigits,
uint8_t maxFractionDigits) {
auto env{android::AttachEnv()};
jni::Global<jni::Object<android::Locale>> locale;
LanguageTag languageTag = !localeId.empty() ? LanguageTag::fromBCP47(localeId) : LanguageTag();
if (!languageTag.language) {
locale = jni::NewGlobal(*env, android::Locale::getDefault(*env));
} else if (!languageTag.region) {
locale = jni::NewGlobal(*env, android::Locale::New(*env, jni::Make<jni::String>(*env, *languageTag.language)));
} else {
locale = jni::NewGlobal(*env,
android::Locale::New(*env,
jni::Make<jni::String>(*env, *languageTag.language),
jni::Make<jni::String>(*env, *languageTag.region)));
}
jni::Global<jni::Object<android::NumberFormat>> formatter;
if (currency.empty()) {
formatter = jni::NewGlobal(*env, android::NumberFormat::getInstance(*env, locale));
android::NumberFormat::setMinimumFractionDigits(*env, formatter, static_cast<jni::jint>(minFractionDigits));
android::NumberFormat::setMaximumFractionDigits(*env, formatter, static_cast<jni::jint>(maxFractionDigits));
} else {
formatter = jni::NewGlobal(*env, android::NumberFormat::getCurrencyInstance(*env, locale));
}
auto result = android::NumberFormat::format(*env, formatter, static_cast<jni::jdouble>(number));
return jni::Make<std::string>(*env, result);
}
} // namespace platform
} // namespace mbgl

View File

@@ -0,0 +1,29 @@
#pragma once
#include <jni/jni.hpp>
#include "collator_jni.hpp"
/*
android::NumberFormat is the JNI wrapper
of java/text/NumberFormat.
*/
namespace mbgl {
namespace android {
class NumberFormat {
public:
static constexpr auto Name() { return "java/text/NumberFormat"; };
static jni::Local<jni::Object<NumberFormat>> getInstance(jni::JNIEnv&, const jni::Object<Locale>&);
static jni::Local<jni::Object<NumberFormat>> getCurrencyInstance(jni::JNIEnv&, const jni::Object<Locale>&);
static jni::Local<jni::String> format(jni::JNIEnv&, const jni::Object<NumberFormat>&, jni::jdouble);
static void setMinimumFractionDigits(jni::JNIEnv&, const jni::Object<NumberFormat>&, jni::jint);
static void setMaximumFractionDigits(jni::JNIEnv&, const jni::Object<NumberFormat>&, jni::jint);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,19 @@
#include "util.hpp"
namespace mbgl {
namespace android {
namespace java {
namespace util {
void registerNative(jni::JNIEnv& env) {
jni::Class<Arrays>::Singleton(env);
jni::Class<List>::Singleton(env);
jni::Class<Set>::Singleton(env);
jni::Class<Map>::Singleton(env);
jni::Class<Map::Entry>::Singleton(env);
}
} // namespace util
} // namespace java
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,81 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace java {
namespace util {
class List : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "java/util/List"; };
template <class T>
static jni::Local<jni::Array<jni::Object<T>>> toArray(jni::JNIEnv& env, const jni::Object<List>& list) {
static auto& javaClass = jni::Class<List>::Singleton(env);
static auto toArray = javaClass.GetMethod<jni::Array<jni::Object<>>()>(env, "toArray");
return jni::Local<jni::Array<jni::Object<T>>>(env, list.Call(env, toArray).release());
};
};
class Arrays : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "java/util/Arrays"; };
template <class T>
static jni::Local<jni::Object<List>> asList(jni::JNIEnv& env, const jni::Array<jni::Object<T>>& array) {
static auto& javaClass = jni::Class<Arrays>::Singleton(env);
static auto asList = javaClass.GetStaticMethod<jni::Object<List>(jni::Array<jni::Object<>>)>(env, "asList");
auto typeErasedArray = jni::Local<jni::Array<jni::Object<>>>(env, jni::NewLocal(env, array).release());
return javaClass.Call(env, asList, typeErasedArray);
}
};
class Set : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "java/util/Set"; };
template <class T>
static jni::Local<jni::Array<jni::Object<T>>> toArray(jni::JNIEnv& env, const jni::Object<Set>& list) {
static auto& javaClass = jni::Class<Set>::Singleton(env);
static auto toArray = javaClass.GetMethod<jni::Array<jni::Object<>>()>(env, "toArray");
return jni::Local<jni::Array<jni::Object<T>>>(env, list.Call(env, toArray).release());
};
};
class Map : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "java/util/Map"; };
class Entry : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "java/util/Map$Entry"; };
template <class T>
static jni::Local<jni::Object<T>> getKey(jni::JNIEnv& env, const jni::Object<Entry>& entry) {
static auto& javaClass = jni::Class<Map::Entry>::Singleton(env);
static auto method = javaClass.GetMethod<jni::Object<>()>(env, "getKey");
return jni::Cast(env, jni::Class<T>::Singleton(env), entry.Call(env, method));
}
template <class T>
static jni::Local<jni::Object<T>> getValue(jni::JNIEnv& env, const jni::Object<Entry>& entry) {
static auto& javaClass = jni::Class<Map::Entry>::Singleton(env);
static auto method = javaClass.GetMethod<jni::Object<>()>(env, "getValue");
return jni::Cast(env, jni::Class<T>::Singleton(env), entry.Call(env, method));
}
};
};
void registerNative(jni::JNIEnv&);
} // namespace util
} // namespace java
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,49 @@
#include "java_types.hpp"
namespace mbgl {
namespace android {
namespace java {
jni::jclass* ObjectArray::jclass;
jni::jclass* String::jclass;
jni::jclass* Boolean::jclass;
jni::jmethodID* Boolean::booleanValueMethodId;
jni::jclass* Number::jclass;
jni::jmethodID* Number::floatValueMethodId;
jni::jmethodID* Number::doubleValueMethodId;
jni::jmethodID* Number::longValueMethodId;
jni::jclass* Map::jclass;
jni::jmethodID* Map::getMethodId;
jni::jmethodID* Map::keySetMethodId;
jni::jclass* Set::jclass;
jni::jmethodID* Set::toArrayMethodId;
void registerNatives(JNIEnv& env) {
ObjectArray::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "[Ljava/lang/Object;")).release();
String::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/String")).release();
Boolean::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Boolean")).release();
Boolean::booleanValueMethodId = &jni::GetMethodID(env, *Boolean::jclass, "booleanValue", "()Z");
Number::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Number")).release();
Number::floatValueMethodId = &jni::GetMethodID(env, *Number::jclass, "floatValue", "()F");
Number::doubleValueMethodId = &jni::GetMethodID(env, *Number::jclass, "doubleValue", "()D");
Number::longValueMethodId = &jni::GetMethodID(env, *Number::jclass, "longValue", "()J");
Map::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/Map")).release();
Map::getMethodId = &jni::GetMethodID(env, *Map::jclass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
Map::keySetMethodId = &jni::GetMethodID(env, *Map::jclass, "keySet", "()Ljava/util/Set;");
Set::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/Set")).release();
Set::toArrayMethodId = &jni::GetMethodID(env, *Set::jclass, "toArray", "()[Ljava/lang/Object;");
}
} // namespace java
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,43 @@
#pragma once
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace java {
struct ObjectArray {
static jni::jclass* jclass;
};
struct String {
static jni::jclass* jclass;
};
struct Boolean {
static jni::jclass* jclass;
static jni::jmethodID* booleanValueMethodId;
};
struct Number {
static jni::jclass* jclass;
static jni::jmethodID* floatValueMethodId;
static jni::jmethodID* doubleValueMethodId;
static jni::jmethodID* longValueMethodId;
};
struct Map {
static jni::jclass* jclass;
static jni::jmethodID* getMethodId;
static jni::jmethodID* keySetMethodId;
};
struct Set {
static jni::jclass* jclass;
static jni::jmethodID* toArrayMethodId;
};
void registerNatives(JNIEnv&);
} // namespace java
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,173 @@
#include "jni_native.hpp"
#include "annotation/marker.hpp"
#include "annotation/polygon.hpp"
#include "annotation/polyline.hpp"
#include "bitmap.hpp"
#include "bitmap_factory.hpp"
#include "connectivity_listener.hpp"
#include "conversion/collection.hpp"
#include "conversion/conversion.hpp"
#include "file_source.hpp"
#include "geojson/feature.hpp"
#include "geojson/feature_collection.hpp"
#include "geojson/geometry.hpp"
#include "geojson/geometry_collection.hpp"
#include "geojson/line_string.hpp"
#include "geojson/multi_line_string.hpp"
#include "geojson/multi_point.hpp"
#include "geojson/multi_polygon.hpp"
#include "geojson/point.hpp"
#include "geojson/polygon.hpp"
#include "geometry/lat_lng.hpp"
#include "geometry/lat_lng_bounds.hpp"
#include "geometry/lat_lng_quad.hpp"
#include "geometry/projected_meters.hpp"
#include "graphics/pointf.hpp"
#include "graphics/rectf.hpp"
#include "gson/json_array.hpp"
#include "gson/json_element.hpp"
#include "gson/json_object.hpp"
#include "gson/json_primitive.hpp"
#include "java_types.hpp"
#include "map_renderer.hpp"
#include "map_renderer_runnable.hpp"
#include "maplibre.hpp"
#include "native_map_view.hpp"
#include "util/tile_server_options.hpp"
#ifndef MBGL_MODULE_OFFLINE_DISABLE
#include "offline/offline_manager.hpp"
#include "offline/offline_region.hpp"
#include "offline/offline_region_definition.hpp"
#include "offline/offline_region_error.hpp"
#include "offline/offline_region_status.hpp"
#endif
#include "style/transition_options.hpp"
#include "style/layers/layer_manager.hpp"
#include "style/sources/source.hpp"
#include "style/light.hpp"
#include "style/formatted.hpp"
#include "style/formatted_section.hpp"
#ifndef MBGL_MODULE_SNAPSHOT_DISABLE
#include "snapshotter/map_snapshotter.hpp"
#include "snapshotter/map_snapshot.hpp"
#endif
#include "i18n/collator_jni.hpp"
#include "i18n/number_format_jni.hpp"
#include "logger.hpp"
#include "text/local_glyph_rasterizer_jni.hpp"
namespace mbgl {
namespace android {
void RegisterNativeHTTPRequest(JNIEnv&);
void registerNatives(JavaVM* vm) {
theJVM = vm;
jni::JNIEnv& env = jni::GetEnv(*vm, jni::jni_version_1_6);
// For the FileSource
static mbgl::util::RunLoop mainRunLoop;
FileSource::registerNative(env);
// Basic types
java::registerNatives(env);
java::util::registerNative(env);
PointF::registerNative(env);
RectF::registerNative(env);
// GeoJSON
geojson::Feature::registerNative(env);
geojson::FeatureCollection::registerNative(env);
geojson::Geometry::registerNative(env);
geojson::GeometryCollection::registerNative(env);
geojson::LineString::registerNative(env);
geojson::MultiLineString::registerNative(env);
geojson::MultiPoint::registerNative(env);
geojson::MultiPolygon::registerNative(env);
geojson::Point::registerNative(env);
geojson::Polygon::registerNative(env);
// Geometry
LatLng::registerNative(env);
LatLngBounds::registerNative(env);
LatLngQuad::registerNative(env);
ProjectedMeters::registerNative(env);
// GSon
gson::JsonArray::registerNative(env);
gson::JsonElement::registerNative(env);
gson::JsonObject::registerNative(env);
gson::JsonPrimitive::registerNative(env);
// Annotation
Marker::registerNative(env);
Polygon::registerNative(env);
Polyline::registerNative(env);
// Map
MapRenderer::registerNative(env);
MapRendererRunnable::registerNative(env);
NativeMapView::registerNative(env);
// Http
RegisterNativeHTTPRequest(env);
// Bitmap
Bitmap::registerNative(env);
BitmapFactory::registerNative(env);
// Style
TransitionOptions::registerNative(env);
LayerManagerAndroid::get()->registerNative(env);
Source::registerNative(env);
Light::registerNative(env);
Position::registerNative(env);
Formatted::registerNative(env);
FormattedSection::registerNative(env);
// Map
CameraPosition::registerNative(env);
Image::registerNative(env);
// Connectivity
ConnectivityListener::registerNative(env);
// Offline
#ifndef MBGL_MODULE_OFFLINE_DISABLE
OfflineManager::registerNative(env);
OfflineRegion::registerNative(env);
OfflineRegionDefinition::registerNative(env);
OfflineTilePyramidRegionDefinition::registerNative(env);
OfflineGeometryRegionDefinition::registerNative(env);
OfflineRegionError::registerNative(env);
OfflineRegionStatus::registerNative(env);
#endif
// Snapshotter
#ifndef MBGL_MODULE_SNAPSHOT_DISABLE
MapSnapshotter::registerNative(env);
MapSnapshot::registerNative(env);
#endif
// text
LocalGlyphRasterizer::registerNative(env);
Locale::registerNative(env);
Collator::registerNative(env);
StringUtils::registerNative(env);
NumberFormat::registerNative(env);
// Logger
Logger::registerNative(env);
// AssetManager
MapLibre::registerNative(env);
// Util
DefaultStyle::registerNative(env);
TileServerOptions::registerNative(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,13 @@
#pragma once
#include <mbgl/util/util.hpp>
#include "jni.hpp"
namespace mbgl {
namespace android {
MBGL_EXPORT void registerNatives(JavaVM* vm);
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,34 @@
#include "logger.hpp"
#include "java_types.hpp"
namespace mbgl {
namespace android {
void Logger::registerNative(jni::JNIEnv& env) {
jni::Class<Logger>::Singleton(env);
}
void Logger::log(jni::JNIEnv& env, EventSeverity severity, const std::string& msg) {
static auto& _class = jni::Class<Logger>::Singleton(env);
auto tag = jni::Make<jni::String>(env, "Mbgl");
auto message = jni::Make<jni::String>(env, msg);
using Signature = void(jni::String, jni::String);
if (severity == EventSeverity::Debug) {
auto static debug = _class.GetStaticMethod<Signature>(env, "d");
_class.Call(env, debug, tag, message);
} else if (severity == EventSeverity::Info) {
auto static info = _class.GetStaticMethod<Signature>(env, "i");
_class.Call(env, info, tag, message);
} else if (severity == EventSeverity::Warning) {
auto static warning = _class.GetStaticMethod<Signature>(env, "w");
_class.Call(env, warning, tag, message);
} else {
auto static error = _class.GetStaticMethod<Signature>(env, "e");
_class.Call(env, error, tag, message);
}
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,19 @@
#pragma once
#include <mbgl/util/event.hpp>
#include <jni/jni.hpp>
#include "bitmap.hpp"
namespace mbgl {
namespace android {
class Logger {
public:
static constexpr auto Name() { return "org/maplibre/android/log/Logger"; };
static void registerNative(jni::JNIEnv &);
static void log(jni::JNIEnv &, EventSeverity severity, const std::string &msg);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,13 @@
#include <mbgl/util/logging.hpp>
#include "logger.hpp"
#include "attach_env.hpp"
namespace mbgl {
void Log::platformRecord(EventSeverity severity, const std::string &msg) {
auto env{android::AttachEnv()};
android::Logger::log(*env, severity, msg);
}
} // namespace mbgl

View File

@@ -0,0 +1,9 @@
#include "jni.hpp"
#include "jni_native.hpp"
#include <jni/jni.hpp>
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
assert(vm != nullptr);
mbgl::android::registerNatives(vm);
return JNI_VERSION_1_6;
}

View File

@@ -0,0 +1,74 @@
#include "camera_position.hpp"
#include "../geometry/lat_lng.hpp"
namespace mbgl {
namespace android {
jni::Local<jni::Object<CameraPosition>> CameraPosition::New(jni::JNIEnv& env,
mbgl::CameraOptions options,
float pixelRatio) {
static auto& javaClass = jni::Class<CameraPosition>::Singleton(env);
static auto constructor =
javaClass.GetConstructor<jni::Object<LatLng>, double, double, double, jni::Array<jni::jdouble>>(env);
// wrap LatLng values coming from core
auto center = options.center.value();
center.wrap();
// convert bearing, measured in radians counterclockwise from true north.
// Wrapped to [−π rad, π rad). Android binding from 0 to 360 degrees
double bearing_degrees = options.bearing.value_or(0);
while (bearing_degrees > 360) {
bearing_degrees -= 360;
}
while (bearing_degrees < 0) {
bearing_degrees += 360;
}
// convert tilt, core ranges from [0 rad, 1,0472 rad], android ranges from 0 to 60
double tilt_degrees = options.pitch.value_or(0);
std::vector<jdouble> paddingVect;
auto insets = options.padding.value_or(EdgeInsets{0, 0, 0, 0});
auto padding = jni::Array<jni::jdouble>::New(env, 4);
paddingVect.push_back(insets.left() * pixelRatio);
paddingVect.push_back(insets.top() * pixelRatio);
paddingVect.push_back(insets.right() * pixelRatio);
paddingVect.push_back(insets.bottom() * pixelRatio);
padding.SetRegion<std::vector<jni::jdouble>>(env, 0, paddingVect);
return javaClass.New(
env, constructor, LatLng::New(env, center), options.zoom.value_or(0), tilt_degrees, bearing_degrees, padding);
}
mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env,
const jni::Object<CameraPosition>& position,
float pixelRatio) {
static auto& javaClass = jni::Class<CameraPosition>::Singleton(env);
static auto bearing = javaClass.GetField<jni::jdouble>(env, "bearing");
static auto target = javaClass.GetField<jni::Object<LatLng>>(env, "target");
static auto tilt = javaClass.GetField<jni::jdouble>(env, "tilt");
static auto zoom = javaClass.GetField<jni::jdouble>(env, "zoom");
static auto paddingField = javaClass.GetField<jni::Array<jni::jdouble>>(env, "padding");
auto padding = position.Get(env, paddingField);
auto center = LatLng::getLatLng(env, position.Get(env, target));
return mbgl::CameraOptions{center,
padding && padding.Length(env) == 4 ? EdgeInsets{padding.Get(env, 1) * pixelRatio,
padding.Get(env, 0) * pixelRatio,
padding.Get(env, 3) * pixelRatio,
padding.Get(env, 2) * pixelRatio}
: (EdgeInsets){},
{},
position.Get(env, zoom),
position.Get(env, bearing),
position.Get(env, tilt)};
}
void CameraPosition::registerNative(jni::JNIEnv& env) {
jni::Class<CameraPosition>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,23 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/map/camera.hpp>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class CameraPosition : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "org/maplibre/android/camera/CameraPosition"; };
static jni::Local<jni::Object<CameraPosition>> New(jni::JNIEnv&, mbgl::CameraOptions, float pixelRatio);
static mbgl::CameraOptions getCameraOptions(jni::JNIEnv&, const jni::Object<CameraPosition>&, float pixelRatio);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,74 @@
#include <mbgl/style/image.hpp>
#include <mbgl/util/exception.hpp>
#include "image.hpp"
namespace mbgl {
namespace android {
mbgl::style::Image Image::getImage(jni::JNIEnv& env, const jni::Object<Image>& image) {
static auto& javaClass = jni::Class<Image>::Singleton(env);
static auto widthField = javaClass.GetField<jni::jint>(env, "width");
static auto heightField = javaClass.GetField<jni::jint>(env, "height");
static auto pixelRatioField = javaClass.GetField<jni::jfloat>(env, "pixelRatio");
static auto bufferField = javaClass.GetField<jni::Array<jbyte>>(env, "buffer");
static auto nameField = javaClass.GetField<jni::String>(env, "name");
static auto sdfField = javaClass.GetField<jni::jboolean>(env, "sdf");
static auto contentField = javaClass.GetField<jni::Array<jfloat>>(env, "content");
static auto stretchXField = javaClass.GetField<jni::Array<jfloat>>(env, "stretchX");
static auto stretchYField = javaClass.GetField<jni::Array<jfloat>>(env, "stretchY");
auto height = image.Get(env, heightField);
auto width = image.Get(env, widthField);
auto pixelRatio = image.Get(env, pixelRatioField);
auto pixels = image.Get(env, bufferField);
auto name = jni::Make<std::string>(env, image.Get(env, nameField));
auto sdf = (bool)image.Get(env, sdfField);
auto content = image.Get(env, contentField);
auto stretchX = image.Get(env, stretchXField);
auto stretchY = image.Get(env, stretchYField);
jni::NullCheck(env, pixels.get());
std::size_t size = pixels.Length(env);
mbgl::PremultipliedImage premultipliedImage({static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
if (premultipliedImage.bytes() != uint32_t(size)) {
throw mbgl::util::StyleImageException("Image pixel count mismatch");
}
jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));
style::ImageStretches imageStretchesX = {};
style::ImageStretches imageStretchesY = {};
if (stretchX.get()) {
std::size_t sizeX = stretchX.Length(env);
for (jni::jsize i = 0; i < sizeX - 1; i += 2) {
imageStretchesX.push_back({stretchX.Get(env, i), stretchX.Get(env, i + 1)});
}
}
if (stretchY.get()) {
std::size_t sizeY = stretchY.Length(env);
for (jni::jsize i = 0; i < sizeY - 1; i += 2) {
imageStretchesY.push_back({stretchY.Get(env, i), stretchY.Get(env, i + 1)});
}
}
if (content) {
const style::ImageContent imageContent = style::ImageContent{
content.Get(env, 0), content.Get(env, 1), content.Get(env, 2), content.Get(env, 3)};
return mbgl::style::Image{name,
std::move(premultipliedImage),
pixelRatio,
sdf,
imageStretchesX,
imageStretchesY,
std::move(imageContent)};
}
return mbgl::style::Image{name, std::move(premultipliedImage), pixelRatio, sdf, imageStretchesX, imageStretchesY};
}
void Image::registerNative(jni::JNIEnv& env) {
jni::Class<Image>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,21 @@
#pragma once
#include <mbgl/util/noncopyable.hpp>
#include <jni/jni.hpp>
#include <mbgl/style/image.hpp>
namespace mbgl {
namespace android {
class Image : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "org/maplibre/android/maps/Image"; };
static mbgl::style::Image getImage(jni::JNIEnv&, const jni::Object<Image>&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,293 @@
#include "map_renderer.hpp"
#include <mbgl/renderer/renderer.hpp>
#include <mbgl/gfx/backend_scope.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/logging.hpp>
#include <string>
#include "attach_env.hpp"
#include "android_renderer_backend.hpp"
#include "map_renderer_runnable.hpp"
namespace mbgl {
namespace android {
MapRenderer::MapRenderer(jni::JNIEnv& _env,
const jni::Object<MapRenderer>& obj,
jni::jfloat pixelRatio_,
const jni::String& localIdeographFontFamily_)
: javaPeer(_env, obj),
pixelRatio(pixelRatio_),
localIdeographFontFamily(localIdeographFontFamily_ ? jni::Make<std::string>(_env, localIdeographFontFamily_)
: std::optional<std::string>{}),
mailboxData(this) {}
MapRenderer::MailboxData::MailboxData(Scheduler* scheduler_)
: scheduler(scheduler_) {
assert(scheduler);
}
std::shared_ptr<Mailbox> MapRenderer::MailboxData::getMailbox() const noexcept {
if (!mailbox) {
mailbox = std::make_shared<Mailbox>(*scheduler);
}
return mailbox;
}
MapRenderer::~MapRenderer() = default;
void MapRenderer::reset() {
try {
destroyed = true;
if (renderer) {
// Make sure to destroy the renderer on the GL Thread
auto self = ActorRef<MapRenderer>(*this, mailboxData.getMailbox());
self.ask(&MapRenderer::resetRenderer).wait();
}
// Lock to make sure there is no concurrent initialisation on the gl thread
std::lock_guard<std::mutex> lock(initialisationMutex);
rendererObserver.reset();
} catch (const std::exception& exception) {
Log::Error(Event::Android, std::string("MapRenderer::reset failed: ") + exception.what());
}
}
ActorRef<Renderer> MapRenderer::actor() const {
return *rendererRef;
}
void MapRenderer::schedule(std::function<void()>&& scheduled) {
try {
// Create a runnable
android::UniqueEnv _env = android::AttachEnv();
auto runnable = std::make_unique<MapRendererRunnable>(*_env, std::move(scheduled));
// Obtain ownership of the peer (gets transferred to the MapRenderer on the JVM for later GC)
auto peer = runnable->peer();
// Queue the event on the Java Peer
static auto& javaClass = jni::Class<MapRenderer>::Singleton(*_env);
static auto queueEvent = javaClass.GetMethod<void(jni::Object<MapRendererRunnable>)>(*_env, "queueEvent");
auto weakReference = javaPeer.get(*_env);
if (weakReference) {
weakReference.Call(*_env, queueEvent, peer);
}
// Release the c++ peer as it will be destroyed on GC of the Java Peer
runnable.release();
} catch (const std::exception& exception) {
Log::Error(Event::Android, std::string("MapRenderer::schedule failed: ") + exception.what());
}
}
std::size_t MapRenderer::waitForEmpty(Milliseconds timeout) {
try {
android::UniqueEnv _env = android::AttachEnv();
static auto& javaClass = jni::Class<MapRenderer>::Singleton(*_env);
static auto waitForEmpty = javaClass.GetMethod<jni::jint(jni::jlong)>(*_env, "waitForEmpty");
if (auto weakReference = javaPeer.get(*_env)) {
return weakReference.Call(*_env, waitForEmpty, static_cast<int64_t>(timeout.count()));
}
// If the peer is already cleaned up, there's nothing to wait for
return 0;
} catch (...) {
Log::Error(Event::Android, "MapRenderer::waitForEmpty failed");
jni::ThrowJavaError(*android::AttachEnv(), std::current_exception());
return 0;
}
}
void MapRenderer::requestRender() {
try {
android::UniqueEnv _env = android::AttachEnv();
static auto& javaClass = jni::Class<MapRenderer>::Singleton(*_env);
static auto onInvalidate = javaClass.GetMethod<void()>(*_env, "requestRender");
auto weakReference = javaPeer.get(*_env);
if (weakReference) {
weakReference.Call(*_env, onInvalidate);
}
} catch (const std::exception& exception) {
Log::Error(Event::Android, std::string("MapRenderer::requestRender failed: ") + exception.what());
}
}
void MapRenderer::update(std::shared_ptr<UpdateParameters> params) {
try {
// Lock on the parameters
std::lock_guard<std::mutex> lock(updateMutex);
updateParameters = std::move(params);
} catch (const std::exception& exception) {
Log::Error(Event::Android, std::string("MapRenderer::update failed: ") + exception.what());
}
}
void MapRenderer::setObserver(std::shared_ptr<RendererObserver> _rendererObserver) {
try {
// Lock as the initialization can come from the main thread or the GL thread first
std::lock_guard<std::mutex> lock(initialisationMutex);
rendererObserver = std::move(_rendererObserver);
// Set the new observer on the Renderer implementation
if (renderer) {
renderer->setObserver(rendererObserver.get());
}
} catch (const std::exception& exception) {
Log::Error(Event::Android, std::string("MapRenderer::setObserver failed: ") + exception.what());
}
}
void MapRenderer::requestSnapshot(SnapshotCallback callback) {
auto self = ActorRef<MapRenderer>(*this, mailboxData.getMailbox());
self.invoke(
&MapRenderer::scheduleSnapshot,
std::make_unique<SnapshotCallback>(
[&, callback = std::move(callback), runloop = util::RunLoop::Get()](PremultipliedImage image) {
runloop->invoke(
[callback = std::move(callback), image = std::move(image), renderer = std::move(this)]() mutable {
if (renderer && !renderer->destroyed) {
callback(std::move(image));
}
});
snapshotCallback.reset();
}));
}
// Called on OpenGL thread //
void MapRenderer::resetRenderer() {
renderer.reset();
backend.reset();
}
void MapRenderer::scheduleSnapshot(std::unique_ptr<SnapshotCallback> callback) {
snapshotCallback = std::move(callback);
requestRender();
}
void MapRenderer::render(JNIEnv&) {
assert(renderer);
std::shared_ptr<UpdateParameters> params;
{
// Lock on the parameters
std::unique_lock<std::mutex> lock(updateMutex);
if (!updateParameters) return;
// Hold on to the update parameters during render
params = updateParameters;
}
// Activate the backend
gfx::BackendScope backendGuard{*backend};
// Ensure that the "current" scheduler on the render thread is
// this scheduler.
Scheduler::SetCurrent(this);
if (framebufferSizeChanged) {
backend->updateViewPort();
framebufferSizeChanged = false;
}
renderer->render(params);
// Deliver the snapshot if requested
if (snapshotCallback) {
snapshotCallback->operator()(backend->readFramebuffer());
snapshotCallback.reset();
}
}
void MapRenderer::onSurfaceCreated(JNIEnv&) {
// Lock as the initialization can come from the main thread or the GL thread first
std::lock_guard<std::mutex> lock(initialisationMutex);
// The GL context is already active if get a new surface.
gfx::BackendScope backendGuard{*backend, gfx::BackendScope::ScopeType::Implicit};
// The android system will have already destroyed the underlying
// GL resources if this is not the first initialization and an
// attempt to clean them up will fail
if (backend) backend->markContextLost();
if (renderer) renderer->markContextLost();
// Reset in opposite order
renderer.reset();
backend.reset();
// Create the new backend and renderer
backend = std::make_unique<AndroidRendererBackend>();
renderer = std::make_unique<Renderer>(*backend, pixelRatio, localIdeographFontFamily);
rendererRef = std::make_unique<ActorRef<Renderer>>(*renderer, mailboxData.getMailbox());
// Set the observer on the new Renderer implementation
if (rendererObserver) {
renderer->setObserver(rendererObserver.get());
}
}
void MapRenderer::onSurfaceChanged(JNIEnv& env, jint width, jint height) {
if (!renderer) {
// In case the surface has been destroyed (due to app back-grounding)
onSurfaceCreated(env);
}
backend->resizeFramebuffer(width, height);
framebufferSizeChanged = true;
requestRender();
}
void MapRenderer::onRendererReset(JNIEnv&) {
// Make sure to destroy the renderer on the GL Thread
auto self = ActorRef<MapRenderer>(*this, mailboxData.getMailbox());
self.ask(&MapRenderer::resetRenderer).wait();
}
// needs to be called on GL thread
void MapRenderer::onSurfaceDestroyed(JNIEnv&) {
resetRenderer();
}
void MapRenderer::setSwapBehaviorFlush(JNIEnv&, jboolean flush) {
backend->setSwapBehavior(flush ? gfx::Renderable::SwapBehaviour::Flush : gfx::Renderable::SwapBehaviour::NoFlush);
}
// Static methods //
void MapRenderer::registerNative(jni::JNIEnv& env) {
// Lookup the class
static auto& javaClass = jni::Class<MapRenderer>::Singleton(env);
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
// Register the peer
jni::RegisterNativePeer<MapRenderer>(
env,
javaClass,
"nativePtr",
jni::MakePeer<MapRenderer, const jni::Object<MapRenderer>&, jni::jfloat, const jni::String&>,
"nativeInitialize",
"finalize",
METHOD(&MapRenderer::render, "nativeRender"),
METHOD(&MapRenderer::onRendererReset, "nativeReset"),
METHOD(&MapRenderer::onSurfaceCreated, "nativeOnSurfaceCreated"),
METHOD(&MapRenderer::onSurfaceChanged, "nativeOnSurfaceChanged"),
METHOD(&MapRenderer::onSurfaceDestroyed, "nativeOnSurfaceDestroyed"),
METHOD(&MapRenderer::setSwapBehaviorFlush, "nativeSetSwapBehaviorFlush"));
}
MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, const jni::Object<MapRenderer>& jObject) {
static auto& javaClass = jni::Class<MapRenderer>::Singleton(env);
static auto field = javaClass.GetField<jlong>(env, "nativePtr");
MapRenderer* mapRenderer = reinterpret_cast<MapRenderer*>(jObject.Get(env, field));
assert(mapRenderer != nullptr);
return *mapRenderer;
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,143 @@
#pragma once
#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/util/image.hpp>
#include <memory>
#include <mutex>
#include <utility>
#include <optional>
#include <jni/jni.hpp>
namespace mbgl {
template <class>
class ActorRef;
class Mailbox;
class Renderer;
class RendererBackend;
class RendererObserver;
class ThreadPool;
class UpdateParameters;
namespace android {
class AndroidRendererBackend;
/**
* The MapRenderer is a peer class that encapsulates the actions
* performed on the GL Thread.
*
* The public methods are safe to call from the main thread, others are not.
*/
class MapRenderer : public Scheduler {
public:
static constexpr auto Name() { return "org/maplibre/android/maps/renderer/MapRenderer"; };
static void registerNative(jni::JNIEnv&);
static MapRenderer& getNativePeer(JNIEnv&, const jni::Object<MapRenderer>&);
MapRenderer(jni::JNIEnv& _env,
const jni::Object<MapRenderer>&,
jni::jfloat pixelRatio,
const jni::String& localIdeographFontFamily);
~MapRenderer() override;
// Resets the renderer to clean up on the calling thread
void reset();
// Takes the RendererObserver by shared_ptr so we
// don't have to make the header public. Use
// this instead of Renderer#setObserver directly
void setObserver(std::shared_ptr<RendererObserver>);
// Sets the new update parameters to use on subsequent
// renders. Be sure to trigger a render with
// requestRender().
void update(std::shared_ptr<UpdateParameters>);
// Gives a handle to the Renderer to enable actions on
// any thread.
ActorRef<Renderer> actor() const;
// From Scheduler. Schedules by using callbacks to the
// JVM to process the mailbox on the right thread.
void schedule(std::function<void()>&& scheduled) override;
mapbox::base::WeakPtr<Scheduler> makeWeakPtr() override { return weakFactory.makeWeakPtr(); }
// Wait for the queue to be empty
// A timeout of zero results in an unbounded wait
std::size_t waitForEmpty(Milliseconds timeout) override;
void requestRender();
// Snapshot - requires a RunLoop on the calling thread
using SnapshotCallback = std::function<void(PremultipliedImage)>;
void requestSnapshot(SnapshotCallback);
protected:
// Called from the GL Thread //
void scheduleSnapshot(std::unique_ptr<SnapshotCallback>);
private:
struct MailboxData {
explicit MailboxData(Scheduler*);
std::shared_ptr<Mailbox> getMailbox() const noexcept;
private:
Scheduler* scheduler;
mutable std::shared_ptr<Mailbox> mailbox;
};
// Called from the GL Thread //
// Resets the renderer
void resetRenderer();
// Renders a frame.
void render(JNIEnv&);
void onSurfaceCreated(JNIEnv&);
void onSurfaceChanged(JNIEnv&, jint width, jint height);
void onSurfaceDestroyed(JNIEnv&);
private:
// Called on either Main or GL thread //
void onRendererReset(JNIEnv&);
void setSwapBehaviorFlush(JNIEnv&, jboolean flush);
private:
jni::WeakReference<jni::Object<MapRenderer>, jni::EnvAttachingDeleter> javaPeer;
float pixelRatio;
std::optional<std::string> localIdeographFontFamily;
std::shared_ptr<ThreadPool> threadPool;
const MailboxData mailboxData;
std::mutex initialisationMutex;
std::shared_ptr<RendererObserver> rendererObserver;
std::unique_ptr<AndroidRendererBackend> backend;
std::unique_ptr<Renderer> renderer;
std::unique_ptr<ActorRef<Renderer>> rendererRef;
std::shared_ptr<UpdateParameters> updateParameters;
std::mutex updateMutex;
bool framebufferSizeChanged = false;
std::atomic<bool> destroyed{false};
std::unique_ptr<SnapshotCallback> snapshotCallback;
mapbox::base::WeakPtrFactory<Scheduler> weakFactory{this};
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,48 @@
#include "map_renderer_runnable.hpp"
#include <mbgl/util/logging.hpp>
namespace mbgl {
namespace android {
MapRendererRunnable::MapRendererRunnable(jni::JNIEnv& env, std::function<void()> function_)
: function(std::move(function_)) {
// Create the Java peer and hold on to a global reference
// Not using a weak reference here as this might oerflow
// the weak reference table on some devices
jni::UniqueLocalFrame frame = jni::PushLocalFrame(env, 5);
static auto& javaClass = jni::Class<MapRendererRunnable>::Singleton(env);
static auto constructor = javaClass.GetConstructor<jlong>(env);
auto instance = javaClass.New(env, constructor, reinterpret_cast<jlong>(this));
javaPeer = jni::NewGlobal(env, instance);
}
MapRendererRunnable::~MapRendererRunnable() = default;
void MapRendererRunnable::run(jni::JNIEnv&) {
if (function) function();
}
jni::Global<jni::Object<MapRendererRunnable>> MapRendererRunnable::peer() {
return std::move(javaPeer);
}
// Static methods //
void MapRendererRunnable::registerNative(jni::JNIEnv& env) {
// Lookup the class
static auto& javaClass = jni::Class<MapRendererRunnable>::Singleton(env);
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
jni::RegisterNativePeer<MapRendererRunnable>(env,
javaClass,
"nativePtr",
std::make_unique<MapRendererRunnable, JNIEnv&>,
"nativeInitialize",
"finalize",
METHOD(&MapRendererRunnable::run, "run"));
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,44 @@
#pragma once
#include <mbgl/actor/mailbox.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <memory>
#include <utility>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
/**
* The MapRendererRunnable is a peer class that encapsulates
* a scheduled mailbox in a Java Runnable so it can be
* scheduled on the map renderer thread.
*
*/
class MapRendererRunnable {
public:
static constexpr auto Name() { return "org/maplibre/android/maps/renderer/MapRendererRunnable"; };
static void registerNative(jni::JNIEnv&);
MapRendererRunnable(jni::JNIEnv&, std::function<void()>);
// Only for jni registration, unused
MapRendererRunnable(jni::JNIEnv&) { assert(false); }
~MapRendererRunnable();
void run(jni::JNIEnv&);
// Transfers ownership of the Peer object to the caller
jni::Global<jni::Object<MapRendererRunnable>> peer();
private:
jni::Global<jni::Object<MapRendererRunnable>> javaPeer;
std::function<void()> function;
};
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,23 @@
#include "maplibre.hpp"
namespace mbgl {
namespace android {
jni::Local<jni::Object<AssetManager>> MapLibre::getAssetManager(jni::JNIEnv& env) {
static auto& javaClass = jni::Class<MapLibre>::Singleton(env);
auto method = javaClass.GetStaticMethod<jni::Object<AssetManager>()>(env, "getAssetManager");
return javaClass.Call(env, method);
}
jboolean MapLibre::hasInstance(jni::JNIEnv& env) {
static auto& javaClass = jni::Class<MapLibre>::Singleton(env);
auto method = javaClass.GetStaticMethod<jboolean()>(env, "hasInstance");
return javaClass.Call(env, method);
}
void MapLibre::registerNative(jni::JNIEnv& env) {
jni::Class<MapLibre>::Singleton(env);
}
} // namespace android
} // namespace mbgl

View File

@@ -0,0 +1,19 @@
#pragma once
#include "asset_manager.hpp"
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class MapLibre {
public:
static constexpr auto Name() { return "org/maplibre/android/MapLibre"; };
static jboolean hasInstance(jni::JNIEnv&);
static jni::Local<jni::Object<AssetManager>> getAssetManager(jni::JNIEnv&);
static void registerNative(jni::JNIEnv&);
};
} // namespace android
} // namespace mbgl

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,318 @@
#pragma once
#include <mbgl/map/change.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/storage/network_status.hpp>
#include "annotation/marker.hpp"
#include "annotation/polygon.hpp"
#include "annotation/polyline.hpp"
#include "graphics/pointf.hpp"
#include "graphics/rectf.hpp"
#include "geojson/feature.hpp"
#include "geojson/geometry.hpp"
#include "geometry/lat_lng.hpp"
#include "geometry/projected_meters.hpp"
#include "style/layers/layer_manager.hpp"
#include "style/sources/source.hpp"
#include "geometry/lat_lng_bounds.hpp"
#include "map/camera_position.hpp"
#include "map/image.hpp"
#include "style/light.hpp"
#include "bitmap.hpp"
#include <exception>
#include <string>
#include <jni.h>
#include <android/native_window.h>
#include <EGL/egl.h>
#include <jni/jni.hpp>
namespace mbgl {
namespace android {
class AndroidRendererFrontend;
class FileSource;
class MapRenderer;
class NativeMapView : public MapObserver {
public:
static constexpr auto Name() { return "org/maplibre/android/maps/NativeMapView"; };
static void registerNative(jni::JNIEnv&);
NativeMapView(jni::JNIEnv&,
const jni::Object<NativeMapView>&,
const jni::Object<FileSource>&,
const jni::Object<MapRenderer>&,
jni::jfloat,
jni::jboolean);
virtual ~NativeMapView();
// mbgl::RendererBackend (mbgl::MapObserver) //
void onCameraWillChange(MapObserver::CameraChangeMode) override;
void onCameraIsChanging() override;
void onCameraDidChange(MapObserver::CameraChangeMode) override;
void onWillStartLoadingMap() override;
void onDidFinishLoadingMap() override;
void onDidFailLoadingMap(MapLoadError, const std::string&) override;
void onWillStartRenderingFrame() override;
void onDidFinishRenderingFrame(MapObserver::RenderFrameStatus) override;
void onWillStartRenderingMap() override;
void onDidFinishRenderingMap(MapObserver::RenderMode) override;
void onDidBecomeIdle() override;
void onDidFinishLoadingStyle() override;
void onSourceChanged(mbgl::style::Source&) override;
void onStyleImageMissing(const std::string&) override;
bool onCanRemoveUnusedStyleImage(const std::string&) override;
// JNI //
void resizeView(jni::JNIEnv&, int, int);
jni::Local<jni::String> getStyleUrl(jni::JNIEnv&);
void setStyleUrl(jni::JNIEnv&, const jni::String&);
jni::Local<jni::String> getStyleJson(jni::JNIEnv&);
void setStyleJson(jni::JNIEnv&, const jni::String&);
void setLatLngBounds(jni::JNIEnv&, const jni::Object<mbgl::android::LatLngBounds>&);
void cancelTransitions(jni::JNIEnv&);
void setGestureInProgress(jni::JNIEnv&, jni::jboolean);
void moveBy(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jlong);
void jumpTo(jni::JNIEnv&,
jni::jdouble,
jni::jdouble,
jni::jdouble,
jni::jdouble,
jni::jdouble,
const jni::Array<jni::jdouble>&);
void easeTo(jni::JNIEnv&,
jni::jdouble,
jni::jdouble,
jni::jdouble,
jni::jlong,
jni::jdouble,
jni::jdouble,
const jni::Array<jni::jdouble>&,
jni::jboolean);
void flyTo(jni::JNIEnv&,
jni::jdouble,
jni::jdouble,
jni::jdouble,
jni::jlong,
jni::jdouble,
jni::jdouble,
const jni::Array<jni::jdouble>&);
jni::Local<jni::Object<LatLng>> getLatLng(JNIEnv&);
void setLatLng(jni::JNIEnv&, jni::jdouble, jni::jdouble, const jni::Array<jni::jdouble>&, jni::jlong);
jni::Local<jni::Object<CameraPosition>> getCameraForLatLngBounds(jni::JNIEnv&,
const jni::Object<mbgl::android::LatLngBounds>&,
double top,
double left,
double bottom,
double right,
double bearing,
double tilt);
jni::Local<jni::Object<CameraPosition>> getCameraForGeometry(jni::JNIEnv&,
const jni::Object<geojson::Geometry>&,
double top,
double left,
double bottom,
double right,
double bearing,
double tilt);
void setReachability(jni::JNIEnv&, jni::jboolean);
void resetPosition(jni::JNIEnv&);
jni::jdouble getPitch(jni::JNIEnv&);
void setPitch(jni::JNIEnv&, jni::jdouble, jni::jlong);
void setZoom(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong);
jni::jdouble getZoom(jni::JNIEnv&);
void resetZoom(jni::JNIEnv&);
void setMinZoom(jni::JNIEnv&, jni::jdouble);
jni::jdouble getMinZoom(jni::JNIEnv&);
void setMaxZoom(jni::JNIEnv&, jni::jdouble);
jni::jdouble getMaxZoom(jni::JNIEnv&);
void setMinPitch(jni::JNIEnv&, jni::jdouble);
jni::jdouble getMinPitch(jni::JNIEnv&);
void setMaxPitch(jni::JNIEnv&, jni::jdouble);
jni::jdouble getMaxPitch(jni::JNIEnv&);
void rotateBy(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong);
void setBearing(jni::JNIEnv&, jni::jdouble, jni::jlong);
void setBearingXY(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong);
jni::jdouble getBearing(jni::JNIEnv&);
void resetNorth(jni::JNIEnv&);
void setVisibleCoordinateBounds(
JNIEnv&, const jni::Array<jni::Object<LatLng>>&, const jni::Object<RectF>&, jni::jdouble, jni::jlong);
void getVisibleCoordinateBounds(JNIEnv& env, jni::Array<jdouble>& output);
void scheduleSnapshot(jni::JNIEnv&);
jni::Local<jni::Object<CameraPosition>> getCameraPosition(jni::JNIEnv&);
void updateMarker(jni::JNIEnv&, jni::jlong, jni::jdouble, jni::jdouble, const jni::String&);
jni::Local<jni::Array<jni::jlong>> addMarkers(jni::JNIEnv&, const jni::Array<jni::Object<Marker>>&);
void onLowMemory(JNIEnv& env);
void setDebug(JNIEnv&, jni::jboolean);
jni::jboolean getDebug(JNIEnv&);
jni::jboolean isFullyLoaded(JNIEnv&);
jni::jdouble getMetersPerPixelAtLatitude(JNIEnv&, jni::jdouble, jni::jdouble);
jni::Local<jni::Object<ProjectedMeters>> projectedMetersForLatLng(JNIEnv&, jni::jdouble, jni::jdouble);
jni::Local<jni::Object<PointF>> pixelForLatLng(JNIEnv&, jdouble, jdouble);
void pixelsForLatLngs(JNIEnv&, const jni::Array<jdouble>&, jni::Array<jdouble>&, jfloat);
jni::Local<jni::Object<LatLng>> latLngForProjectedMeters(JNIEnv&, jdouble, jdouble);
jni::Local<jni::Object<LatLng>> latLngForPixel(JNIEnv&, jfloat, jfloat);
void latLngsForPixels(JNIEnv&, const jni::Array<jdouble>&, jni::Array<jdouble>&, jfloat);
jni::Local<jni::Array<jlong>> addPolylines(JNIEnv&, const jni::Array<jni::Object<Polyline>>&);
jni::Local<jni::Array<jlong>> addPolygons(JNIEnv&, const jni::Array<jni::Object<Polygon>>&);
void updatePolyline(JNIEnv&, jlong, const jni::Object<Polyline>&);
void updatePolygon(JNIEnv&, jlong, const jni::Object<Polygon>&);
void removeAnnotations(JNIEnv&, const jni::Array<jlong>&);
void addAnnotationIcon(JNIEnv&, const jni::String&, jint, jint, jfloat, const jni::Array<jbyte>&);
void removeAnnotationIcon(JNIEnv&, const jni::String&);
jni::jdouble getTopOffsetPixelsForAnnotationSymbol(JNIEnv&, const jni::String&);
jni::Local<jni::Object<TransitionOptions>> getTransitionOptions(JNIEnv&);
void setTransitionOptions(JNIEnv&, const jni::Object<TransitionOptions>&);
jni::Local<jni::Array<jlong>> queryPointAnnotations(JNIEnv&, const jni::Object<RectF>&);
jni::Local<jni::Array<jlong>> queryShapeAnnotations(JNIEnv&, const jni::Object<RectF>&);
jni::Local<jni::Array<jni::Object<geojson::Feature>>> queryRenderedFeaturesForPoint(
JNIEnv&, jni::jfloat, jni::jfloat, const jni::Array<jni::String>&, const jni::Array<jni::Object<>>& jfilter);
jni::Local<jni::Array<jni::Object<geojson::Feature>>> queryRenderedFeaturesForBox(
JNIEnv&,
jni::jfloat,
jni::jfloat,
jni::jfloat,
jni::jfloat,
const jni::Array<jni::String>&,
const jni::Array<jni::Object<>>& jfilter);
jni::Local<jni::Object<Light>> getLight(JNIEnv&);
jni::Local<jni::Array<jni::Object<Layer>>> getLayers(JNIEnv&);
jni::Local<jni::Object<Layer>> getLayer(JNIEnv&, const jni::String&);
void addLayer(JNIEnv&, jlong, const jni::String&);
void addLayerAbove(JNIEnv&, jlong, const jni::String&);
void addLayerAt(JNIEnv&, jni::jlong, jni::jint);
jni::jboolean removeLayerAt(JNIEnv&, jni::jint);
jni::jboolean removeLayer(JNIEnv&, jlong);
jni::Local<jni::Array<jni::Object<Source>>> getSources(JNIEnv&);
jni::Local<jni::Object<Source>> getSource(JNIEnv&, const jni::String&);
void addSource(JNIEnv&, const jni::Object<Source>&, jlong nativePtr);
jni::jboolean removeSource(JNIEnv&, const jni::Object<Source>&, jlong nativePtr);
void addImage(JNIEnv&, const jni::String&, const jni::Object<Bitmap>& bitmap, jni::jfloat, jni::jboolean);
void addImages(JNIEnv&, const jni::Array<jni::Object<mbgl::android::Image>>&);
void removeImage(JNIEnv&, const jni::String&);
jni::Local<jni::Object<Bitmap>> getImage(JNIEnv&, const jni::String&);
void setPrefetchTiles(JNIEnv&, jni::jboolean);
jni::jboolean getPrefetchTiles(JNIEnv&);
void setPrefetchZoomDelta(JNIEnv&, jni::jint);
jni::jint getPrefetchZoomDelta(JNIEnv&);
mbgl::Map& getMap();
void triggerRepaint(JNIEnv&);
private:
std::unique_ptr<AndroidRendererFrontend> rendererFrontend;
JavaVM* vm = nullptr;
jni::WeakReference<jni::Object<NativeMapView>> javaPeer;
MapRenderer& mapRenderer;
std::string styleUrl;
float pixelRatio;
// Minimum texture size according to OpenGL ES 2.0 specification.
int width = 64;
int height = 64;
// Ensure these are initialised last
std::unique_ptr<mbgl::Map> map;
};
} // namespace android
} // namespace mbgl

Some files were not shown because too many files have changed in this diff Show More