From daecbed823b3e02044d7e91f689a0ea3e01bf25a Mon Sep 17 00:00:00 2001 From: Igor Tarasov Date: Thu, 2 Apr 2020 19:45:56 +0300 Subject: [PATCH] Move JNI bindings code from mapbox-gl-native to this repo. (#301) --- MapboxGLAndroidSDK/src/cpp/CMakeLists.txt | 206 +++ .../src/cpp/android_renderer_backend.cpp | 67 + .../src/cpp/android_renderer_backend.hpp | 43 + .../src/cpp/android_renderer_frontend.cpp | 143 ++ .../src/cpp/android_renderer_frontend.hpp | 68 + .../src/cpp/annotation/marker.cpp | 23 + .../src/cpp/annotation/marker.hpp | 28 + .../src/cpp/annotation/multi_point.hpp | 34 + .../src/cpp/annotation/polygon.cpp | 40 + .../src/cpp/annotation/polygon.hpp | 21 + .../src/cpp/annotation/polyline.cpp | 31 + .../src/cpp/annotation/polyline.hpp | 21 + MapboxGLAndroidSDK/src/cpp/asset_manager.hpp | 14 + .../src/cpp/asset_manager_file_source.cpp | 65 + .../src/cpp/asset_manager_file_source.hpp | 30 + .../src/cpp/connectivity_listener.cpp | 41 + .../src/cpp/connectivity_listener.hpp | 29 + .../src/cpp/conversion/collection.cpp | 32 + .../src/cpp/conversion/collection.hpp | 18 + .../src/cpp/conversion/color.cpp | 17 + .../src/cpp/conversion/color.hpp | 18 + .../src/cpp/conversion/constant.cpp | 62 + .../src/cpp/conversion/constant.hpp | 110 ++ .../src/cpp/conversion/conversion.hpp | 50 + MapboxGLAndroidSDK/src/cpp/file_source.cpp | 252 ++++ MapboxGLAndroidSDK/src/cpp/file_source.hpp | 83 ++ .../src/cpp/geojson/feature.cpp | 88 ++ .../src/cpp/geojson/feature.hpp | 24 + .../src/cpp/geojson/feature_collection.cpp | 37 + .../src/cpp/geojson/feature_collection.hpp | 26 + .../src/cpp/geojson/geometry.cpp | 97 ++ .../src/cpp/geojson/geometry.hpp | 28 + .../src/cpp/geojson/geometry_collection.cpp | 47 + .../src/cpp/geojson/geometry_collection.hpp | 26 + .../src/cpp/geojson/line_string.cpp | 55 + .../src/cpp/geojson/line_string.hpp | 36 + .../src/cpp/geojson/multi_line_string.cpp | 56 + .../src/cpp/geojson/multi_line_string.hpp | 34 + .../src/cpp/geojson/multi_point.cpp | 41 + .../src/cpp/geojson/multi_point.hpp | 33 + .../src/cpp/geojson/multi_polygon.cpp | 53 + .../src/cpp/geojson/multi_polygon.hpp | 32 + MapboxGLAndroidSDK/src/cpp/geojson/point.cpp | 34 + MapboxGLAndroidSDK/src/cpp/geojson/point.hpp | 27 + .../src/cpp/geojson/polygon.cpp | 55 + .../src/cpp/geojson/polygon.hpp | 35 + MapboxGLAndroidSDK/src/cpp/geojson/util.hpp | 52 + .../src/cpp/geometry/lat_lng.cpp | 29 + .../src/cpp/geometry/lat_lng.hpp | 29 + .../src/cpp/geometry/lat_lng_bounds.cpp | 30 + .../src/cpp/geometry/lat_lng_bounds.hpp | 26 + .../src/cpp/geometry/lat_lng_quad.cpp | 37 + .../src/cpp/geometry/lat_lng_quad.hpp | 27 + .../src/cpp/geometry/projected_meters.cpp | 17 + .../src/cpp/geometry/projected_meters.hpp | 21 + .../src/cpp/graphics/pointf.cpp | 25 + .../src/cpp/graphics/pointf.hpp | 24 + MapboxGLAndroidSDK/src/cpp/graphics/rectf.cpp | 35 + MapboxGLAndroidSDK/src/cpp/graphics/rectf.hpp | 28 + .../src/cpp/gson/json_array.cpp | 50 + .../src/cpp/gson/json_array.hpp | 24 + .../src/cpp/gson/json_element.cpp | 88 ++ .../src/cpp/gson/json_element.hpp | 24 + .../src/cpp/gson/json_object.cpp | 65 + .../src/cpp/gson/json_object.hpp | 24 + .../src/cpp/gson/json_primitive.cpp | 13 + .../src/cpp/gson/json_primitive.hpp | 21 + .../src/cpp/http_file_source.cpp | 195 +++ MapboxGLAndroidSDK/src/cpp/i18n/collator.cpp | 192 +++ .../src/cpp/i18n/collator_jni.hpp | 62 + .../src/cpp/i18n/number_format.cpp | 80 ++ .../src/cpp/i18n/number_format_jni.hpp | 29 + MapboxGLAndroidSDK/src/cpp/java/util.cpp | 19 + MapboxGLAndroidSDK/src/cpp/java/util.hpp | 81 ++ MapboxGLAndroidSDK/src/cpp/java_types.cpp | 49 + MapboxGLAndroidSDK/src/cpp/java_types.hpp | 43 + MapboxGLAndroidSDK/src/cpp/jni_native.cpp | 168 +++ MapboxGLAndroidSDK/src/cpp/jni_native.hpp | 13 + MapboxGLAndroidSDK/src/cpp/logger.cpp | 34 + MapboxGLAndroidSDK/src/cpp/logger.hpp | 21 + .../src/cpp/logging_android.cpp | 13 + MapboxGLAndroidSDK/src/cpp/main.cpp | 9 + .../src/cpp/map/camera_position.cpp | 69 + .../src/cpp/map/camera_position.hpp | 23 + MapboxGLAndroidSDK/src/cpp/map/image.cpp | 41 + MapboxGLAndroidSDK/src/cpp/map/image.hpp | 21 + MapboxGLAndroidSDK/src/cpp/map_renderer.cpp | 247 ++++ MapboxGLAndroidSDK/src/cpp/map_renderer.hpp | 138 ++ .../src/cpp/map_renderer_runnable.cpp | 49 + .../src/cpp/map_renderer_runnable.hpp | 47 + MapboxGLAndroidSDK/src/cpp/mapbox.cpp | 23 + MapboxGLAndroidSDK/src/cpp/mapbox.hpp | 19 + .../src/cpp/native_map_view.cpp | 1263 +++++++++++++++++ .../src/cpp/native_map_view.hpp | 279 ++++ .../src/cpp/offline/offline_manager.cpp | 335 +++++ .../src/cpp/offline/offline_manager.hpp | 111 ++ .../src/cpp/offline/offline_region.cpp | 314 ++++ .../src/cpp/offline/offline_region.hpp | 92 ++ .../cpp/offline/offline_region_definition.cpp | 107 ++ .../cpp/offline/offline_region_definition.hpp | 43 + .../src/cpp/offline/offline_region_error.cpp | 44 + .../src/cpp/offline/offline_region_error.hpp | 19 + .../src/cpp/offline/offline_region_status.cpp | 38 + .../src/cpp/offline/offline_region_status.hpp | 19 + .../src/cpp/snapshotter/map_snapshot.cpp | 65 + .../src/cpp/snapshotter/map_snapshot.hpp | 48 + .../src/cpp/snapshotter/map_snapshotter.cpp | 353 +++++ .../src/cpp/snapshotter/map_snapshotter.hpp | 87 ++ .../src/cpp/style/android_conversion.hpp | 148 ++ .../src/cpp/style/conversion/filter.cpp | 26 + .../src/cpp/style/conversion/filter.hpp | 16 + .../src/cpp/style/conversion/position.cpp | 24 + .../src/cpp/style/conversion/position.hpp | 25 + .../style/conversion/property_expression.hpp | 23 + .../cpp/style/conversion/property_value.hpp | 62 + .../style/conversion/transition_options.cpp | 17 + .../style/conversion/transition_options.hpp | 21 + .../cpp/style/conversion/url_or_tileset.cpp | 30 + .../cpp/style/conversion/url_or_tileset.hpp | 16 + .../src/cpp/style/formatted.cpp | 54 + .../src/cpp/style/formatted.hpp | 23 + .../src/cpp/style/formatted_section.cpp | 11 + .../src/cpp/style/formatted_section.hpp | 18 + .../src/cpp/style/layers/background_layer.cpp | 148 ++ .../src/cpp/style/layers/background_layer.hpp | 58 + .../src/cpp/style/layers/circle_layer.cpp | 270 ++++ .../src/cpp/style/layers/circle_layer.hpp | 84 ++ .../src/cpp/style/layers/custom_layer.cpp | 61 + .../src/cpp/style/layers/custom_layer.hpp | 41 + .../cpp/style/layers/fill_extrusion_layer.cpp | 225 +++ .../cpp/style/layers/fill_extrusion_layer.hpp | 74 + .../src/cpp/style/layers/fill_layer.cpp | 207 +++ .../src/cpp/style/layers/fill_layer.hpp | 72 + .../src/cpp/style/layers/heatmap_layer.cpp | 163 +++ .../src/cpp/style/layers/heatmap_layer.hpp | 62 + .../src/cpp/style/layers/hillshade_layer.cpp | 181 +++ .../src/cpp/style/layers/hillshade_layer.hpp | 66 + .../src/cpp/style/layers/layer.cpp | 178 +++ .../src/cpp/style/layers/layer.cpp.ejs | 139 ++ .../src/cpp/style/layers/layer.hpp | 117 ++ .../src/cpp/style/layers/layer.hpp.ejs | 62 + .../src/cpp/style/layers/layer_manager.cpp | 173 +++ .../src/cpp/style/layers/layer_manager.hpp | 57 + .../src/cpp/style/layers/line_layer.cpp | 315 ++++ .../src/cpp/style/layers/line_layer.hpp | 96 ++ .../src/cpp/style/layers/raster_layer.cpp | 222 +++ .../src/cpp/style/layers/raster_layer.hpp | 74 + .../src/cpp/style/layers/symbol_layer.cpp | 594 ++++++++ .../src/cpp/style/layers/symbol_layer.hpp | 180 +++ MapboxGLAndroidSDK/src/cpp/style/light.cpp | 143 ++ MapboxGLAndroidSDK/src/cpp/style/light.hpp | 53 + MapboxGLAndroidSDK/src/cpp/style/position.cpp | 35 + MapboxGLAndroidSDK/src/cpp/style/position.hpp | 24 + .../style/sources/custom_geometry_source.cpp | 209 +++ .../style/sources/custom_geometry_source.hpp | 50 + .../src/cpp/style/sources/geojson_source.cpp | 259 ++++ .../src/cpp/style/sources/geojson_source.hpp | 79 ++ .../src/cpp/style/sources/image_source.cpp | 76 + .../src/cpp/style/sources/image_source.hpp | 39 + .../cpp/style/sources/raster_dem_source.cpp | 61 + .../cpp/style/sources/raster_dem_source.hpp | 29 + .../src/cpp/style/sources/raster_source.cpp | 58 + .../src/cpp/style/sources/raster_source.hpp | 29 + .../src/cpp/style/sources/source.cpp | 230 +++ .../src/cpp/style/sources/source.hpp | 72 + .../src/cpp/style/sources/unknown_source.cpp | 40 + .../src/cpp/style/sources/unknown_source.hpp | 27 + .../src/cpp/style/sources/vector_source.cpp | 81 ++ .../src/cpp/style/sources/vector_source.hpp | 33 + .../src/cpp/style/transition_options.cpp | 35 + .../src/cpp/style/transition_options.hpp | 26 + MapboxGLAndroidSDK/src/cpp/style/value.cpp | 83 ++ MapboxGLAndroidSDK/src/cpp/style/value.hpp | 43 + .../src/cpp/text/local_glyph_rasterizer.cpp | 131 ++ .../cpp/text/local_glyph_rasterizer_jni.hpp | 33 + .../mapboxsdk/style/sources/Source.java | 8 + gradle/native-build.gradle | 2 +- vendor/mapbox-gl-native | 2 +- 178 files changed, 14422 insertions(+), 2 deletions(-) create mode 100644 MapboxGLAndroidSDK/src/cpp/CMakeLists.txt create mode 100755 MapboxGLAndroidSDK/src/cpp/android_renderer_backend.cpp create mode 100755 MapboxGLAndroidSDK/src/cpp/android_renderer_backend.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/android_renderer_frontend.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/android_renderer_frontend.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/annotation/marker.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/annotation/marker.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/annotation/multi_point.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/annotation/polygon.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/annotation/polygon.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/annotation/polyline.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/annotation/polyline.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/asset_manager.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/asset_manager_file_source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/asset_manager_file_source.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/connectivity_listener.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/connectivity_listener.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/conversion/collection.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/conversion/collection.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/conversion/color.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/conversion/color.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/conversion/constant.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/conversion/constant.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/conversion/conversion.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/file_source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/file_source.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/feature.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/feature.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/feature_collection.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/feature_collection.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/geometry.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/geometry.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/geometry_collection.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/geometry_collection.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/line_string.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/line_string.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/multi_line_string.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/multi_line_string.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/multi_point.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/multi_point.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/multi_polygon.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/multi_polygon.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/point.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/point.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/polygon.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/polygon.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geojson/util.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geometry/lat_lng.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geometry/lat_lng.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_bounds.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_bounds.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_quad.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_quad.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geometry/projected_meters.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/geometry/projected_meters.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/graphics/pointf.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/graphics/pointf.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/graphics/rectf.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/graphics/rectf.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/gson/json_array.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/gson/json_array.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/gson/json_element.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/gson/json_element.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/gson/json_object.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/gson/json_object.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/gson/json_primitive.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/gson/json_primitive.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/http_file_source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/i18n/collator.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/i18n/collator_jni.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/i18n/number_format.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/i18n/number_format_jni.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/java/util.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/java/util.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/java_types.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/java_types.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/jni_native.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/jni_native.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/logger.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/logger.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/logging_android.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/main.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/map/camera_position.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/map/camera_position.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/map/image.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/map/image.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/map_renderer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/map_renderer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/map_renderer_runnable.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/map_renderer_runnable.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/mapbox.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/mapbox.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/native_map_view.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/native_map_view.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/offline/offline_manager.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/offline/offline_manager.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/offline/offline_region.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/offline/offline_region.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/offline/offline_region_definition.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/offline/offline_region_definition.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/offline/offline_region_error.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/offline/offline_region_error.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/offline/offline_region_status.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/offline/offline_region_status.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshot.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshot.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshotter.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshotter.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/android_conversion.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/conversion/filter.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/conversion/filter.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/conversion/position.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/conversion/position.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/conversion/property_expression.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/conversion/property_value.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/conversion/transition_options.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/conversion/transition_options.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/conversion/url_or_tileset.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/conversion/url_or_tileset.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/formatted.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/formatted.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/formatted_section.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/formatted_section.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/background_layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/background_layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/circle_layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/circle_layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/custom_layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/custom_layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/fill_extrusion_layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/fill_extrusion_layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/fill_layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/fill_layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/heatmap_layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/heatmap_layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/hillshade_layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/hillshade_layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/layer.cpp.ejs create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/layer.hpp.ejs create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/layer_manager.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/layer_manager.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/line_layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/line_layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/raster_layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/raster_layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/symbol_layer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/layers/symbol_layer.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/light.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/light.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/position.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/position.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/custom_geometry_source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/custom_geometry_source.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/geojson_source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/geojson_source.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/image_source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/image_source.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/raster_dem_source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/raster_dem_source.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/raster_source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/raster_source.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/source.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/unknown_source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/unknown_source.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/vector_source.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/sources/vector_source.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/transition_options.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/transition_options.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/value.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/style/value.hpp create mode 100644 MapboxGLAndroidSDK/src/cpp/text/local_glyph_rasterizer.cpp create mode 100644 MapboxGLAndroidSDK/src/cpp/text/local_glyph_rasterizer_jni.hpp diff --git a/MapboxGLAndroidSDK/src/cpp/CMakeLists.txt b/MapboxGLAndroidSDK/src/cpp/CMakeLists.txt new file mode 100644 index 000000000..d031fafcd --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/CMakeLists.txt @@ -0,0 +1,206 @@ +cmake_minimum_required(VERSION 3.10) + +add_subdirectory(../../../vendor/mapbox-gl-native ../../../vendor/mapbox-gl-native) + +add_library( + mapbox-gl 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}/mapbox.cpp + ${PROJECT_SOURCE_DIR}/mapbox.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/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 +) + + +target_include_directories( + mapbox-gl + PRIVATE + ${PROJECT_SOURCE_DIR} + ../../../vendor/mapbox-gl-native/src + ../../../vendor/mapbox-gl-native/platform/android/src + ../../../vendor/mapbox-gl-native/platform/default/include + +) + +target_link_libraries( + mapbox-gl + PRIVATE + mbgl-compiler-options + Mapbox::Base::jni.hpp + mbgl-core + mbgl-vendor-unique_resource + +) + +install(TARGETS mapbox-gl LIBRARY DESTINATION lib) diff --git a/MapboxGLAndroidSDK/src/cpp/android_renderer_backend.cpp b/MapboxGLAndroidSDK/src/cpp/android_renderer_backend.cpp new file mode 100755 index 000000000..e2d064838 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/android_renderer_backend.cpp @@ -0,0 +1,67 @@ +#include "android_renderer_backend.hpp" + +#include +#include +#include + +#include + +#include + +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()); + } + +private: + AndroidRendererBackend& backend; +}; + +AndroidRendererBackend::AndroidRendererBackend() + : gl::RendererBackend(gfx::ContextMode::Unique), + mbgl::gfx::Renderable({ 64, 64 }, std::make_unique(*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(width), static_cast(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().setCleanupOnDestruction(false); + } +} + +} // namespace android +} // namspace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/android_renderer_backend.hpp b/MapboxGLAndroidSDK/src/cpp/android_renderer_backend.hpp new file mode 100755 index 000000000..f06f38e68 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/android_renderer_backend.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +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(); + + // 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 diff --git a/MapboxGLAndroidSDK/src/cpp/android_renderer_frontend.cpp b/MapboxGLAndroidSDK/src/cpp/android_renderer_frontend.cpp new file mode 100644 index 000000000..d1a2624f0 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/android_renderer_frontend.cpp @@ -0,0 +1,143 @@ +#include "android_renderer_frontend.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#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(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) override { + delegate.invoke(&RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded, placementChanged); + } + + void onDidFinishRenderingMap() override { + delegate.invoke(&RendererObserver::onDidFinishRenderingMap); + } + + void onStyleImageMissing(const std::string& id, StyleImageMissingCallback done) override { + delegate.invoke(&RendererObserver::onStyleImageMissing, id, done); + } + + void onRemoveUnusedStyleImages(const std::vector& ids) override { + delegate.invoke(&RendererObserver::onRemoveUnusedStyleImages, ids); + } + +private: + std::shared_ptr mailbox; + ActorRef delegate; +}; + +AndroidRendererFrontend::AndroidRendererFrontend(MapRenderer& mapRenderer_) + : mapRenderer(mapRenderer_) + , mapRunLoop(util::RunLoop::Get()) + , updateAsyncTask(std::make_unique([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(*mapRunLoop, observer)); +} + +void AndroidRendererFrontend::update(std::shared_ptr params) { + updateParams = std::move(params); + updateAsyncTask->send(); +} + +void AndroidRendererFrontend::reduceMemoryUse() { + mapRenderer.actor().invoke(&Renderer::reduceMemoryUse); +} + +std::vector 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 AndroidRendererFrontend::queryRenderedFeatures(const ScreenBox& box, + const RenderedQueryOptions& options) const { + + // Select the right overloaded method + std::vector (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 AndroidRendererFrontend::queryRenderedFeatures(const ScreenCoordinate& point, + const RenderedQueryOptions& options) const { + + // Select the right overloaded method + std::vector (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 optional>& args) const { + return mapRenderer.actor().ask(&Renderer::queryFeatureExtensions, sourceID, feature, extension, extensionField, args).get(); +} + +} // namespace android +} // namespace mbgl + diff --git a/MapboxGLAndroidSDK/src/cpp/android_renderer_frontend.hpp b/MapboxGLAndroidSDK/src/cpp/android_renderer_frontend.hpp new file mode 100644 index 000000000..dc5f11e57 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/android_renderer_frontend.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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) override; + + // Feature querying + std::vector queryRenderedFeatures(const ScreenCoordinate&, const RenderedQueryOptions&) const; + std::vector queryRenderedFeatures(const ScreenBox&, const RenderedQueryOptions&) const; + std::vector 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 optional>& args) const; + + // Memory + void reduceMemoryUse(); + +private: + MapRenderer& mapRenderer; + util::RunLoop* mapRunLoop; + std::unique_ptr updateAsyncTask; + std::shared_ptr updateParams; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/annotation/marker.cpp b/MapboxGLAndroidSDK/src/cpp/annotation/marker.cpp new file mode 100644 index 000000000..899f87a74 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/annotation/marker.cpp @@ -0,0 +1,23 @@ +#include "marker.hpp" + +namespace mbgl { +namespace android { + +mbgl::Point Marker::getPosition(jni::JNIEnv& env, const jni::Object& marker) { + static auto& javaClass = jni::Class::Singleton(env); + static auto positionField = javaClass.GetField>(env, "position"); + return LatLng::getGeometry(env, marker.Get(env, positionField)); +} + +std::string Marker::getIconId(jni::JNIEnv& env, const jni::Object& marker) { + static auto& javaClass = jni::Class::Singleton(env); + static auto iconIdField = javaClass.GetField(env, "iconId"); + return jni::Make(env, marker.Get(env, iconIdField)); +} + +void Marker::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/annotation/marker.hpp b/MapboxGLAndroidSDK/src/cpp/annotation/marker.hpp new file mode 100644 index 000000000..b38a25b4f --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/annotation/marker.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include + +#include "../geometry/lat_lng.hpp" + +namespace mbgl { +namespace android { + +class Marker : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/annotations/Marker"; }; + + static mbgl::Point getPosition(jni::JNIEnv&, const jni::Object&); + + static std::string getIconId(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); + +}; + + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/annotation/multi_point.hpp b/MapboxGLAndroidSDK/src/cpp/annotation/multi_point.hpp new file mode 100644 index 000000000..20f1b3eaf --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/annotation/multi_point.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#include "../geometry/lat_lng.hpp" +#include "../java/util.hpp" + +namespace mbgl { +namespace android { + +class MultiPoint : protected mbgl::util::noncopyable { + +protected: + + template + static Geometry toGeometry(JNIEnv& env, const jni::Object& pointsList) { + auto jarray = java::util::List::toArray(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 \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/annotation/polygon.cpp b/MapboxGLAndroidSDK/src/cpp/annotation/polygon.cpp new file mode 100644 index 000000000..f22cffb10 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/annotation/polygon.cpp @@ -0,0 +1,40 @@ +#include "polygon.hpp" + +#include "../conversion/color.hpp" + +namespace mbgl { +namespace android { + +mbgl::FillAnnotation Polygon::toAnnotation(jni::JNIEnv& env, const jni::Object& polygon) { + static auto& javaClass = jni::Class::Singleton(env); + static auto points = javaClass.GetField>(env, "points"); + static auto holes = javaClass.GetField>(env, "holes"); + static auto alpha = javaClass.GetField(env, "alpha"); + static auto fillColor = javaClass.GetField(env, "fillColor"); + static auto strokeColor = javaClass.GetField(env, "strokeColor"); + + mbgl::Polygon geometry { + MultiPoint::toGeometry>(env, polygon.Get(env, points)) + }; + + auto jHoleListsArray = java::util::List::toArray(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>(env, jHoleListsArray.Get(env, i))); + } + + mbgl::FillAnnotation annotation { geometry }; + annotation.opacity = polygon.Get(env, alpha); + annotation.color = *conversion::convert(env, polygon.Get(env, fillColor)); + annotation.outlineColor = *conversion::convert(env, polygon.Get(env, strokeColor)); + + return annotation; +} + +void Polygon::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/annotation/polygon.hpp b/MapboxGLAndroidSDK/src/cpp/annotation/polygon.hpp new file mode 100644 index 000000000..bc21878ef --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/annotation/polygon.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "multi_point.hpp" + +namespace mbgl { +namespace android { + +class Polygon : private MultiPoint { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/annotations/Polygon"; }; + + static mbgl::FillAnnotation toAnnotation(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/annotation/polyline.cpp b/MapboxGLAndroidSDK/src/cpp/annotation/polyline.cpp new file mode 100644 index 000000000..d6161e856 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/annotation/polyline.cpp @@ -0,0 +1,31 @@ +#include "polyline.hpp" + +#include "../conversion/color.hpp" + +namespace mbgl { +namespace android { + +mbgl::LineAnnotation Polyline::toAnnotation(jni::JNIEnv& env, const jni::Object& polyline) { + static auto& javaClass = jni::Class::Singleton(env); + static auto points = javaClass.GetField>(env, "points"); + static auto alpha = javaClass.GetField(env, "alpha"); + static auto color = javaClass.GetField(env, "color"); + static auto width = javaClass.GetField(env, "width"); + + mbgl::LineAnnotation annotation { + MultiPoint::toGeometry>(env, polyline.Get(env, points)) + }; + + annotation.opacity = polyline.Get(env, alpha); + annotation.color = *conversion::convert(env, polyline.Get(env, color)); + annotation.width = polyline.Get(env, width); + + return annotation; +} + +void Polyline::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/annotation/polyline.hpp b/MapboxGLAndroidSDK/src/cpp/annotation/polyline.hpp new file mode 100644 index 000000000..6178e98cf --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/annotation/polyline.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "multi_point.hpp" + +namespace mbgl { +namespace android { + +class Polyline : private MultiPoint { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/annotations/Polyline"; }; + + static mbgl::LineAnnotation toAnnotation(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/asset_manager.hpp b/MapboxGLAndroidSDK/src/cpp/asset_manager.hpp new file mode 100644 index 000000000..b87d18951 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/asset_manager.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace mbgl { +namespace android { + +class AssetManager { +public: + static constexpr auto Name() { + return "android/content/res/AssetManager"; + }; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/asset_manager_file_source.cpp b/MapboxGLAndroidSDK/src/cpp/asset_manager_file_source.cpp new file mode 100644 index 000000000..a8045a2b9 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/asset_manager_file_source.cpp @@ -0,0 +1,65 @@ +#include "asset_manager_file_source.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mbgl { + +class AssetManagerFileSource::Impl { +public: + Impl(ActorRef, AAssetManager* assetManager_) : assetManager(assetManager_) { + } + + void request(const std::string& url, ActorRef 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( + reinterpret_cast(AAsset_getBuffer(asset)), AAsset_getLength64(asset)); + AAsset_close(asset); + } else { + response.error = std::make_unique(Response::Error::Reason::NotFound, + "Could not read asset"); + } + + req.invoke(&FileSourceRequest::setResponse, response); + } + +private: + AAssetManager* assetManager; +}; + +AssetManagerFileSource::AssetManagerFileSource(jni::JNIEnv& env, + const jni::Object& assetManager_) + : assetManager(jni::NewGlobal(env, assetManager_)), + impl(std::make_unique>( + util::makeThreadPrioritySetter(platform::EXPERIMENTAL_THREAD_PRIORITY_FILE), + "AssetManagerFileSource", + AAssetManager_fromJava(&env, jni::Unwrap(assetManager.get())))) {} + +AssetManagerFileSource::~AssetManagerFileSource() = default; + +std::unique_ptr AssetManagerFileSource::request(const Resource& resource, Callback callback) { + auto req = std::make_unique(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); +} + +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/asset_manager_file_source.hpp b/MapboxGLAndroidSDK/src/cpp/asset_manager_file_source.hpp new file mode 100644 index 000000000..bcbc6f1e9 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/asset_manager_file_source.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "asset_manager.hpp" + +#include + +namespace mbgl { + +namespace util { +template class Thread; +} // namespace util + +class AssetManagerFileSource : public FileSource { +public: + AssetManagerFileSource(jni::JNIEnv&, const jni::Object&); + ~AssetManagerFileSource() override; + + std::unique_ptr request(const Resource&, Callback) override; + bool canRequest(const Resource&) const override; + +private: + class Impl; + + jni::Global> assetManager; + std::unique_ptr> impl; +}; + +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/connectivity_listener.cpp b/MapboxGLAndroidSDK/src/cpp/connectivity_listener.cpp new file mode 100644 index 000000000..9c13f1f74 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/connectivity_listener.cpp @@ -0,0 +1,41 @@ +#include "connectivity_listener.hpp" + +#include +#include + +#include + +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::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + std::make_unique, + "initialize", + "finalize", + METHOD(&ConnectivityListener::onConnectivityStateChanged, "nativeOnConnectivityStateChanged") + ); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/connectivity_listener.hpp b/MapboxGLAndroidSDK/src/cpp/connectivity_listener.hpp new file mode 100644 index 000000000..a09507c0f --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/connectivity_listener.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace android { + +class ConnectivityListener : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/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 diff --git a/MapboxGLAndroidSDK/src/cpp/conversion/collection.cpp b/MapboxGLAndroidSDK/src/cpp/conversion/collection.cpp new file mode 100644 index 000000000..954a161ba --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/conversion/collection.cpp @@ -0,0 +1,32 @@ +#include "collection.hpp" +#include "constant.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +std::vector toVector(JNIEnv& env, const jni::Array& array) { + std::size_t len = array.Length(env); + std::vector vector; + vector.reserve(len); + + for (std::size_t i = 0; i < len; i++) { + vector.push_back(jni::Make(env, array.Get(env, i))); + } + + return vector; +} + +jni::Local> toArray(JNIEnv& env, const std::vector& vector) { + auto result = jni::Array::New(env, vector.size()); + + for (std::size_t i = 0; i < vector.size(); i++) { + result.Set(env, i, jni::Make(env, vector.at(i))); + } + + return result; +} + +} +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/conversion/collection.hpp b/MapboxGLAndroidSDK/src/cpp/conversion/collection.hpp new file mode 100644 index 000000000..322ebb62e --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/conversion/collection.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "conversion.hpp" + +#include + +#include + +namespace mbgl { +namespace android { +namespace conversion { + +std::vector toVector(JNIEnv& env, const jni::Array& array); +jni::Local> toArray(JNIEnv& env, const std::vector&); + +} +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/conversion/color.cpp b/MapboxGLAndroidSDK/src/cpp/conversion/color.cpp new file mode 100644 index 000000000..ce85943e6 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/conversion/color.cpp @@ -0,0 +1,17 @@ +#include "color.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +Result Converter::operator()(jni::JNIEnv&, const int& color) const { + float r = (color >> 16) & 0xFF; + float g = (color >> 8) & 0xFF; + float b = (color) & 0xFF; + float a = (color >> 24) & 0xFF; + return { mbgl::Color( r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f ) }; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/conversion/color.hpp b/MapboxGLAndroidSDK/src/cpp/conversion/color.hpp new file mode 100644 index 000000000..2b4144b93 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/conversion/color.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "conversion.hpp" + +#include + +namespace mbgl { +namespace android { +namespace conversion { + +template <> +struct Converter { + Result operator()(jni::JNIEnv&, const int& color) const; +}; + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/conversion/constant.cpp b/MapboxGLAndroidSDK/src/cpp/conversion/constant.cpp new file mode 100644 index 000000000..46a91026d --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/conversion/constant.cpp @@ -0,0 +1,62 @@ +#include "constant.hpp" +#include "collection.hpp" +#include "../style/formatted.hpp" + +#include + +namespace mbgl { +namespace android { +namespace conversion { + +Result>> Converter>, bool>::operator()(jni::JNIEnv& env, const bool& value) const { + return jni::Box(env, value ? jni::jni_true : jni::jni_false); +} + +Result>> Converter>, float>::operator()(jni::JNIEnv& env, const float& value) const { + return jni::Box(env, value); +} + +Result>> Converter>, double>::operator()(jni::JNIEnv& env, const double& value) const { + return jni::Box(env, value); +} + +Result>> Converter>, std::string>::operator()(jni::JNIEnv& env, const std::string& value) const { + return jni::Make(env, value); +} + +Result>> Converter>, Color>::operator()(jni::JNIEnv& env, const Color& value) const { + return jni::Make(env, value.stringify()); +} + +Result>> Converter>, style::expression::Formatted>::operator()(jni::JNIEnv& env, const style::expression::Formatted& value) const { + return Formatted::New(env, value); +} + +Result>> Converter>, std::vector>::operator()(jni::JNIEnv& env, const std::vector& value) const { + auto result = jni::Array::New(env, value.size()); + + for (std::size_t i = 0; i < value.size(); i++) { + result.Set(env, i, jni::Make(env, value.at(i))); + } + + return result; +} + +Result>> Converter>, std::vector>::operator()(jni::JNIEnv& env, const std::vector& value) const { + auto result = jni::Array::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>> Converter>, style::expression::Image>::operator()( + jni::JNIEnv& env, const style::expression::Image& value) const { + return jni::Make(env, value.id()); +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/conversion/constant.hpp b/MapboxGLAndroidSDK/src/cpp/conversion/constant.hpp new file mode 100644 index 000000000..a9239fe84 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/conversion/constant.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include "conversion.hpp" + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +namespace mbgl { +namespace android { +namespace conversion { + +template <> +struct Converter>, bool> { + Result>> operator()(jni::JNIEnv& env, const bool& value) const; +}; + +template <> +struct Converter>, float> { + Result>> operator()(jni::JNIEnv& env, const float& value) const; +}; + +template <> +struct Converter>, double> { + Result>> 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 +struct Converter>, T, typename std::enable_if::value>::type> { + Result>> 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>, std::string> { + Result>> operator()(jni::JNIEnv& env, const std::string& value) const; +}; + +template <> +struct Converter>, Color> { + Result>> operator()(jni::JNIEnv& env, const Color& value) const; +}; + +template <> +struct Converter>, style::expression::Formatted> { + Result>> operator()(jni::JNIEnv& env, const style::expression::Formatted& value) const; +}; + +template +struct Converter>, std::array> { + Result>> operator()(jni::JNIEnv& env, const std::array& value) const { + std::vector v; + for (const float& id : value) { + v.push_back(id); + } + return convert>, std::vector>(env, v); + } +}; + +template <> +struct Converter>, std::vector> { + Result>> operator()(jni::JNIEnv& env, const std::vector& value) const; +}; + +template <> +struct Converter>, std::vector> { + Result>> operator()(jni::JNIEnv& env, const std::vector& value) const; +}; + +template +struct Converter>, T, typename std::enable_if_t::value>> { + Result>> operator()(jni::JNIEnv& env, const T& value) const { + return convert>, std::string>(env, Enum::toString(value)); + } +}; + +template +struct Converter>, std::vector, typename std::enable_if_t::value>> { + Result>> operator()(jni::JNIEnv& env, const std::vector& value) const { + auto result = jni::Array::New(env, value.size()); + for (std::size_t i = 0; i < value.size(); ++i) { + result.Set(env, i, jni::Make(env, Enum::toString(value.at(i)))); + } + return result; + } +}; + +template <> +struct Converter>, style::expression::Image> { + Result>> operator()(jni::JNIEnv& env, const style::expression::Image& value) const; +}; + +} // namespace conversion +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/conversion/conversion.hpp b/MapboxGLAndroidSDK/src/cpp/conversion/conversion.hpp new file mode 100644 index 000000000..d1766f975 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/conversion/conversion.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include + +#include + +namespace mbgl { +namespace android { +namespace conversion { + +struct Error { std::string message; }; + +template +class Result : private variant { +public: + using variant::variant; + + explicit operator bool() const { + return this->template is(); + } + + T& operator*() { + assert(this->template is()); + return this->template get(); + } + + const T& operator*() const { + assert(this->template is()); + return this->template get(); + } + + const Error& error() const { + assert(this->template is()); + return this->template get(); + } +}; + +template +struct Converter; + +template +Result convert(jni::JNIEnv& env, const V& value, Args&&...args) { + return Converter()(env, value, std::forward(args)...); +} + +} // namespace conversion +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/file_source.cpp b/MapboxGLAndroidSDK/src/cpp/file_source.cpp new file mode 100644 index 000000000..f95066546 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/file_source.cpp @@ -0,0 +1,252 @@ +#include "file_source.hpp" + +#include "attach_env.hpp" +#include "mapbox.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +#include "asset_manager_file_source.hpp" + +namespace mbgl { +namespace android { + +// FileSource // + +FileSource::FileSource(jni::JNIEnv& _env, const jni::String& accessToken, const jni::String& _cachePath) { + std::string path = jni::Make(_env, _cachePath); + mapbox::sqlite::setTempPath(path); + + mbgl::FileSourceManager::get()->registerFileSourceFactory( + mbgl::FileSourceType::Asset, [](const mbgl::ResourceOptions&) { + auto env{android::AttachEnv()}; + std::unique_ptr assetFileSource; + if (android::Mapbox::hasInstance(*env)) { + auto assetManager = android::Mapbox::getAssetManager(*env); + assetFileSource = std::make_unique(*env, assetManager); + } + return assetFileSource; + }); + + resourceOptions.withAccessToken(accessToken ? jni::Make(_env, accessToken) : "") + .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); + databaseSource = std::static_pointer_cast(std::shared_ptr( + mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Database, resourceOptions))); + onlineSource = mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Network, resourceOptions); +} + +FileSource::~FileSource() { +} + +jni::Local FileSource::getAccessToken(jni::JNIEnv& env) { + if (auto* token = onlineSource->getProperty(mbgl::ACCESS_TOKEN_KEY).getString()) { + return jni::Make(env, *token); + } + + ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Online functionality is disabled."); + return jni::Make(env, ""); +} + +void FileSource::setAccessToken(jni::JNIEnv& env, const jni::String& token) { + if (onlineSource) { + onlineSource->setProperty(mbgl::ACCESS_TOKEN_KEY, token ? jni::Make(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(env, url)); + } else { + ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Online functionality is disabled."); + } +} + +void FileSource::setResourceTransform(jni::JNIEnv& env, const jni::Object& 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(env, transformCallback); + resourceTransform = std::make_unique>( + *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(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& _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(env, "Another resources cache path change is in progress")); + return; + } + + std::string newPath = jni::Make(env, path); + mapbox::sqlite::setTempPath(newPath); + + auto global = jni::NewGlobal(env, _callback); + pathChangeCallback = Scheduler::GetCurrent()->bindOnce( + [this, callback = std::make_shared(std::move(global)), newPath] { + android::UniqueEnv _env = android::AttachEnv(); + FileSource::ResourcesCachePathChangeCallback::onSuccess( + *_env, *callback, jni::Make(*_env, newPath)); + pathChangeCallback = {}; + }); + + databaseSource->setDatabasePath(newPath + DATABASE_FILE, pathChangeCallback); +} + +void FileSource::resume(jni::JNIEnv&) { + if (!resourceLoader) { + return; + } + + if (!activationCounter) { + activationCounter = optional(1) ; + return; + } + + activationCounter.value()++; + if (activationCounter == 1) { + resourceLoader->resume(); + } +} + +void FileSource::pause(jni::JNIEnv&) { + if (!resourceLoader) { + return; + } + + if (activationCounter) { + activationCounter.value()--; + if (activationCounter == 0) { + resourceLoader->pause(); + } + } +} + +jni::jboolean FileSource::isResumed(jni::JNIEnv&) { + if (activationCounter) { + return (jboolean) (activationCounter > 0); + } + return (jboolean) false; +} + +FileSource* FileSource::getNativePeer(jni::JNIEnv& env, const jni::Object& jFileSource) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "nativePtr"); + return reinterpret_cast(jFileSource.Get(env, field)); +} + +mbgl::ResourceOptions FileSource::getSharedResourceOptions(jni::JNIEnv& env, + const jni::Object& jFileSource) { + FileSource* fileSource = FileSource::getNativePeer(env, jFileSource); + // Core could be compiled without support for any sources. + if (fileSource) { + return fileSource->resourceOptions.clone(); + } + + return {}; +} + +// FileSource::ResourcesCachePathChangeCallback // + +void FileSource::ResourcesCachePathChangeCallback::onSuccess(jni::JNIEnv& env, + const jni::Object& callback, + const jni::String& path) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onSuccess"); + + callback.Call(env, method, path); +} + +void FileSource::ResourcesCachePathChangeCallback::onError(jni::JNIEnv& env, + const jni::Object& callback, + const jni::String& message) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(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::Singleton(env); + jni::Class::Singleton(env); + + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer(env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize", + METHOD(&FileSource::getAccessToken, "getAccessToken"), + METHOD(&FileSource::setAccessToken, "setAccessToken"), + METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"), + 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& callback, int kind, std::string url_) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onURL"); + + return jni::Make(env, + callback.Call(env, method, kind, + jni::Make(env, url_))); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/file_source.hpp b/MapboxGLAndroidSDK/src/cpp/file_source.hpp new file mode 100644 index 000000000..3bf0fed2e --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/file_source.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include +#include + +#include "asset_manager.hpp" + +#include + +namespace mbgl { + +template 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 "com/mapbox/mapboxsdk/storage/FileSource"; }; + + struct ResourceTransformCallback { + static constexpr auto Name() { return "com/mapbox/mapboxsdk/storage/FileSource$ResourceTransformCallback"; } + + static std::string onURL(jni::JNIEnv&, const jni::Object&, int, std::string); + }; + + struct ResourcesCachePathChangeCallback { + static constexpr auto Name() { return "com/mapbox/mapboxsdk/storage/FileSource$ResourcesCachePathChangeCallback";} + + static void onSuccess(jni::JNIEnv&, + const jni::Object&, + const jni::String&); + + static void onError(jni::JNIEnv&, + const jni::Object&, + const jni::String&); + }; + + FileSource(jni::JNIEnv&, const jni::String&, const jni::String&); + + ~FileSource(); + + jni::Local getAccessToken(jni::JNIEnv&); + + void setAccessToken(jni::JNIEnv&, const jni::String&); + + void setAPIBaseUrl(jni::JNIEnv&, const jni::String&); + + void setResourceTransform(jni::JNIEnv&, const jni::Object&); + + void setResourceCachePath(jni::JNIEnv&, const jni::String&, const jni::Object&); + + void resume(jni::JNIEnv&); + + void pause(jni::JNIEnv&); + + jni::jboolean isResumed(jni::JNIEnv&); + + static FileSource* getNativePeer(jni::JNIEnv&, const jni::Object&); + + static mbgl::ResourceOptions getSharedResourceOptions(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); + +private: + const std::string DATABASE_FILE = "/mbgl-offline.db"; + optional activationCounter; + mbgl::ResourceOptions resourceOptions; + std::unique_ptr> resourceTransform; + std::function pathChangeCallback; + std::shared_ptr databaseSource; + std::shared_ptr onlineSource; + std::shared_ptr resourceLoader; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/feature.cpp b/MapboxGLAndroidSDK/src/cpp/geojson/feature.cpp new file mode 100644 index 000000000..afbf1ee11 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/feature.cpp @@ -0,0 +1,88 @@ +#include "feature.hpp" +#include "geometry.hpp" +#include "../gson/json_object.hpp" + +#include + +namespace mbgl { +namespace android { +namespace geojson { + +using namespace gson; + +mbgl::GeoJSONFeature Feature::convert(jni::JNIEnv& env, const jni::Object& jFeature) { + static auto& javaClass = jni::Class::Singleton(env); + static auto id = javaClass.GetMethod(env, "id"); + static auto geometry = javaClass.GetMethod ()>(env, "geometry"); + static auto properties = javaClass.GetMethod ()>(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(env, jId)} : mbid{mapbox::feature::null_value}}; +} + +/** + * Turn feature identifier into std::string + */ +class FeatureIdVisitor { +public: + template + 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> convertFeature(jni::JNIEnv& env, const mbgl::GeoJSONFeature& value) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jni::Object, jni::Object, jni::String)>(env, "fromGeometry"); + + return javaClass.Call(env, method, + Geometry::New(env, value.geometry), + JsonObject::New(env, value.properties), + jni::Make(env, value.id.is() ? std::string {} : value.id.match(FeatureIdVisitor()))); +} + +jni::Local>> Feature::convert(jni::JNIEnv& env, const std::vector& value) { + auto features = jni::Array>::New(env, value.size()); + + for (size_t i = 0; i < value.size(); ++i) { + features.Set(env, i, convertFeature(env, static_cast(value.at(i)))); + } + + return features; +} + +jni::Local>> Feature::convert(jni::JNIEnv& env, + const std::vector& value) { + auto features = jni::Array>::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::Singleton(env); +} + +} // namespace geojson +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/feature.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/feature.hpp new file mode 100644 index 000000000..aee45262e --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/feature.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include + +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&); + static jni::Local>> convert(jni::JNIEnv&, const std::vector&); + static jni::Local>> convert(jni::JNIEnv&, const std::vector&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/feature_collection.cpp b/MapboxGLAndroidSDK/src/cpp/geojson/feature_collection.cpp new file mode 100644 index 000000000..f90d5226b --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/feature_collection.cpp @@ -0,0 +1,37 @@ +#include "feature_collection.hpp" + +#include "feature.hpp" + +namespace mbgl { +namespace android { +namespace geojson { + +mbgl::FeatureCollection FeatureCollection::convert(jni::JNIEnv& env, const jni::Object& jCollection) { + auto collection = mbgl::FeatureCollection(); + + if (jCollection) { + auto jFeatureList = FeatureCollection::features(env, jCollection); + auto jFeatures = java::util::List::toArray(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> FeatureCollection::features(jni::JNIEnv& env, const jni::Object& jCollection) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod ()>(env, "features"); + return jCollection.Call(env, method); +} + +void FeatureCollection::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/feature_collection.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/feature_collection.hpp new file mode 100644 index 000000000..66e92eaca --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/feature_collection.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "../java/util.hpp" + +#include + +#include + +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&); + + static jni::Local> features(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/geometry.cpp b/MapboxGLAndroidSDK/src/cpp/geojson/geometry.cpp new file mode 100644 index 000000000..0598b9fc5 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/geometry.cpp @@ -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 + +namespace mbgl { +namespace android { +namespace geojson { + +/** + * Turn mapbox::geometry type into Java GeoJson Geometries + */ +class GeometryEvaluator { +public: + + jni::JNIEnv& env; + + jni::Local> operator()(const mbgl::EmptyGeometry &) const { + // FIXME: mapbox-java needs to have its own Empty type. + return GeometryCollection::New(env, {}); + } + + jni::Local> operator()(const mbgl::Point &geometry) const { + return Point::New(env, geometry); + } + + jni::Local> operator()(const mbgl::LineString &geometry) const { + return LineString::New(env, geometry); + } + + jni::Local> operator()(const mbgl::MultiLineString &geometry) const { + return MultiLineString::New(env, geometry); + } + + jni::Local> operator()(const mbgl::MultiPoint &geometry) const { + return MultiPoint::New(env, geometry); + } + + jni::Local> operator()(const mbgl::Polygon &geometry) const { + return Polygon::New(env, geometry); + } + + jni::Local> operator()(const mbgl::MultiPolygon &geometry) const { + return MultiPolygon::New(env, geometry); + } + + jni::Local> operator()(const mapbox::geometry::geometry_collection &geometry) const { + return GeometryCollection::New(env, geometry); + } +}; + +jni::Local> Geometry::New(jni::JNIEnv& env, mbgl::Geometry geometry) { + GeometryEvaluator evaluator { env } ; + return mbgl::Geometry::visit(geometry, evaluator); +} + +mbgl::Geometry Geometry::convert(jni::JNIEnv &env, const jni::Object& jGeometry) { + auto type = Geometry::getType(env, jGeometry); + if (type == Point::Type()) { + return { Point::convert(env, jni::Cast(env, jni::Class::Singleton(env), jGeometry)) }; + } else if (type == MultiPoint::Type()) { + return { MultiPoint::convert(env, jni::Cast(env, jni::Class::Singleton(env), jGeometry)) }; + } else if (type == LineString::Type()) { + return { LineString::convert(env, jni::Cast(env, jni::Class::Singleton(env), jGeometry)) }; + } else if (type == MultiLineString::Type()) { + return { MultiLineString::convert(env, jni::Cast(env, jni::Class::Singleton(env), jGeometry)) }; + } else if (type == Polygon::Type()) { + return { Polygon::convert(env, jni::Cast(env, jni::Class::Singleton(env), jGeometry)) }; + } else if (type == MultiPolygon::Type()) { + return { MultiPolygon::convert(env, jni::Cast(env, jni::Class::Singleton(env), jGeometry)) }; + } else if (type == GeometryCollection::Type()) { + return { GeometryCollection::convert(env, jni::Cast(env, jni::Class::Singleton(env), jGeometry)) }; + } + + throw std::runtime_error(std::string {"Unsupported GeoJSON type: " } + type); +} + +std::string Geometry::getType(jni::JNIEnv &env, const jni::Object& jGeometry) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "type"); + return jni::Make(env, jGeometry.Call(env, method)); +} + +void Geometry::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace geojson +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/geometry.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/geometry.hpp new file mode 100644 index 000000000..4bf55c858 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/geometry.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "../java/util.hpp" + +#include + +namespace mbgl { +namespace android { +namespace geojson { + +class Geometry { +public: + static constexpr auto Name() { return "com/mapbox/geojson/Geometry"; }; + + static jni::Local> New(jni::JNIEnv&, mbgl::Geometry); + + static mbgl::Geometry convert(jni::JNIEnv&, const jni::Object&); + + static std::string getType(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/geometry_collection.cpp b/MapboxGLAndroidSDK/src/cpp/geojson/geometry_collection.cpp new file mode 100644 index 000000000..cca909126 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/geometry_collection.cpp @@ -0,0 +1,47 @@ +#include "geometry_collection.hpp" +#include "../java/util.hpp" + +namespace mbgl { +namespace android { +namespace geojson { + +jni::Local> GeometryCollection::New(jni::JNIEnv& env, const mapbox::geometry::geometry_collection& collection) { + // Create an array of geometries + auto jarray = jni::Array>::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::Singleton(env); + static auto method = javaClass.GetStaticMethod (jni::Object)>(env, "fromGeometries"); + return javaClass.Call(env, method, java::util::Arrays::asList(env, jarray)); +} + +mapbox::geometry::geometry_collection GeometryCollection::convert(jni::JNIEnv &env, const jni::Object& jCollection) { + // Get geometries + static auto& javaClass = jni::Class::Singleton(env); + static auto getGeometries = javaClass.GetMethod ()>(env, "geometries"); + + // Turn into array + auto jarray = java::util::List::toArray(env, jCollection.Call(env, getGeometries)); + + // Convert each geometry + mapbox::geometry::geometry_collection 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::Singleton(env); +} + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/geometry_collection.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/geometry_collection.hpp new file mode 100644 index 000000000..9f02ac848 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/geometry_collection.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "geometry.hpp" + +#include + +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> New(jni::JNIEnv&, const mapbox::geometry::geometry_collection&); + + static mapbox::geometry::geometry_collection convert(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/line_string.cpp b/MapboxGLAndroidSDK/src/cpp/geojson/line_string.cpp new file mode 100644 index 000000000..ae1345c95 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/line_string.cpp @@ -0,0 +1,55 @@ +#include "line_string.hpp" +#include "point.hpp" +#include "util.hpp" +#include "../java/util.hpp" + +namespace mbgl { +namespace android { +namespace geojson { + +jni::Local> LineString::New(jni::JNIEnv& env, const mbgl::LineString& lineString) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod(jni::Object)>(env, "fromLngLats"); + + return javaClass.Call(env, method, asPointsList(env, lineString)); +} + +mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, const jni::Object& 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*/>& jPointList) { + mapbox::geojson::line_string lineString; + + if (jPointList) { + auto jPointArray = java::util::List::toArray(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> LineString::coordinates(jni::JNIEnv &env, const jni::Object& jLineString) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod ()>(env, "coordinates"); + return jLineString.Call(env, method); +} + +void LineString::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/line_string.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/line_string.hpp new file mode 100644 index 000000000..906d809fa --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/line_string.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#include "geometry.hpp" +#include "../java/util.hpp" + +#include + +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> New(jni::JNIEnv&, const mbgl::LineString&); + + static mapbox::geojson::line_string convert(jni::JNIEnv&, const jni::Object&); + + static mapbox::geojson::line_string convert(jni::JNIEnv&, const jni::Object*/>&); + + static jni::Local> coordinates(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/multi_line_string.cpp b/MapboxGLAndroidSDK/src/cpp/geojson/multi_line_string.cpp new file mode 100644 index 000000000..1aaf53c01 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/multi_line_string.cpp @@ -0,0 +1,56 @@ +#include "multi_line_string.hpp" + +#include "line_string.hpp" +#include "util.hpp" + +namespace mbgl { +namespace android { +namespace geojson { + +jni::Local> MultiLineString::New(jni::JNIEnv& env, const mbgl::MultiLineString& multiLineString) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jni::Object)>(env, "fromLngLats"); + + return javaClass.Call(env, method, asPointsListsList(env, multiLineString)); +} + +mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, const jni::Object& 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>*/>& jPointListsList) { + mapbox::geojson::multi_line_string multiLineString; + + if (jPointListsList) { + auto jPositionListsArray = java::util::List::toArray(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> MultiLineString::coordinates(jni::JNIEnv &env, const jni::Object& jLineString) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod ()>(env, "coordinates"); + return jLineString.Call(env, method); +} + +void MultiLineString::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/multi_line_string.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/multi_line_string.hpp new file mode 100644 index 000000000..65ce43bd1 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/multi_line_string.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#include "../java/util.hpp" +#include "geometry.hpp" + +#include + +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> New(jni::JNIEnv&, const mbgl::MultiLineString&); + + static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, const jni::Object&); + + static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, const jni::Object>*/>&); + + static jni::Local> coordinates(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/multi_point.cpp b/MapboxGLAndroidSDK/src/cpp/geojson/multi_point.cpp new file mode 100644 index 000000000..d530ef721 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/multi_point.cpp @@ -0,0 +1,41 @@ +#include "multi_point.hpp" + +#include "line_string.hpp" + +#include "util.hpp" + +namespace mbgl { +namespace android { +namespace geojson { + +jni::Local> MultiPoint::New(JNIEnv& env, const mbgl::MultiPoint& multiPoint) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod(jni::Object)>(env, "fromLngLats"); + + return javaClass.Call(env, method, asPointsList(env, multiPoint)); +} + +mapbox::geojson::multi_point MultiPoint::convert(jni::JNIEnv& env, const jni::Object& jMultiPoint) { + mapbox::geojson::multi_point multiPoint; + + if (jMultiPoint) { + multiPoint = convertExplicit( + LineString::convert(env, MultiPoint::coordinates(env, jMultiPoint))); + } + + return multiPoint; +} + +jni::Local> MultiPoint::coordinates(jni::JNIEnv& env, const jni::Object& jMultiPoint) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod ()>(env, "coordinates"); + return jMultiPoint.Call(env, method); +} + +void MultiPoint::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/multi_point.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/multi_point.hpp new file mode 100644 index 000000000..a3ca49b73 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/multi_point.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include "geometry.hpp" +#include "../java/util.hpp" + +#include + +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> New(jni::JNIEnv&, const mbgl::MultiPoint&); + + static mapbox::geojson::multi_point convert(jni::JNIEnv&, const jni::Object&); + + static jni::Local> coordinates(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/multi_polygon.cpp b/MapboxGLAndroidSDK/src/cpp/geojson/multi_polygon.cpp new file mode 100644 index 000000000..3c5c3ec33 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/multi_polygon.cpp @@ -0,0 +1,53 @@ +#include "multi_polygon.hpp" + +#include "polygon.hpp" +#include "util.hpp" + +namespace mbgl { +namespace android { +namespace geojson { + +jni::Local> MultiPolygon::New(JNIEnv& env, const mbgl::MultiPolygon& multiPolygon) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jni::Object)>(env, "fromLngLats"); + + auto jarray = jni::Array>::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& jMultiPolygon) { + mapbox::geojson::multi_polygon multiPolygon; + + if (jMultiPolygon) { + auto jPointListsListList = MultiPolygon::coordinates(env, jMultiPolygon); + auto jPointListsListArray = java::util::List::toArray(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> MultiPolygon::coordinates(jni::JNIEnv& env, const jni::Object& jPolygon) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod ()>(env, "coordinates"); + return jPolygon.Call(env, method); +} + +void MultiPolygon::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/multi_polygon.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/multi_polygon.hpp new file mode 100644 index 000000000..fd7b08016 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/multi_polygon.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include "../java/util.hpp" +#include "geometry.hpp" + +#include + +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> New(jni::JNIEnv&, const mbgl::MultiPolygon&); + + static mapbox::geojson::multi_polygon convert(jni::JNIEnv&, const jni::Object&); + + static jni::Local> coordinates(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/point.cpp b/MapboxGLAndroidSDK/src/cpp/geojson/point.cpp new file mode 100644 index 000000000..2000447c0 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/point.cpp @@ -0,0 +1,34 @@ +#include "point.hpp" + +namespace mbgl { +namespace android { +namespace geojson { + +jni::Local> Point::New(jni::JNIEnv& env, const mbgl::Point& point) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jni::jdouble, jni::jdouble)>(env, "fromLngLat"); + return javaClass.Call(env, method, point.x, point.y); +} + +mbgl::Point Point::convert(jni::JNIEnv &env, const jni::Object& jPoint) { + static auto& javaClass = jni::Class::Singleton(env); + static auto longitude = javaClass.GetMethod(env, "longitude"); + static auto latitude = javaClass.GetMethod(env, "latitude"); + + if (!jPoint) { + return {}; + } + + return { + jPoint.Call(env, longitude), + jPoint.Call(env, latitude) + }; +} + +void Point::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace geojson +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/point.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/point.hpp new file mode 100644 index 000000000..b5d71ecae --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/point.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "geometry.hpp" + +#include + +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> New(jni::JNIEnv&, const mbgl::Point&); + static mbgl::Point convert(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/polygon.cpp b/MapboxGLAndroidSDK/src/cpp/geojson/polygon.cpp new file mode 100644 index 000000000..4437d134a --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/polygon.cpp @@ -0,0 +1,55 @@ +#include "polygon.hpp" + +#include "multi_line_string.hpp" + +#include "util.hpp" + +namespace mbgl { +namespace android { +namespace geojson { + +jni::Local> Polygon::New(jni::JNIEnv& env, const mbgl::Polygon& polygon) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jni::Object)>(env, "fromLngLats"); + + return javaClass.Call(env, method, asPointsListsList(env, polygon)); +} + +mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, const jni::Object& 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>*/>& 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(std::move(line))); + } + } + + return polygon; +} + + +jni::Local> Polygon::coordinates(jni::JNIEnv &env, const jni::Object& jPolygon) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod ()>(env, "coordinates"); + return jPolygon.Call(env, method); +} + +void Polygon::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/polygon.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/polygon.hpp new file mode 100644 index 000000000..8e1761fed --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/polygon.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include "geometry.hpp" +#include "../java/util.hpp" + +#include + + +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> New(jni::JNIEnv&, const mbgl::Polygon&); + + static mapbox::geojson::polygon convert(jni::JNIEnv &, const jni::Object&); + + static mapbox::geojson::polygon convert(jni::JNIEnv&, const jni::Object>*/>&); + + static jni::Local> coordinates(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv &); +}; + +} // namespace geojson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geojson/util.hpp b/MapboxGLAndroidSDK/src/cpp/geojson/util.hpp new file mode 100644 index 000000000..59154516f --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geojson/util.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "point.hpp" + +#include + +namespace mbgl { +namespace android { +namespace geojson { + +// Clang 3.8 fails to implicitly convert matching types, so we'll have to do it explicitly. +template +To convertExplicit(From&& src) { + static_assert(std::is_same::container_type, + typename To::container_type>::value, + "container types do not match"); + static_assert(std::is_rvalue_reference::value, + "argument must be rvalue reference"); + return *reinterpret_cast>(&src); +} + +/** + * Geometry -> List + */ +template +static jni::Local> asPointsList(jni::JNIEnv& env, const T& pointsList) { + auto jarray = jni::Array>::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> + */ +template +static jni::Local> asPointsListsList(JNIEnv& env, const SHAPE& value) { + auto jarray = jni::Array>::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 diff --git a/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng.cpp b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng.cpp new file mode 100644 index 000000000..5d8313cb2 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng.cpp @@ -0,0 +1,29 @@ +#include "lat_lng.hpp" + +namespace mbgl { +namespace android { + +jni::Local> LatLng::New(jni::JNIEnv& env, const mbgl::LatLng& latLng) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, latLng.latitude(), latLng.longitude()); +} + +mbgl::Point LatLng::getGeometry(jni::JNIEnv& env, const jni::Object& latLng) { + static auto& javaClass = jni::Class::Singleton(env); + static auto latitudeField = javaClass.GetField(env, "latitude"); + static auto longitudeField = javaClass.GetField(env, "longitude"); + return mbgl::Point(latLng.Get(env, longitudeField), latLng.Get(env, latitudeField)); +} + +mbgl::LatLng LatLng::getLatLng(jni::JNIEnv& env, const jni::Object& latLng) { + auto point = LatLng::getGeometry(env, latLng); + return mbgl::LatLng(point.y, point.x); +} + +void LatLng::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng.hpp b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng.hpp new file mode 100644 index 000000000..30b7db1ef --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +#include + +namespace mbgl { +namespace android { + +class LatLng : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/LatLng"; }; + + static jni::Local> New(jni::JNIEnv&, const mbgl::LatLng&); + + static mbgl::Point getGeometry(jni::JNIEnv&, const jni::Object&); + + static mbgl::LatLng getLatLng(jni::JNIEnv&, const jni::Object&); + + + static void registerNative(jni::JNIEnv&); +}; + + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_bounds.cpp b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_bounds.cpp new file mode 100644 index 000000000..cf534a06e --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_bounds.cpp @@ -0,0 +1,30 @@ +#include "lat_lng_bounds.hpp" + +namespace mbgl { +namespace android { + +jni::Local> LatLngBounds::New(jni::JNIEnv& env, mbgl::LatLngBounds bounds) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, bounds.north(), bounds.east(), bounds.south(), bounds.west()); +} + +mbgl::LatLngBounds LatLngBounds::getLatLngBounds(jni::JNIEnv& env, const jni::Object& bounds) { + static auto& javaClass = jni::Class::Singleton(env); + static auto swLatField = javaClass.GetField(env, "latitudeSouth"); + static auto swLonField = javaClass.GetField(env, "longitudeWest"); + static auto neLatField = javaClass.GetField(env, "latitudeNorth"); + static auto neLonField = javaClass.GetField(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::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_bounds.hpp b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_bounds.hpp new file mode 100644 index 000000000..d51026711 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_bounds.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include + +namespace mbgl { +namespace android { + +class LatLngBounds : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/LatLngBounds"; }; + + static jni::Local> New(jni::JNIEnv&, mbgl::LatLngBounds); + + static mbgl::LatLngBounds getLatLngBounds(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_quad.cpp b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_quad.cpp new file mode 100644 index 000000000..1a1e49048 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_quad.cpp @@ -0,0 +1,37 @@ +#include "lat_lng_quad.hpp" +#include "lat_lng.hpp" + +namespace mbgl { +namespace android { + +jni::Local> LatLngQuad::New(jni::JNIEnv& env, std::array coordinates) { + static auto& javaClass = jni::Class::Singleton(env); + static auto quadConstructor = javaClass.GetConstructor, jni::Object, jni::Object, jni::Object>(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 LatLngQuad::getLatLngArray(jni::JNIEnv& env, const jni::Object& quad) { + static auto& javaClass = jni::Class::Singleton(env); + static auto topLeftField = javaClass.GetField >(env, "topLeft"); + static auto topRightField = javaClass.GetField >(env, "topRight"); + static auto bottomRightField = javaClass.GetField >(env, "bottomRight"); + static auto bottomLeftField = javaClass.GetField >(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::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_quad.hpp b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_quad.hpp new file mode 100644 index 000000000..f6560b57b --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geometry/lat_lng_quad.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace mbgl { +namespace android { + +class LatLngQuad : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/LatLngQuad"; }; + + static jni::Local> New(jni::JNIEnv&, std::array); + + static std::array getLatLngArray(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geometry/projected_meters.cpp b/MapboxGLAndroidSDK/src/cpp/geometry/projected_meters.cpp new file mode 100644 index 000000000..e079de006 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geometry/projected_meters.cpp @@ -0,0 +1,17 @@ +#include "projected_meters.hpp" + +namespace mbgl { +namespace android { + +jni::Local> ProjectedMeters::New(jni::JNIEnv& env, double northing, double easting) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, northing, easting); +} + +void ProjectedMeters::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/geometry/projected_meters.hpp b/MapboxGLAndroidSDK/src/cpp/geometry/projected_meters.hpp new file mode 100644 index 000000000..baad1efc8 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/geometry/projected_meters.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { +namespace android { + +class ProjectedMeters : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/geometry/ProjectedMeters"; }; + + static jni::Local> New(jni::JNIEnv&, double, double); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/graphics/pointf.cpp b/MapboxGLAndroidSDK/src/cpp/graphics/pointf.cpp new file mode 100644 index 000000000..2fc762d6e --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/graphics/pointf.cpp @@ -0,0 +1,25 @@ +#include +#include "pointf.hpp" + +namespace mbgl { +namespace android { + +jni::Local> PointF::New(jni::JNIEnv& env, float x, float y) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, x, y); +} + +mbgl::ScreenCoordinate PointF::getScreenCoordinate(jni::JNIEnv& env, const jni::Object& point) { + static auto& javaClass = jni::Class::Singleton(env); + static auto xField = javaClass.GetField(env, "x"); + static auto yField = javaClass.GetField(env, "y"); + return mbgl::ScreenCoordinate{point.Get(env, xField), point.Get(env, yField)}; +} + +void PointF::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/graphics/pointf.hpp b/MapboxGLAndroidSDK/src/cpp/graphics/pointf.hpp new file mode 100644 index 000000000..899468b8e --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/graphics/pointf.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace android { + +class PointF : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "android/graphics/PointF"; }; + + static jni::Local> New(jni::JNIEnv&, float, float); + + static mbgl::ScreenCoordinate getScreenCoordinate(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/graphics/rectf.cpp b/MapboxGLAndroidSDK/src/cpp/graphics/rectf.cpp new file mode 100644 index 000000000..8f418da03 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/graphics/rectf.cpp @@ -0,0 +1,35 @@ +#include "rectf.hpp" + +namespace mbgl { +namespace android { + +float RectF::getLeft(jni::JNIEnv& env, const jni::Object& rectf) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "left"); + return rectf.Get(env, field); +} + +float RectF::getTop(jni::JNIEnv& env, const jni::Object& rectf) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "top"); + return rectf.Get(env, field); +} + +float RectF::getRight(jni::JNIEnv& env, const jni::Object& rectf) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "right"); + return rectf.Get(env, field); +} + +float RectF::getBottom(jni::JNIEnv& env, const jni::Object& rectf) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "bottom"); + return rectf.Get(env, field); +} + +void RectF::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/graphics/rectf.hpp b/MapboxGLAndroidSDK/src/cpp/graphics/rectf.hpp new file mode 100644 index 000000000..ba611f226 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/graphics/rectf.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include + +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&); + + static float getTop(jni::JNIEnv&, const jni::Object&); + + static float getRight(jni::JNIEnv&, const jni::Object&); + + static float getBottom(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/gson/json_array.cpp b/MapboxGLAndroidSDK/src/cpp/gson/json_array.cpp new file mode 100644 index 000000000..8ffa658b7 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/gson/json_array.cpp @@ -0,0 +1,50 @@ +#include "json_array.hpp" +#include "json_element.hpp" + +namespace mbgl { +namespace android { +namespace gson { + +jni::Local> JsonArray::New(jni::JNIEnv& env, const std::vector& values){ + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + static auto addMethod = javaClass.GetMethod)>(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 JsonArray::convert(jni::JNIEnv& env, const jni::Object& jsonArray) { + std::vector values; + + if (jsonArray) { + static auto& javaClass = jni::Class::Singleton(env); + static auto getMethod = javaClass.GetMethod (jni::jint)>(env, "get"); + static auto sizeMethod = javaClass.GetMethod(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::Singleton(env); +} + +} // namespace gson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/gson/json_array.hpp b/MapboxGLAndroidSDK/src/cpp/gson/json_array.hpp new file mode 100644 index 000000000..24d5d3d29 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/gson/json_array.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "json_element.hpp" + +#include + +namespace mbgl { +namespace android { +namespace gson { + +class JsonArray { +public: + using SuperTag = JsonElement; + static constexpr auto Name() { return "com/google/gson/JsonArray"; }; + + static jni::Local> New(jni::JNIEnv&, const std::vector&); + static std::vector convert(JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace gson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/gson/json_element.cpp b/MapboxGLAndroidSDK/src/cpp/gson/json_element.cpp new file mode 100644 index 000000000..ba22304d0 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/gson/json_element.cpp @@ -0,0 +1,88 @@ +#include "json_element.hpp" +#include "json_array.hpp" +#include "json_object.hpp" +#include "json_primitive.hpp" + +namespace mbgl { +namespace android { +namespace gson { + +jni::Local> JsonElement::New(jni::JNIEnv& env, const mbgl::Value& value) { + static auto& primitive = jni::Class::Singleton(env); + static auto stringConstructor = primitive.GetConstructor(env); + static auto numberConstructor = primitive.GetConstructor(env); + static auto booleanConstructor = primitive.GetConstructor(env); + + return value.match( + [&] (const mbgl::NullValue&) { + return jni::Local>(); + }, + [&] (const std::string& value) { + return primitive.New(env, stringConstructor, jni::Make(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& 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) { + if (!jsonElement) { + return mbgl::NullValue(); + } + + static auto& elementClass = jni::Class::Singleton(env); + static auto isJsonObject = elementClass.GetMethod(env, "isJsonObject"); + static auto isJsonArray = elementClass.GetMethod(env, "isJsonArray"); + static auto isJsonPrimitive = elementClass.GetMethod(env, "isJsonPrimitive"); + + static auto& primitiveClass = jni::Class::Singleton(env); + static auto isBoolean = primitiveClass.GetMethod(env, "isBoolean"); + static auto isString = primitiveClass.GetMethod(env, "isString"); + static auto isNumber = primitiveClass.GetMethod(env, "isNumber"); + static auto getAsBoolean = primitiveClass.GetMethod(env, "getAsBoolean"); + static auto getAsString = primitiveClass.GetMethod(env, "getAsString"); + static auto getAsDouble = primitiveClass.GetMethod(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(env, primitive.Call(env, getAsString)); + } else { + return mbgl::NullValue(); + } + } else if (jsonElement.Call(env, isJsonObject)) { + return JsonObject::convert(env, jni::Cast(env, jni::Class::Singleton(env), jsonElement)); + } else if (jsonElement.Call(env, isJsonArray)) { + return JsonArray::convert(env, jni::Cast(env, jni::Class::Singleton(env), jsonElement)); + } else { + return mbgl::NullValue(); + } +} + +void JsonElement::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace gson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/gson/json_element.hpp b/MapboxGLAndroidSDK/src/cpp/gson/json_element.hpp new file mode 100644 index 000000000..e3b8a0268 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/gson/json_element.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include + +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> New(jni::JNIEnv&, const mbgl::Value&); + static mbgl::Value convert(JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace gson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/gson/json_object.cpp b/MapboxGLAndroidSDK/src/cpp/gson/json_object.cpp new file mode 100644 index 000000000..10d244ee4 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/gson/json_object.cpp @@ -0,0 +1,65 @@ +#include "json_object.hpp" + +#include "json_element.hpp" + +#include "../java/util.hpp" + +namespace mbgl { +namespace android { +namespace gson { + + +jni::Local> JsonObject::New(jni::JNIEnv& env, const mbgl::PropertyMap& values) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + static auto addMethod = javaClass.GetMethod)>(env, "add"); + + auto jsonObject = javaClass.New(env, constructor); + + for (auto &item : values) { + jsonObject.Call(env, addMethod, + jni::Make(env, item.first), + JsonElement::New(env, item.second)); + } + + return jsonObject; +} + +template // void (jni::String, jni::Object) +static void iterateEntrySet(jni::JNIEnv& env, const jni::Object& jsonObject, F callback) { + // Get Set> + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod ()>(env, "entrySet"); + + auto entryArray = java::util::Set::toArray(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(env, entry), + java::util::Map::Entry::getValue(env, entry)); + } + } +} + +mbgl::PropertyMap JsonObject::convert(jni::JNIEnv& env, const jni::Object& jsonObject) { + mbgl::PropertyMap map; + + if (jsonObject) { + iterateEntrySet(env, jsonObject, [&map, &env](const jni::String& jId, const jni::Object& jsonElement) { + map[jni::Make(env, jId)] = JsonElement::convert(env, jsonElement); + }); + } + + return map; +} + +void JsonObject::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace gson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/gson/json_object.hpp b/MapboxGLAndroidSDK/src/cpp/gson/json_object.hpp new file mode 100644 index 000000000..b8916f8ae --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/gson/json_object.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "json_element.hpp" + +#include + +namespace mbgl { +namespace android { +namespace gson { + +class JsonObject { +public: + using SuperTag = JsonElement; + static constexpr auto Name() { return "com/google/gson/JsonObject"; }; + + static jni::Local> New(jni::JNIEnv&, const mbgl::PropertyMap&); + static mbgl::PropertyMap convert(JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace gson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/gson/json_primitive.cpp b/MapboxGLAndroidSDK/src/cpp/gson/json_primitive.cpp new file mode 100644 index 000000000..79d6b0761 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/gson/json_primitive.cpp @@ -0,0 +1,13 @@ +#include "json_primitive.hpp" + +namespace mbgl { +namespace android { +namespace gson { + +void JsonPrimitive::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace gson +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/gson/json_primitive.hpp b/MapboxGLAndroidSDK/src/cpp/gson/json_primitive.hpp new file mode 100644 index 000000000..36f138d90 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/gson/json_primitive.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "json_element.hpp" + +#include + +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 \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/http_file_source.cpp b/MapboxGLAndroidSDK/src/cpp/http_file_source.cpp new file mode 100644 index 000000000..37f730737 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/http_file_source.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "attach_env.hpp" + +namespace mbgl { + +class HTTPFileSource::Impl { +public: + android::UniqueEnv env { android::AttachEnv() }; +}; + +class HTTPRequest : public AsyncRequest { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/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& body); + + jni::Global> 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::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + jni::RegisterNativePeer(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::Singleton(env); + static auto constructor = + javaClass.GetConstructor(env); + + javaRequest = jni::NewGlobal(env, + javaClass.New(env, constructor, + reinterpret_cast(this), + jni::Make(env, resource.url), + jni::Make(env, etagStr), + jni::Make(env, modifiedStr), + (jboolean) (resource_.usage == Resource::Usage::Offline) + ) + ); +} + +HTTPRequest::~HTTPRequest() { + android::UniqueEnv env = android::AttachEnv(); + + static auto& javaClass = jni::Class::Singleton(*env); + static auto cancel = javaClass.GetMethod(*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& body) { + + using Error = Response::Error; + + if (etag) { + response.etag = jni::Make(env, etag); + } + + if (modified) { + response.modified = util::parseTimestamp(jni::Make(env, modified).c_str()); + } + + if (cacheControl) { + const auto cc = http::CacheControl::parse(jni::Make(env, cacheControl).c_str()); + response.expires = cc.toTimePoint(); + response.mustRevalidate = cc.mustRevalidate; + } + + if (expires) { + response.expires = util::parseTimestamp(jni::Make(env, expires).c_str()); + } + + if (code == 200) { + if (body) { + auto data = std::make_shared(body.Length(env), char()); + jni::GetArrayRegion(env, *body, 0, data->size(), reinterpret_cast(&(*data)[0])); + response.data = data; + } else { + response.data = std::make_shared(); + } + } 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::Reason::NotFound, "HTTP status code 404"); + } else if (code == 429) { + optional retryAfter; + optional xRateLimitReset; + if (jRetryAfter) { + retryAfter = jni::Make(env, jRetryAfter); + } + if (jXRateLimitReset) { + xRateLimitReset = jni::Make(env, jXRateLimitReset); + } + response.error = std::make_unique(Error::Reason::RateLimit, "HTTP status code 429", http::parseRetryHeaders(retryAfter, xRateLimitReset)); + } else if (code >= 500 && code < 600) { + response.error = std::make_unique(Error::Reason::Server, std::string{ "HTTP status code " } + util::toString(code)); + } else { + response.error = std::make_unique(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(env, message); + + using Error = Response::Error; + + switch (type) { + case connectionError: + response.error = std::make_unique(Error::Reason::Connection, messageStr); + break; + case temporaryError: + response.error = std::make_unique(Error::Reason::Server, messageStr); + break; + default: + response.error = std::make_unique(Error::Reason::Other, messageStr); + } + + async.send(); +} + +HTTPFileSource::HTTPFileSource() + : impl(std::make_unique()) { +} + +HTTPFileSource::~HTTPFileSource() = default; + +std::unique_ptr HTTPFileSource::request(const Resource& resource, Callback callback) { + return std::make_unique(*impl->env, resource, callback); +} + +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/i18n/collator.cpp b/MapboxGLAndroidSDK/src/cpp/i18n/collator.cpp new file mode 100644 index 000000000..92c6ce016 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/i18n/collator.cpp @@ -0,0 +1,192 @@ +#include +#include + +#include + +#include "attach_env.hpp" +#include "collator_jni.hpp" + +namespace mbgl { +namespace android { + +void Collator::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +jni::Local> Collator::getInstance(jni::JNIEnv& env, const jni::Object& locale) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jni::Object)>(env, "getInstance"); + return javaClass.Call(env, method, locale); +} + +void Collator::setStrength(jni::JNIEnv& env, const jni::Object& collator, jni::jint strength) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "setStrength"); + collator.Call(env, method, strength); +} + +jni::jint Collator::compare(jni::JNIEnv& env, const jni::Object& collator, const jni::String& lhs, const jni::String& rhs) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "compare"); + return collator.Call(env, method, lhs, rhs); +} + +void StringUtils::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +jni::Local StringUtils::unaccent(jni::JNIEnv& env, const jni::String& value) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod(env, "unaccent"); + return javaClass.Call(env, method, value); +} + +void Locale::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +/* +We would prefer to use for/toLanguageTag, but they're only available in API level 21+ + +jni::Object Locale::forLanguageTag(jni::JNIEnv& env, jni::String languageTag) { + using Signature = jni::Object(jni::String); + auto method = javaClass.GetStaticMethod(env, "forLanguageTag"); + return javaClass.Call(env, method, languageTag); +} + +jni::String Locale::toLanguageTag(jni::JNIEnv& env, jni::Object locale) { + using Signature = jni::String(); + auto static method = javaClass.GetMethod(env, "toLanguageTag"); + return locale.Call(env, method); +} +*/ + +jni::Local Locale::getLanguage(jni::JNIEnv& env, const jni::Object& locale) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "getLanguage"); + return locale.Call(env, method); +} + +jni::Local Locale::getCountry(jni::JNIEnv& env, const jni::Object& locale) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "getCountry"); + return locale.Call(env, method); +} + +jni::Local> Locale::getDefault(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod ()>(env, "getDefault"); + return javaClass.Call(env, method); +} + +jni::Local> Locale::New(jni::JNIEnv& env, const jni::String& language) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, language); +} + +jni::Local> Locale::New(jni::JNIEnv& env, const jni::String& language, const jni::String& region) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, language, region); +} + +} // namespace android + +namespace platform { + +class Collator::Impl { +public: + Impl(bool caseSensitive_, bool diacriticSensitive_, optional 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(*env, *languageTag.language))); + } else { + locale = jni::NewGlobal(*env, + android::Locale::New(*env, jni::Make(*env, *languageTag.language), + jni::Make(*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 jlhs = useUnaccent + ? android::StringUtils::unaccent(*env, jni::Make(*env, lhs)) + : jni::Make(*env, lhs); + jni::Local jrhs = useUnaccent + ? android::StringUtils::unaccent(*env, jni::Make(*env, rhs)) + : jni::Make(*env, rhs); + + jni::jint result = android::Collator::compare(*env, collator, jlhs, jrhs); + + return result; + } + + std::string resolvedLocale() const { + std::string language = jni::Make(*env, android::Locale::getLanguage(*env, locale)); + std::string region = jni::Make(*env, android::Locale::getCountry(*env, locale)); + + optional resultLanguage; + if (!language.empty()) resultLanguage = language; + optional resultRegion; + if (!region.empty()) resultRegion = region; + + return LanguageTag(resultLanguage, {}, resultRegion).toBCP47(); + } + +private: + bool caseSensitive; + bool diacriticSensitive; + + android::UniqueEnv env; + jni::Global> collator; + jni::Global> locale; +}; + +Collator::Collator(bool caseSensitive, bool diacriticSensitive, const optional& locale_) + : impl(std::make_shared(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 diff --git a/MapboxGLAndroidSDK/src/cpp/i18n/collator_jni.hpp b/MapboxGLAndroidSDK/src/cpp/i18n/collator_jni.hpp new file mode 100644 index 000000000..612f9b49f --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/i18n/collator_jni.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +/* + 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 forLanguageTag(jni::JNIEnv&, jni::String); + static jni::String toLanguageTag(jni::JNIEnv&, jni::Object); + */ + static jni::Local> getDefault(jni::JNIEnv&); + static jni::Local getLanguage(jni::JNIEnv&, const jni::Object&); + static jni::Local getCountry(jni::JNIEnv&, const jni::Object&); + + static jni::Local> New(jni::JNIEnv&, const jni::String&); + static jni::Local> 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> getInstance(jni::JNIEnv&, const jni::Object&); + + static void setStrength(jni::JNIEnv&, const jni::Object&, jni::jint); + + static jni::jint compare(jni::JNIEnv&, const jni::Object&, const jni::String&, const jni::String&); + + + static void registerNative(jni::JNIEnv&); +}; + + +class StringUtils { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/utils/StringUtils"; }; + + static jni::Local unaccent(jni::JNIEnv&, const jni::String&); + + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/i18n/number_format.cpp b/MapboxGLAndroidSDK/src/cpp/i18n/number_format.cpp new file mode 100644 index 000000000..a804e74f1 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/i18n/number_format.cpp @@ -0,0 +1,80 @@ +#include +#include + +#include + +#include "attach_env.hpp" +#include "number_format_jni.hpp" + +namespace mbgl { +namespace android { + +void NumberFormat::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +jni::Local> NumberFormat::getInstance(jni::JNIEnv& env, const jni::Object& locale) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jni::Object)>(env, "getInstance"); + return javaClass.Call(env, method, locale); +} + +jni::Local> NumberFormat::getCurrencyInstance(jni::JNIEnv& env, const jni::Object& locale) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jni::Object)>(env, "getCurrencyInstance"); + return javaClass.Call(env, method, locale); +} + +jni::Local NumberFormat::format(jni::JNIEnv& env, const jni::Object& nf, jni::jdouble number) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "format"); + return nf.Call(env, method, number); +} + +void NumberFormat::setMinimumFractionDigits(jni::JNIEnv& env, const jni::Object& nf, jni::jint value) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "setMinimumFractionDigits"); + return nf.Call(env, method, value); +} + +void NumberFormat::setMaximumFractionDigits(jni::JNIEnv& env, const jni::Object& nf, jni::jint value) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(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> 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(*env, *languageTag.language))); + } else { + locale = jni::NewGlobal(*env, android::Locale::New(*env, jni::Make(*env, *languageTag.language), + jni::Make(*env, *languageTag.region))); + } + + jni::Global> formatter; + if (currency.empty()) { + formatter = jni::NewGlobal(*env, android::NumberFormat::getInstance(*env, locale)); + android::NumberFormat::setMinimumFractionDigits(*env, formatter, static_cast(minFractionDigits)); + android::NumberFormat::setMaximumFractionDigits(*env, formatter, static_cast(maxFractionDigits)); + } else { + formatter = jni::NewGlobal(*env, android::NumberFormat::getCurrencyInstance(*env, locale)); + } + + auto result = android::NumberFormat::format(*env, formatter, static_cast(number)); + return jni::Make(*env, result); +} + +} // namespace platform +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/i18n/number_format_jni.hpp b/MapboxGLAndroidSDK/src/cpp/i18n/number_format_jni.hpp new file mode 100644 index 000000000..172003892 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/i18n/number_format_jni.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#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> getInstance(jni::JNIEnv&, const jni::Object&); + static jni::Local> getCurrencyInstance(jni::JNIEnv&, const jni::Object&); + static jni::Local format(jni::JNIEnv&, const jni::Object&, jni::jdouble); + static void setMinimumFractionDigits(jni::JNIEnv&, const jni::Object&, jni::jint); + static void setMaximumFractionDigits(jni::JNIEnv&, const jni::Object&, jni::jint); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/java/util.cpp b/MapboxGLAndroidSDK/src/cpp/java/util.cpp new file mode 100644 index 000000000..b3aebb36c --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/java/util.cpp @@ -0,0 +1,19 @@ +#include "util.hpp" + +namespace mbgl { +namespace android { +namespace java { +namespace util { + +void registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); + jni::Class::Singleton(env); + jni::Class::Singleton(env); + jni::Class::Singleton(env); + jni::Class::Singleton(env); +} + +} // namespace util +} // namespace java +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/java/util.hpp b/MapboxGLAndroidSDK/src/cpp/java/util.hpp new file mode 100644 index 000000000..9b0c01459 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/java/util.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace android { +namespace java { +namespace util { + +class List : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "java/util/List"; }; + + template + static jni::Local>> toArray(jni::JNIEnv& env, const jni::Object& list) { + static auto& javaClass = jni::Class::Singleton(env); + static auto toArray = javaClass.GetMethod> ()>(env, "toArray"); + + return jni::Local>>(env, list.Call(env, toArray).release()); + }; +}; + +class Arrays : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "java/util/Arrays"; }; + + template + static jni::Local> asList(jni::JNIEnv& env, const jni::Array>& array) { + static auto& javaClass = jni::Class::Singleton(env); + static auto asList = javaClass.GetStaticMethod(jni::Array>)>(env, "asList"); + + auto typeErasedArray = jni::Local>>(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 + static jni::Local>> toArray(jni::JNIEnv& env, const jni::Object& list) { + static auto& javaClass = jni::Class::Singleton(env); + static auto toArray = javaClass.GetMethod> ()>(env, "toArray"); + + return jni::Local>>(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 + static jni::Local> getKey(jni::JNIEnv& env, const jni::Object& entry) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod ()>(env, "getKey"); + return jni::Cast(env, jni::Class::Singleton(env), entry.Call(env, method)); + } + + template + static jni::Local> getValue(jni::JNIEnv& env, const jni::Object& entry) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod ()>(env, "getValue"); + return jni::Cast(env, jni::Class::Singleton(env), entry.Call(env, method)); + } + }; +}; + +void registerNative(jni::JNIEnv&); + +} // namespace util +} // namespace java +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/java_types.cpp b/MapboxGLAndroidSDK/src/cpp/java_types.cpp new file mode 100644 index 000000000..7a1ba93a5 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/java_types.cpp @@ -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;"); + } + +} +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/java_types.hpp b/MapboxGLAndroidSDK/src/cpp/java_types.hpp new file mode 100644 index 000000000..c7c93ce71 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/java_types.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +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&); +} +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/jni_native.cpp b/MapboxGLAndroidSDK/src/cpp/jni_native.cpp new file mode 100644 index 000000000..d6b03d981 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/jni_native.cpp @@ -0,0 +1,168 @@ +#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 "mapbox.hpp" +#include "native_map_view.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 + Mapbox::registerNative(env); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/jni_native.hpp b/MapboxGLAndroidSDK/src/cpp/jni_native.hpp new file mode 100644 index 000000000..1cc9a7e81 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/jni_native.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "jni.hpp" + +namespace mbgl { +namespace android { + +MBGL_EXPORT void registerNatives(JavaVM* vm); + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/logger.cpp b/MapboxGLAndroidSDK/src/cpp/logger.cpp new file mode 100644 index 000000000..f5d78cc83 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/logger.cpp @@ -0,0 +1,34 @@ +#include "logger.hpp" +#include "java_types.hpp" + +namespace mbgl { +namespace android { + +void Logger::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +void Logger::log(jni::JNIEnv& env, EventSeverity severity, const std::string &msg) { + static auto& _class = jni::Class::Singleton(env); + + auto tag = jni::Make(env, "Mbgl"); + auto message = jni::Make(env, msg); + using Signature = void(jni::String, jni::String); + + if (severity == EventSeverity::Debug) { + auto static debug = _class.GetStaticMethod(env, "d"); + _class.Call(env, debug, tag, message); + } else if (severity == EventSeverity::Info) { + auto static info = _class.GetStaticMethod(env, "i"); + _class.Call(env, info, tag, message); + } else if(severity == EventSeverity::Warning) { + auto static warning = _class.GetStaticMethod(env, "w"); + _class.Call(env, warning, tag, message); + } else { + auto static error = _class.GetStaticMethod(env, "e"); + _class.Call(env, error, tag, message); + } +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/logger.hpp b/MapboxGLAndroidSDK/src/cpp/logger.hpp new file mode 100644 index 000000000..7481ecb26 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/logger.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include "bitmap.hpp" + +namespace mbgl { +namespace android { + +class Logger { +public: + static constexpr auto Name() { + return "com/mapbox/mapboxsdk/log/Logger"; + }; + static void registerNative(jni::JNIEnv&); + + static void log(jni::JNIEnv&, EventSeverity severity, const std::string &msg); +}; + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/logging_android.cpp b/MapboxGLAndroidSDK/src/cpp/logging_android.cpp new file mode 100644 index 000000000..130136728 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/logging_android.cpp @@ -0,0 +1,13 @@ +#include + +#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); +} + +} \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/main.cpp b/MapboxGLAndroidSDK/src/cpp/main.cpp new file mode 100644 index 000000000..9b5c6c197 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/main.cpp @@ -0,0 +1,9 @@ +#include "jni.hpp" +#include "jni_native.hpp" +#include + +extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) { + assert(vm != nullptr); + mbgl::android::registerNatives(vm); + return JNI_VERSION_1_6; +} diff --git a/MapboxGLAndroidSDK/src/cpp/map/camera_position.cpp b/MapboxGLAndroidSDK/src/cpp/map/camera_position.cpp new file mode 100644 index 000000000..4266b64fd --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/map/camera_position.cpp @@ -0,0 +1,69 @@ +#include "camera_position.hpp" +#include "../geometry/lat_lng.hpp" + +namespace mbgl { +namespace android { + +jni::Local> CameraPosition::New(jni::JNIEnv &env, mbgl::CameraOptions options, float pixelRatio) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor, double, double, double, jni::Array>(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 paddingVect; + auto insets = options.padding.value_or(EdgeInsets {0, 0, 0, 0}); + auto padding = jni::Array::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>(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& position, float pixelRatio) { + static auto& javaClass = jni::Class::Singleton(env); + static auto bearing = javaClass.GetField(env, "bearing"); + static auto target = javaClass.GetField>(env, "target"); + static auto tilt = javaClass.GetField(env, "tilt"); + static auto zoom = javaClass.GetField(env, "zoom"); + static auto paddingField = javaClass.GetField>(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::Singleton(env); +} + +} // namespace android +} // namespace mb + diff --git a/MapboxGLAndroidSDK/src/cpp/map/camera_position.hpp b/MapboxGLAndroidSDK/src/cpp/map/camera_position.hpp new file mode 100644 index 000000000..b677f04ea --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/map/camera_position.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { +namespace android { + +class CameraPosition : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/camera/CameraPosition"; }; + + static jni::Local> New(jni::JNIEnv&, mbgl::CameraOptions, float pixelRatio); + + static mbgl::CameraOptions getCameraOptions(jni::JNIEnv&, const jni::Object&, float pixelRatio); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/map/image.cpp b/MapboxGLAndroidSDK/src/cpp/map/image.cpp new file mode 100644 index 000000000..10c32d257 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/map/image.cpp @@ -0,0 +1,41 @@ +#include +#include +#include "image.hpp" + +namespace mbgl { +namespace android { + +mbgl::style::Image Image::getImage(jni::JNIEnv& env, const jni::Object& image) { + static auto& javaClass = jni::Class::Singleton(env); + static auto widthField = javaClass.GetField(env, "width"); + static auto heightField = javaClass.GetField(env, "height"); + static auto pixelRatioField = javaClass.GetField(env, "pixelRatio"); + static auto bufferField = javaClass.GetField>(env, "buffer"); + static auto nameField = javaClass.GetField(env, "name"); + static auto sdfField = javaClass.GetField(env, "sdf"); + + 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(env, image.Get(env, nameField)); + auto sdf = (bool) image.Get(env, sdfField); + + jni::NullCheck(env, pixels.get()); + std::size_t size = pixels.Length(env); + + mbgl::PremultipliedImage premultipliedImage({ static_cast(width), static_cast(height) }); + if (premultipliedImage.bytes() != uint32_t(size)) { + throw mbgl::util::StyleImageException("Image pixel count mismatch"); + } + + jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast(premultipliedImage.data.get())); + return mbgl::style::Image {name, std::move(premultipliedImage), pixelRatio, sdf}; +} + +void Image::registerNative(jni::JNIEnv &env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mb diff --git a/MapboxGLAndroidSDK/src/cpp/map/image.hpp b/MapboxGLAndroidSDK/src/cpp/map/image.hpp new file mode 100644 index 000000000..7549bc646 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/map/image.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { +namespace android { + +class Image : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/Image"; }; + + static mbgl::style::Image getImage(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/map_renderer.cpp b/MapboxGLAndroidSDK/src/cpp/map_renderer.cpp new file mode 100644 index 000000000..3ebbdb7e5 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/map_renderer.cpp @@ -0,0 +1,247 @@ +#include "map_renderer.hpp" + +#include +#include +#include + +#include + +#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& obj, + jni::jfloat pixelRatio_, + const jni::String& localIdeographFontFamily_) + : javaPeer(_env, obj), + pixelRatio(pixelRatio_), + localIdeographFontFamily(localIdeographFontFamily_ ? jni::Make(_env, localIdeographFontFamily_) + : optional{}), + mailboxData(this) {} + +MapRenderer::MailboxData::MailboxData(Scheduler* scheduler_) : scheduler(scheduler_) { + assert(scheduler); +} + +std::shared_ptr MapRenderer::MailboxData::getMailbox() const noexcept { + if (!mailbox) { + mailbox = std::make_shared(*scheduler); + } + return mailbox; +} + +MapRenderer::~MapRenderer() = default; + +void MapRenderer::reset() { + destroyed = true; + + if (renderer) { + // Make sure to destroy the renderer on the GL Thread + auto self = ActorRef(*this, mailboxData.getMailbox()); + self.ask(&MapRenderer::resetRenderer).wait(); + } + + // Lock to make sure there is no concurrent initialisation on the gl thread + std::lock_guard lock(initialisationMutex); + rendererObserver.reset(); +} + +ActorRef MapRenderer::actor() const { + return *rendererRef; +} + +void MapRenderer::schedule(std::function scheduled) { + // Create a runnable + android::UniqueEnv _env = android::AttachEnv(); + auto runnable = std::make_unique(*_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::Singleton(*_env); + static auto queueEvent = javaClass.GetMethod)>(*_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(); +} + +void MapRenderer::requestRender() { + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onInvalidate = javaClass.GetMethod(*_env, "requestRender"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onInvalidate); + } +} + +void MapRenderer::update(std::shared_ptr params) { + // Lock on the parameters + std::lock_guard lock(updateMutex); + updateParameters = std::move(params); +} + +void MapRenderer::setObserver(std::shared_ptr _rendererObserver) { + // Lock as the initialization can come from the main thread or the GL thread first + std::lock_guard lock(initialisationMutex); + + rendererObserver = std::move(_rendererObserver); + + // Set the new observer on the Renderer implementation + if (renderer) { + renderer->setObserver(rendererObserver.get()); + } +} + +void MapRenderer::requestSnapshot(SnapshotCallback callback) { + auto self = ActorRef(*this, mailboxData.getMailbox()); + self.invoke( + &MapRenderer::scheduleSnapshot, + std::make_unique([&, 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(); +} + +void MapRenderer::scheduleSnapshot(std::unique_ptr callback) { + snapshotCallback = std::move(callback); + requestRender(); +} + +void MapRenderer::render(JNIEnv&) { + assert (renderer); + + std::shared_ptr params; + { + // Lock on the parameters + std::unique_lock 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 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(); + renderer = std::make_unique(*backend, pixelRatio, localIdeographFontFamily); + rendererRef = std::make_unique>(*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(*this, mailboxData.getMailbox()); + self.ask(&MapRenderer::resetRenderer).wait(); +} + +// needs to be called on GL thread +void MapRenderer::onSurfaceDestroyed(JNIEnv&) { + resetRenderer(); +} + +// Static methods // + +void MapRenderer::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer(env, javaClass, "nativePtr", + jni::MakePeer&, 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")); +} + +MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, const jni::Object& jObject) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "nativePtr"); + MapRenderer* mapRenderer = reinterpret_cast(jObject.Get(env, field)); + assert(mapRenderer != nullptr); + return *mapRenderer; +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/map_renderer.hpp b/MapboxGLAndroidSDK/src/cpp/map_renderer.hpp new file mode 100644 index 000000000..e37d37cfc --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/map_renderer.hpp @@ -0,0 +1,138 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace mbgl { + +template +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 "com/mapbox/mapboxsdk/maps/renderer/MapRenderer"; }; + + static void registerNative(jni::JNIEnv&); + + static MapRenderer& getNativePeer(JNIEnv&, const jni::Object&); + + MapRenderer(jni::JNIEnv& _env, + const jni::Object&, + 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); + + // Sets the new update parameters to use on subsequent + // renders. Be sure to trigger a render with + // requestRender(). + void update(std::shared_ptr); + + // Gives a handle to the Renderer to enable actions on + // any thread. + ActorRef actor() const; + + // From Scheduler. Schedules by using callbacks to the + // JVM to process the mailbox on the right thread. + void schedule(std::function scheduled) override; + mapbox::base::WeakPtr makeWeakPtr() override { return weakFactory.makeWeakPtr(); } + + void requestRender(); + + // Snapshot - requires a RunLoop on the calling thread + using SnapshotCallback = std::function; + void requestSnapshot(SnapshotCallback); + +protected: + // Called from the GL Thread // + + void scheduleSnapshot(std::unique_ptr); + +private: + struct MailboxData { + explicit MailboxData(Scheduler*); + std::shared_ptr getMailbox() const noexcept; + + private: + Scheduler* scheduler; + mutable std::shared_ptr 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&); + +private: + jni::WeakReference, jni::EnvAttachingDeleter> javaPeer; + + float pixelRatio; + optional localIdeographFontFamily; + + std::shared_ptr threadPool; + const MailboxData mailboxData; + + std::mutex initialisationMutex; + std::shared_ptr rendererObserver; + + std::unique_ptr backend; + std::unique_ptr renderer; + std::unique_ptr> rendererRef; + + std::shared_ptr updateParameters; + std::mutex updateMutex; + + bool framebufferSizeChanged = false; + std::atomic destroyed {false}; + + std::unique_ptr snapshotCallback; + mapbox::base::WeakPtrFactory weakFactory{this}; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/map_renderer_runnable.cpp b/MapboxGLAndroidSDK/src/cpp/map_renderer_runnable.cpp new file mode 100644 index 000000000..227f49ee3 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/map_renderer_runnable.cpp @@ -0,0 +1,49 @@ +#include "map_renderer_runnable.hpp" + +#include + +namespace mbgl { +namespace android { + +MapRendererRunnable::MapRendererRunnable(jni::JNIEnv& env, std::function 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::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + auto instance = javaClass.New(env, constructor, reinterpret_cast(this)); + javaPeer = jni::NewGlobal(env, instance); +} + +MapRendererRunnable::~MapRendererRunnable() = default; + +void MapRendererRunnable::run(jni::JNIEnv&) { + if (function) function(); +} + +jni::Global> MapRendererRunnable::peer() { + return std::move(javaPeer); +} + +// Static methods // + +void MapRendererRunnable::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + std::make_unique, + "nativeInitialize", + "finalize", + METHOD(&MapRendererRunnable::run, "run")); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/map_renderer_runnable.hpp b/MapboxGLAndroidSDK/src/cpp/map_renderer_runnable.hpp new file mode 100644 index 000000000..24d0f2af4 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/map_renderer_runnable.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +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 "com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable"; }; + + static void registerNative(jni::JNIEnv&); + + MapRendererRunnable(jni::JNIEnv&, std::function); + + // 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> peer(); + +private: + jni::Global> javaPeer; + std::function function; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/mapbox.cpp b/MapboxGLAndroidSDK/src/cpp/mapbox.cpp new file mode 100644 index 000000000..5246739cf --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/mapbox.cpp @@ -0,0 +1,23 @@ +#include "mapbox.hpp" + +namespace mbgl { +namespace android { + +jni::Local> Mapbox::getAssetManager(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + auto method = javaClass.GetStaticMethod()>(env, "getAssetManager"); + return javaClass.Call(env, method); +} + +jboolean Mapbox::hasInstance(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + auto method = javaClass.GetStaticMethod(env, "hasInstance"); + return javaClass.Call(env, method); +} + +void Mapbox::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/mapbox.hpp b/MapboxGLAndroidSDK/src/cpp/mapbox.hpp new file mode 100644 index 000000000..813f5bf17 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/mapbox.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "asset_manager.hpp" + +#include + +namespace mbgl { +namespace android { + +class Mapbox { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/Mapbox"; }; + static jboolean hasInstance(jni::JNIEnv&); + static jni::Local> getAssetManager(jni::JNIEnv&); + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/native_map_view.cpp b/MapboxGLAndroidSDK/src/cpp/native_map_view.cpp new file mode 100644 index 000000000..ddb2e2c3b --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/native_map_view.cpp @@ -0,0 +1,1263 @@ +#include "native_map_view.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Java -> C++ conversion +#include "style/android_conversion.hpp" +#include +#include + +// C++ -> Java conversion +#include "conversion/conversion.hpp" +#include "conversion/collection.hpp" +#include "style/conversion/filter.hpp" +#include "geojson/feature.hpp" + +#include "android_renderer_frontend.hpp" +#include "attach_env.hpp" +#include "bitmap.hpp" +#include "bitmap_factory.hpp" +#include "file_source.hpp" +#include "geometry/lat_lng_bounds.hpp" +#include "java/util.hpp" +#include "jni.hpp" +#include "map/camera_position.hpp" +#include "map/image.hpp" +#include "map_renderer.hpp" +#include "run_loop_impl.hpp" +#include "style/light.hpp" + +namespace mbgl { +namespace android { + +NativeMapView::NativeMapView(jni::JNIEnv& _env, + const jni::Object& _obj, + const jni::Object& jFileSource, + const jni::Object& jMapRenderer, + jni::jfloat pixelRatio_, + jni::jboolean crossSourceCollisions_) + : javaPeer(_env, _obj), mapRenderer(MapRenderer::getNativePeer(_env, jMapRenderer)), pixelRatio(pixelRatio_) { + // Get a reference to the JavaVM for callbacks + if (_env.GetJavaVM(&vm) < 0) { + _env.ExceptionDescribe(); + return; + } + + // Create a renderer frontend + rendererFrontend = std::make_unique(mapRenderer); + + // Create Map options + MapOptions options; + options.withMapMode(MapMode::Continuous) + .withSize(mbgl::Size{static_cast(width), static_cast(height)}) + .withPixelRatio(pixelRatio) + .withConstrainMode(ConstrainMode::HeightOnly) + .withViewportMode(ViewportMode::Default) + .withCrossSourceCollisions(crossSourceCollisions_); + + // Create the core map + map = std::make_unique( + *rendererFrontend, *this, options, + mbgl::android::FileSource::getSharedResourceOptions(_env, jFileSource)); +} + +/** + * Called through NativeMapView#destroy() + */ +NativeMapView::~NativeMapView() { + map.reset(); + vm = nullptr; +} + +void NativeMapView::onCameraWillChange(MapObserver::CameraChangeMode mode) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onCameraWillChange = javaClass.GetMethod(*_env, "onCameraWillChange"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onCameraWillChange, (jboolean) (mode != MapObserver::CameraChangeMode::Immediate)); + } +} + + +void NativeMapView::onCameraIsChanging() { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onCameraIsChanging = javaClass.GetMethod(*_env, "onCameraIsChanging"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onCameraIsChanging); + } +} + +void NativeMapView::onCameraDidChange(MapObserver::CameraChangeMode mode) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onCameraDidChange = javaClass.GetMethod(*_env, "onCameraDidChange"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onCameraDidChange, + (jboolean) (mode != MapObserver::CameraChangeMode::Immediate)); + } +} + +void NativeMapView::onWillStartLoadingMap() { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onWillStartLoadingMap = javaClass.GetMethod(*_env, "onWillStartLoadingMap"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onWillStartLoadingMap); + } +} + +void NativeMapView::onDidFinishLoadingMap() { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onDidFinishLoadingMap = javaClass.GetMethod(*_env, "onDidFinishLoadingMap"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onDidFinishLoadingMap); + } +} + +void NativeMapView::onDidFailLoadingMap(MapLoadError, const std::string& error) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onDidFailLoadingMap = javaClass.GetMethod(*_env, "onDidFailLoadingMap"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onDidFailLoadingMap, jni::Make(*_env, error)); + } +} + +void NativeMapView::onWillStartRenderingFrame() { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onWillStartRenderingFrame = javaClass.GetMethod(*_env, "onWillStartRenderingFrame"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onWillStartRenderingFrame); + } +} + +void NativeMapView::onDidFinishRenderingFrame(MapObserver::RenderFrameStatus status) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onDidFinishRenderingFrame = javaClass.GetMethod(*_env, "onDidFinishRenderingFrame"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onDidFinishRenderingFrame, (jboolean) (status.mode != MapObserver::RenderMode::Partial)); + } +} + + +void NativeMapView::onWillStartRenderingMap() { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onWillStartRenderingMap = javaClass.GetMethod(*_env, "onWillStartRenderingMap"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onWillStartRenderingMap); + } +} + +void NativeMapView::onDidFinishRenderingMap(MapObserver::RenderMode mode) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onDidFinishRenderingMap = javaClass.GetMethod(*_env, "onDidFinishRenderingMap"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onDidFinishRenderingMap, (jboolean) (mode != MapObserver::RenderMode::Partial)); + } +} + +void NativeMapView::onDidBecomeIdle() { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onDidBecomeIdle = javaClass.GetMethod(*_env, "onDidBecomeIdle"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onDidBecomeIdle); + } +} + +void NativeMapView::onDidFinishLoadingStyle() { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onDidFinishLoadingStyle = javaClass.GetMethod(*_env, "onDidFinishLoadingStyle"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onDidFinishLoadingStyle); + } +} + +void NativeMapView::onSourceChanged(mbgl::style::Source& source) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onSourceChanged = javaClass.GetMethod(*_env, "onSourceChanged"); + auto sourceId = jni::Make(*_env, source.getID()); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onSourceChanged, sourceId); + } +} + +void NativeMapView::onStyleImageMissing(const std::string& imageId) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onStyleImageMissing = javaClass.GetMethod(*_env, "onStyleImageMissing"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onStyleImageMissing, jni::Make(*_env, imageId)); + } +} + +bool NativeMapView::onCanRemoveUnusedStyleImage(const std::string& imageId) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onCanRemoveUnusedStyleImage = javaClass.GetMethod(*_env, "onCanRemoveUnusedStyleImage"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + return weakReference.Call(*_env, onCanRemoveUnusedStyleImage, jni::Make(*_env, imageId)); + } + + return true; +} + +// JNI Methods // + +void NativeMapView::resizeView(jni::JNIEnv&, int w, int h) { + width = util::max(64, w); + height = util::max(64, h); + map->setSize({ static_cast(width), static_cast(height) }); +} + +jni::Local NativeMapView::getStyleUrl(jni::JNIEnv& env) { + return jni::Make(env, map->getStyle().getURL()); +} + +void NativeMapView::setStyleUrl(jni::JNIEnv& env, const jni::String& url) { + map->getStyle().loadURL(jni::Make(env, url)); +} + +jni::Local NativeMapView::getStyleJson(jni::JNIEnv& env) { + return jni::Make(env, map->getStyle().getJSON()); +} + +void NativeMapView::setStyleJson(jni::JNIEnv& env, const jni::String& json) { + map->getStyle().loadJSON(jni::Make(env, json)); +} + +void NativeMapView::setLatLngBounds(jni::JNIEnv& env, const jni::Object& jBounds) { + mbgl::BoundOptions bounds; + if (jBounds) { + bounds.withLatLngBounds(mbgl::android::LatLngBounds::getLatLngBounds(env, jBounds)); + } else { + bounds.withLatLngBounds(mbgl::LatLngBounds()); + } + map->setBounds(bounds); +} + +void NativeMapView::cancelTransitions(jni::JNIEnv&) { + map->cancelTransitions(); +} + +void NativeMapView::setGestureInProgress(jni::JNIEnv&, jni::jboolean inProgress) { + map->setGestureInProgress(inProgress); +} + +void NativeMapView::moveBy(jni::JNIEnv&, jni::jdouble dx, jni::jdouble dy, jni::jlong duration) { + mbgl::AnimationOptions animationOptions; + if (duration > 0) { + animationOptions.duration.emplace(mbgl::Milliseconds(duration)); + animationOptions.easing.emplace(mbgl::util::UnitBezier {0.25, 0.46, 0.45, 0.94}); + } + map->moveBy({dx, dy}, animationOptions); +} + +void NativeMapView::jumpTo(jni::JNIEnv& env, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jdouble pitch, jni::jdouble zoom, const jni::Array& padding) { + mbgl::CameraOptions options; + if (bearing != -1) { + options.bearing = bearing; + } + options.center = mbgl::LatLng(latitude, longitude); + if (padding) { + assert(padding.Length(env) == 4); + options.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1), + padding.Get(env, 2), padding.Get(env, 3)}; + } + if (pitch != -1) { + options.pitch = pitch; + } + if (zoom != -1) { + options.zoom = zoom; + } + + map->jumpTo(options); +} + +void NativeMapView::easeTo(jni::JNIEnv& env, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom, const jni::Array& padding, jni::jboolean easing) { + mbgl::CameraOptions cameraOptions; + if (bearing != -1) { + cameraOptions.bearing = bearing; + } + cameraOptions.center = mbgl::LatLng(latitude, longitude); + if (padding) { + assert(padding.Length(env) == 4); + cameraOptions.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1), + padding.Get(env, 2), padding.Get(env, 3)}; + } + if (pitch != -1) { + cameraOptions.pitch = pitch; + } + if (zoom != -1) { + cameraOptions.zoom = zoom; + } + + mbgl::AnimationOptions animationOptions; + animationOptions.duration.emplace(mbgl::Milliseconds(duration)); + if (!easing) { + // add a linear interpolator instead of easing + animationOptions.easing.emplace(mbgl::util::UnitBezier { 0, 0, 1, 1 }); + } + + map->easeTo(cameraOptions, animationOptions); +} + +void NativeMapView::flyTo(jni::JNIEnv& env, jni::jdouble bearing, jni::jdouble latitude, jni::jdouble longitude, jni::jlong duration, jni::jdouble pitch, jni::jdouble zoom, const jni::Array& padding) { + mbgl::CameraOptions cameraOptions; + if (bearing != -1) { + cameraOptions.bearing = bearing; + } + cameraOptions.center = mbgl::LatLng(latitude, longitude); + if (padding) { + assert(padding.Length(env) == 4); + cameraOptions.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1), + padding.Get(env, 2), padding.Get(env, 3)}; + } + if (pitch != -1) { + cameraOptions.pitch = pitch; + } + if (zoom != -1) { + cameraOptions.zoom = zoom; + } + + mbgl::AnimationOptions animationOptions; + animationOptions.duration.emplace(mbgl::Milliseconds(duration)); + map->flyTo(cameraOptions, animationOptions); +} + +jni::Local> NativeMapView::getLatLng(JNIEnv& env) { + return LatLng::New(env, *map->getCameraOptions(mbgl::nullopt).center); +} + +void NativeMapView::setLatLng(jni::JNIEnv& env, jni::jdouble latitude, jni::jdouble longitude, const jni::Array& padding, jni::jlong duration) { + mbgl::CameraOptions cameraOptions; + cameraOptions.center = mbgl::LatLng(latitude, longitude); + if (padding) { + assert(padding.Length(env) == 4); + cameraOptions.padding = mbgl::EdgeInsets{padding.Get(env, 0), padding.Get(env, 1), + padding.Get(env, 2), padding.Get(env, 3)}; + } + map->easeTo(cameraOptions, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +jni::Local> NativeMapView::getCameraForLatLngBounds(jni::JNIEnv& env, const jni::Object& jBounds, double top, double left, double bottom, double right, double bearing, double tilt) { + mbgl::EdgeInsets padding = {top, left, bottom, right}; + return CameraPosition::New(env, map->cameraForLatLngBounds(mbgl::android::LatLngBounds::getLatLngBounds(env, jBounds), padding, bearing, tilt), pixelRatio); +} + +jni::Local> NativeMapView::getCameraForGeometry(jni::JNIEnv& env, const jni::Object& jGeometry, double top, double left, double bottom, double right, double bearing, double tilt) { + auto geometry = geojson::Geometry::convert(env, jGeometry); + mbgl::EdgeInsets padding = {top, left, bottom, right}; + return CameraPosition::New(env, map->cameraForGeometry(geometry, padding, bearing, tilt), pixelRatio); +} + +void NativeMapView::setReachability(jni::JNIEnv&, jni::jboolean reachable) { + if (reachable) { + mbgl::NetworkStatus::Reachable(); + } +} + +void NativeMapView::resetPosition(jni::JNIEnv&) { + map->jumpTo(mbgl::CameraOptions().withCenter(mbgl::LatLng {}).withZoom(0.0).withBearing(0.0).withPitch(0.0)); +} + +jni::jdouble NativeMapView::getPitch(jni::JNIEnv&) { + return *map->getCameraOptions().pitch; +} + +void NativeMapView::setPitch(jni::JNIEnv&, jni::jdouble pitch, jni::jlong duration) { + map->easeTo(mbgl::CameraOptions().withPitch(pitch), + mbgl::AnimationOptions{ mbgl::Milliseconds(duration) }); +} + +void NativeMapView::setZoom(jni::JNIEnv&, jni::jdouble zoom, jni::jdouble x, jni::jdouble y, jni::jlong duration) { + map->easeTo(mbgl::CameraOptions().withZoom(zoom).withAnchor(mbgl::ScreenCoordinate{ x, y }), + mbgl::AnimationOptions{ mbgl::Milliseconds(duration) }); +} + +jni::jdouble NativeMapView::getZoom(jni::JNIEnv&) { + return *map->getCameraOptions().zoom; +} + +void NativeMapView::resetZoom(jni::JNIEnv&) { + map->jumpTo(mbgl::CameraOptions().withZoom(0.0)); +} + +void NativeMapView::setMinZoom(jni::JNIEnv&, jni::jdouble zoom) { + map->setBounds(BoundOptions().withMinZoom(zoom)); +} + +jni::jdouble NativeMapView::getMinZoom(jni::JNIEnv&) { + return *map->getBounds().minZoom; +} + +void NativeMapView::setMaxZoom(jni::JNIEnv&, jni::jdouble zoom) { + map->setBounds(BoundOptions().withMaxZoom(zoom)); +} + +jni::jdouble NativeMapView::getMaxZoom(jni::JNIEnv&) { + return *map->getBounds().maxZoom; +} + +void NativeMapView::setMinPitch(jni::JNIEnv&, jni::jdouble pitch) { + map->setBounds(BoundOptions().withMinPitch(pitch)); +} + +jni::jdouble NativeMapView::getMinPitch(jni::JNIEnv&) { + return *map->getBounds().minPitch; +} + +void NativeMapView::setMaxPitch(jni::JNIEnv&, jni::jdouble pitch) { + map->setBounds(BoundOptions().withMaxPitch(pitch)); +} + +jni::jdouble NativeMapView::getMaxPitch(jni::JNIEnv&) { + return *map->getBounds().maxPitch; +} + +void NativeMapView::rotateBy(jni::JNIEnv&, jni::jdouble sx, jni::jdouble sy, jni::jdouble ex, jni::jdouble ey, jni::jlong duration) { + mbgl::ScreenCoordinate first(sx, sy); + mbgl::ScreenCoordinate second(ex, ey); + map->rotateBy(first, second, mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +void NativeMapView::setBearing(jni::JNIEnv&, jni::jdouble degrees, jni::jlong duration) { + map->easeTo(mbgl::CameraOptions().withBearing(degrees), mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +void NativeMapView::setBearingXY(jni::JNIEnv&, jni::jdouble degrees, jni::jdouble cx, jni::jdouble cy, jni::jlong duration) { + mbgl::ScreenCoordinate anchor(cx, cy); + map->easeTo(mbgl::CameraOptions().withBearing(degrees).withAnchor(anchor), mbgl::AnimationOptions{mbgl::Milliseconds(duration)}); +} + +jni::jdouble NativeMapView::getBearing(jni::JNIEnv&) { + return *map->getCameraOptions().bearing; +} + +void NativeMapView::resetNorth(jni::JNIEnv&) { + map->easeTo(mbgl::CameraOptions().withBearing(0.0), mbgl::AnimationOptions {{mbgl::Milliseconds(500)}}); +} + +void NativeMapView::setVisibleCoordinateBounds(JNIEnv& env, const jni::Array>& coordinates, const jni::Object& padding, jdouble direction, jni::jlong duration) { + NullCheck(env, &coordinates); + std::size_t count = coordinates.Length(env); + + std::vector latLngs; + latLngs.reserve(count); + + for (std::size_t i = 0; i < count; i++) { + latLngs.push_back(LatLng::getLatLng(env, coordinates.Get(env, i))); + } + + mbgl::EdgeInsets mbglInsets = { RectF::getTop(env, padding), RectF::getLeft(env, padding), RectF::getBottom(env, padding), RectF::getRight(env, padding) }; + mbgl::CameraOptions cameraOptions = map->cameraForLatLngs(latLngs, mbglInsets); + if (direction >= 0) { + cameraOptions.bearing = direction; + } + + mbgl::AnimationOptions animationOptions; + if (duration > 0) { + animationOptions.duration.emplace(mbgl::Milliseconds(duration)); + // equivalent to kCAMediaTimingFunctionDefault in iOS + animationOptions.easing.emplace(mbgl::util::UnitBezier { 0.25, 0.1, 0.25, 0.1 }); + } + + map->easeTo(cameraOptions, animationOptions); +} + +void NativeMapView::getVisibleCoordinateBounds(JNIEnv& env, jni::Array& output) { + auto latlngBounds = map->latLngBoundsForCameraUnwrapped(map->getCameraOptions(mbgl::nullopt)); + + double latNorth = latlngBounds.north(); + double lonEast = latlngBounds.east(); + double latSouth = latlngBounds.south(); + double lonWest = latlngBounds.west(); + + std::vector buffer; + buffer.reserve(4); + + // Order of the LatLngBounds: double latNorth, double lonEast, double latSouth, double lonWest + buffer.push_back(latNorth); + buffer.push_back(lonEast); + buffer.push_back(latSouth); + buffer.push_back(lonWest); + + output.SetRegion>(env, 0, buffer); +} + +void NativeMapView::scheduleSnapshot(jni::JNIEnv&) { + mapRenderer.requestSnapshot([&](PremultipliedImage image) { + auto _env = android::AttachEnv(); + // Convert image to bitmap + auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image)); + + // invoke Mapview#OnSnapshotReady + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onSnapshotReady = javaClass.GetMethod)>(*_env, "onSnapshotReady"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onSnapshotReady, bitmap); + } + }); +} + +jni::Local> NativeMapView::getCameraPosition(jni::JNIEnv& env) { + return CameraPosition::New(env, map->getCameraOptions(mbgl::nullopt), pixelRatio); +} + +void NativeMapView::updateMarker(jni::JNIEnv& env, jni::jlong markerId, jni::jdouble lat, jni::jdouble lon, const jni::String& jid) { + if (markerId == -1) { + return; + } + + std::string iconId = jni::Make(env, jid); + // Because Java only has int, not unsigned int, we need to bump the annotation id up to a long. + map->updateAnnotation(markerId, mbgl::SymbolAnnotation { mbgl::Point(lon, lat), iconId }); +} + +jni::Local> NativeMapView::addMarkers(jni::JNIEnv& env, const jni::Array>& jmarkers) { + jni::NullCheck(env, &jmarkers); + std::size_t len = jmarkers.Length(env); + + std::vector ids; + ids.reserve(len); + + for (std::size_t i = 0; i < len; i++) { + auto marker = jmarkers.Get(env, i); + ids.push_back(map->addAnnotation(mbgl::SymbolAnnotation { + Marker::getPosition(env, marker), + Marker::getIconId(env, marker) + })); + } + + auto result = jni::Array::New(env, len); + result.SetRegion>(env, 0, ids); + + return result; +} + +void NativeMapView::onLowMemory(JNIEnv&) { + rendererFrontend->reduceMemoryUse(); +} + +using DebugOptions = mbgl::MapDebugOptions; + +void NativeMapView::setDebug(JNIEnv&, jni::jboolean debug) { + DebugOptions debugOptions = debug ? DebugOptions::TileBorders | DebugOptions::ParseStatus | DebugOptions::Collision + : DebugOptions::NoDebug; + map->setDebug(debugOptions); +} + +jni::jboolean NativeMapView::getDebug(JNIEnv&) { + return map->getDebug() != DebugOptions::NoDebug; +} + +jni::jboolean NativeMapView::isFullyLoaded(JNIEnv&) { + return map->isFullyLoaded(); +} + +jni::jdouble NativeMapView::getMetersPerPixelAtLatitude(JNIEnv&, jni::jdouble lat, jni::jdouble zoom) { + return mbgl::Projection::getMetersPerPixelAtLatitude(lat, zoom); +} + +jni::Local> NativeMapView::projectedMetersForLatLng(JNIEnv& env, jni::jdouble latitude, jni::jdouble longitude) { + mbgl::ProjectedMeters projectedMeters = mbgl::Projection::projectedMetersForLatLng(mbgl::LatLng(latitude, longitude)); + return ProjectedMeters::New(env, projectedMeters.northing(), projectedMeters.easting()); +} + +jni::Local> NativeMapView::latLngForProjectedMeters(JNIEnv& env, jdouble northing, jdouble easting) { + return LatLng::New(env, mbgl::Projection::latLngForProjectedMeters(mbgl::ProjectedMeters(northing, easting))); +} + +jni::Local> NativeMapView::pixelForLatLng(JNIEnv& env, jdouble latitude, jdouble longitude) { + mbgl::ScreenCoordinate pixel = map->pixelForLatLng(mbgl::LatLng(latitude, longitude)); + return PointF::New(env, static_cast(pixel.x), static_cast(pixel.y)); +} + +void NativeMapView::pixelsForLatLngs(JNIEnv& env, + const jni::Array& input, + jni::Array& output, + jfloat pixelRatio_) { + jni::NullCheck(env, &input); + std::size_t len = input.Length(env); + + std::vector latLngs; + latLngs.reserve(len); + + for (std::size_t i = 0; i < len; i += 2) { + auto latLng = mbgl::LatLng(input.Get(env, i), input.Get(env, i + 1)); + latLngs.push_back(latLng); + } + + std::vector buffer; + buffer.reserve(len); + std::vector coordinates = map->pixelsForLatLngs(latLngs); + for (std::size_t i = 0; i < len / 2; i++) { + buffer.push_back(coordinates[i].x * pixelRatio_); + buffer.push_back(coordinates[i].y * pixelRatio_); + } + + output.SetRegion>(env, 0, buffer); +} + +jni::Local> NativeMapView::latLngForPixel(JNIEnv& env, jfloat x, jfloat y) { + return LatLng::New(env, map->latLngForPixel(mbgl::ScreenCoordinate(x, y))); +} + +void NativeMapView::latLngsForPixels(JNIEnv& env, + const jni::Array& input, + jni::Array& output, + jfloat pixelRatio_) { + jni::NullCheck(env, &input); + std::size_t len = input.Length(env); + + std::vector coordinates; + coordinates.reserve(len); + + for (std::size_t i = 0; i < len; i += 2) { + auto coordinate = mbgl::ScreenCoordinate(input.Get(env, i) / pixelRatio_, input.Get(env, i + 1) / pixelRatio_); + coordinates.push_back(coordinate); + } + + std::vector buffer; + buffer.reserve(len); + std::vector latLngs = map->latLngsForPixels(coordinates); + for (std::size_t i = 0; i < len / 2; i++) { + buffer.push_back(latLngs[i].latitude()); + buffer.push_back(latLngs[i].longitude()); + } + + output.SetRegion>(env, 0, buffer); +} + +jni::Local> NativeMapView::addPolylines(JNIEnv& env, const jni::Array>& polylines) { + NullCheck(env, &polylines); + std::size_t len = polylines.Length(env); + + std::vector ids; + ids.reserve(len); + + for (std::size_t i = 0; i < len; i++) { + mbgl::LineAnnotation annotation = Polyline::toAnnotation(env, polylines.Get(env, i)); + ids.push_back(map->addAnnotation(annotation)); + } + + auto result = jni::Array::New(env, len); + result.SetRegion>(env, 0, ids); + + return result; +} + + +jni::Local> NativeMapView::addPolygons(JNIEnv& env, const jni::Array>& polygons) { + NullCheck(env, &polygons); + std::size_t len = polygons.Length(env); + + std::vector ids; + ids.reserve(len); + + for (std::size_t i = 0; i < len; i++) { + mbgl::FillAnnotation annotation = Polygon::toAnnotation(env, polygons.Get(env, i)); + ids.push_back(map->addAnnotation(annotation)); + } + + auto result = jni::Array::New(env, len); + result.SetRegion>(env, 0, ids); + + return result; +} + +void NativeMapView::updatePolyline(JNIEnv& env, jlong polylineId, const jni::Object& polyline) { + mbgl::LineAnnotation annotation = Polyline::toAnnotation(env, polyline); + map->updateAnnotation(polylineId, annotation); +} + +void NativeMapView::updatePolygon(JNIEnv& env, jlong polygonId, const jni::Object& polygon) { + mbgl::FillAnnotation annotation = Polygon::toAnnotation(env, polygon); + map->updateAnnotation(polygonId, annotation); +} + +void NativeMapView::removeAnnotations(JNIEnv& env, const jni::Array& ids) { + NullCheck(env, &ids); + std::size_t len = ids.Length(env); + auto elements = jni::GetArrayElements(env, *ids); + jlong* jids = std::get<0>(elements).get(); + + for (std::size_t i = 0; i < len; i++) { + if(jids[i] == -1L) { + continue; + } + map->removeAnnotation(jids[i]); + } +} + +void NativeMapView::addAnnotationIcon(JNIEnv& env, const jni::String& symbol, jint w, jint h, jfloat scale, const jni::Array& jpixels) { + const std::string symbolName = jni::Make(env, symbol); + + NullCheck(env, &jpixels); + std::size_t size = jpixels.Length(env); + + mbgl::PremultipliedImage premultipliedImage({ static_cast(w), static_cast(h) }); + if (premultipliedImage.bytes() != uint32_t(size)) { + throw mbgl::util::StyleImageException("Annotation icon image pixel count mismatch"); + } + + jni::GetArrayRegion(env, *jpixels, 0, size, reinterpret_cast(premultipliedImage.data.get())); + map->addAnnotationImage(std::make_unique( + symbolName, std::move(premultipliedImage), float(scale))); +} + +void NativeMapView::removeAnnotationIcon(JNIEnv& env, const jni::String& symbol) { + const std::string symbolName = jni::Make(env, symbol); + map->removeAnnotationImage(symbolName); +} + +jdouble NativeMapView::getTopOffsetPixelsForAnnotationSymbol(JNIEnv& env, const jni::String& symbolName) { + return map->getTopOffsetPixelsForAnnotationImage(jni::Make(env, symbolName)); +} + +jni::Local> NativeMapView::getTransitionOptions(JNIEnv& env) { + const auto transitionOptions = map->getStyle().getTransitionOptions(); + const auto duration = std::chrono::duration_cast(transitionOptions.duration.value_or(mbgl::Duration::zero())).count(); + const auto delay = std::chrono::duration_cast(transitionOptions.delay.value_or(mbgl::Duration::zero())).count(); + const auto enablePlacementTransitions = (jboolean) transitionOptions.enablePlacementTransitions; + return TransitionOptions::fromTransitionOptions(env, duration, delay, enablePlacementTransitions); +} + +void NativeMapView::setTransitionOptions(JNIEnv& env, const jni::Object& options) { + const mbgl::style::TransitionOptions transitionOptions( + Duration(mbgl::Milliseconds(TransitionOptions::getDuration(env, options))), + Duration(mbgl::Milliseconds(TransitionOptions::getDelay(env, options))), + TransitionOptions::isEnablePlacementTransitions(env, options) + ); + map->getStyle().setTransitionOptions(transitionOptions); +} + +jni::Local> NativeMapView::queryPointAnnotations(JNIEnv& env, const jni::Object& rect) { + using namespace mbgl::style; + using namespace mbgl::style::conversion; + + // Convert input + mbgl::ScreenBox box = { + { RectF::getLeft(env, rect), RectF::getTop(env, rect) }, + { RectF::getRight(env, rect), RectF::getBottom(env, rect) }, + }; + + // Assume only points for now + mbgl::AnnotationIDs ids = rendererFrontend->queryPointAnnotations(box); + + // Convert result + std::vector longIds(ids.begin(), ids.end()); + auto result = jni::Array::New(env, ids.size()); + result.SetRegion>(env, 0, longIds); + + return result; +} + +jni::Local> NativeMapView::queryShapeAnnotations(JNIEnv& env, const jni::Object& rect) { + using namespace mbgl::style; + using namespace mbgl::style::conversion; + + // Convert input + mbgl::ScreenBox box = { + {RectF::getLeft(env, rect), RectF::getTop(env, rect)}, + {RectF::getRight(env, rect), RectF::getBottom(env, rect)}, + }; + + mbgl::AnnotationIDs ids = rendererFrontend->queryShapeAnnotations(box); + + // Convert result + std::vector longIds(ids.begin(), ids.end()); + auto result = jni::Array::New(env, ids.size()); + result.SetRegion>(env, 0, longIds); + + return result; +} + +jni::Local>> NativeMapView::queryRenderedFeaturesForPoint(JNIEnv& env, jni::jfloat x, jni::jfloat y, + const jni::Array& layerIds, + const jni::Array>& jfilter) { + using namespace mbgl::android::conversion; + using namespace mbgl::android::geojson; + + mbgl::optional> layers; + if (layerIds && layerIds.Length(env) > 0) { + layers = android::conversion::toVector(env, layerIds); + } + mapbox::geometry::point point = {x, y}; + + return Feature::convert( + env, + rendererFrontend->queryRenderedFeatures(point, { layers, toFilter(env, jfilter) })); +} + +jni::Local>> NativeMapView::queryRenderedFeaturesForBox(JNIEnv& env, jni::jfloat left, jni::jfloat top, + jni::jfloat right, jni::jfloat bottom, const jni::Array& layerIds, + const jni::Array>& jfilter) { + using namespace mbgl::android::conversion; + using namespace mbgl::android::geojson; + + mbgl::optional> layers; + if (layerIds && layerIds.Length(env) > 0) { + layers = toVector(env, layerIds); + } + mapbox::geometry::box box = { + mapbox::geometry::point{ left, top}, + mapbox::geometry::point{ right, bottom } + }; + + return Feature::convert( + env, + rendererFrontend->queryRenderedFeatures(box, { layers, toFilter(env, jfilter) })); +} + +jni::Local> NativeMapView::getLight(JNIEnv& env) { + mbgl::style::Light* light = map->getStyle().getLight(); + if (light) { + return Light::createJavaLightPeer(env, *map, *light); + } else { + return jni::Local>(); + } +} + +jni::Local>> NativeMapView::getLayers(JNIEnv& env) { + + // Get the core layers + std::vector layers = map->getStyle().getLayers(); + + // Convert + auto jLayers = jni::Array>::New(env, layers.size()); + int index = 0; + for (auto layer : layers) { + jLayers.Set(env, index, LayerManagerAndroid::get()->createJavaLayerPeer(env, *layer)); + index++; + } + + return jLayers; +} + +jni::Local> NativeMapView::getLayer(JNIEnv& env, const jni::String& layerId) { + + // Find the layer + mbgl::style::Layer* coreLayer = map->getStyle().getLayer(jni::Make(env, layerId)); + if (!coreLayer) { + mbgl::Log::Debug(mbgl::Event::JNI, "No layer found"); + return jni::Local>(); + } + + // Create and return the layer's native peer + return LayerManagerAndroid::get()->createJavaLayerPeer(env, *coreLayer); +} + +void NativeMapView::addLayer(JNIEnv& env, jlong nativeLayerPtr, const jni::String& before) { + assert(nativeLayerPtr != 0); + + Layer *layer = reinterpret_cast(nativeLayerPtr); + try { + layer->addToStyle( + map->getStyle(), + before ? mbgl::optional(jni::Make(env, before)) : mbgl::optional()); + } catch (const std::runtime_error& error) { + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); + } +} + +void NativeMapView::addLayerAbove(JNIEnv& env, jlong nativeLayerPtr, const jni::String& above) { + assert(nativeLayerPtr != 0); + + Layer *layer = reinterpret_cast(nativeLayerPtr); + + // Find the sibling + auto layers = map->getStyle().getLayers(); + auto siblingId = jni::Make(env, above); + + size_t index = 0; + for (auto l : layers) { + if (l->getID() == siblingId) { + break; + } + index++; + } + + // Check if we found a sibling to place before + mbgl::optional before; + if (index + 1 > layers.size()) { + // Not found + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), + std::string("Could not find layer: ").append(siblingId).c_str()); + return; + } else if (index + 1 < layers.size()) { + // Place before the sibling + before = { layers.at(index + 1)->getID() }; + } + + // Add the layer + try { + layer->addToStyle(map->getStyle(), before); + } catch (const std::runtime_error& error) { + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); + } +} + +void NativeMapView::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint index) { + assert(nativeLayerPtr != 0); + + Layer *layer = reinterpret_cast(nativeLayerPtr); + auto layers = map->getStyle().getLayers(); + + // Check index + int numLayers = layers.size() - 1; + if (index > numLayers || index < 0) { + Log::Error(Event::JNI, "Index out of range: %i", index); + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), + std::string("Invalid index").c_str()); + return; + } + + // Insert it below the current at that index + try { + layer->addToStyle(map->getStyle(), layers.at(index)->getID()); + } catch (const std::runtime_error& error) { + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); + } +} + + +/** + * Remove layer at index. + */ +jni::jboolean NativeMapView::removeLayerAt(JNIEnv& env, jni::jint index) { + auto layers = map->getStyle().getLayers(); + + // Check index + int numLayers = layers.size() - 1; + if (index > numLayers || index < 0) { + Log::Warning(Event::JNI, "Index out of range: %i", index); + return jni::jni_false; + } + + std::unique_ptr coreLayer = map->getStyle().removeLayer(layers.at(index)->getID()); + if (coreLayer) { + jni::Local> layerObj = + LayerManagerAndroid::get()->createJavaLayerPeer(env, std::move(coreLayer)); + return jni::jni_true; + } + return jni::jni_false; +} + +/** + * Remove with wrapper object id. Ownership is transferred back to the wrapper + */ +jni::jboolean NativeMapView::removeLayer(JNIEnv&, jlong layerPtr) { + assert(layerPtr != 0); + + mbgl::android::Layer *layer = reinterpret_cast(layerPtr); + std::unique_ptr coreLayer = map->getStyle().removeLayer(layer->get().getID()); + if (coreLayer) { + layer->setLayer(std::move(coreLayer)); + return jni::jni_true; + } + return jni::jni_false; +} + +jni::Local>> NativeMapView::getSources(JNIEnv& env) { + // Get the core sources + std::vector sources = map->getStyle().getSources(); + + // Convert + auto jSources = jni::Array>::New(env, sources.size()); + int index = 0; + for (auto source : sources) { + jSources.Set(env, index, Source::peerForCoreSource(env, *source, *rendererFrontend)); + index++; + } + + return jSources; +} + +jni::Local> NativeMapView::getSource(JNIEnv& env, const jni::String& sourceId) { + // Find the source + mbgl::style::Source* coreSource = map->getStyle().getSource(jni::Make(env, sourceId)); + if (!coreSource) { + mbgl::Log::Debug(mbgl::Event::JNI, "No source found"); + return jni::Local>(); + } + + // Create and return the source's native peer + return jni::NewLocal(env, Source::peerForCoreSource(env, *coreSource, *rendererFrontend)); +} + +void NativeMapView::addSource(JNIEnv& env, const jni::Object& obj, jlong sourcePtr) { + assert(sourcePtr != 0); + + Source *source = reinterpret_cast(sourcePtr); + try { + source->addToMap(env, obj, *map, *rendererFrontend); + } catch (const std::runtime_error& error) { + jni::ThrowNew(env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/sources/CannotAddSourceException"), error.what()); + } +} + +jni::jboolean NativeMapView::removeSource(JNIEnv& env, const jni::Object& obj, jlong sourcePtr) { + assert(sourcePtr != 0); + + mbgl::android::Source *source = reinterpret_cast(sourcePtr); + if (source->removeFromMap(env, obj, *map)) { + source->releaseJavaPeer(); + return jni::jni_true; + } + + return jni::jni_false; +} + +void NativeMapView::addImage(JNIEnv& env, const jni::String& name, const jni::Object& bitmap, jni::jfloat scale, jni::jboolean sdf) { + jni::NullCheck(env, &bitmap); + mbgl::PremultipliedImage premultipliedImage = Bitmap::GetImage(env, bitmap); + + map->getStyle().addImage(std::make_unique( + jni::Make(env, name), + std::move(premultipliedImage), + float(scale), + sdf) + ); +} + +void NativeMapView::addImages(JNIEnv& env, const jni::Array>& jimages) { + jni::NullCheck(env, &jimages); + std::size_t len = jimages.Length(env); + + for (std::size_t i = 0; i < len; i++) { + auto image = mbgl::android::Image::getImage(env, jimages.Get(env, i)); + map->getStyle().addImage(std::make_unique(image)); + } +} + +void NativeMapView::removeImage(JNIEnv& env, const jni::String& name) { + map->getStyle().removeImage(jni::Make(env, name)); +} + +jni::Local> NativeMapView::getImage(JNIEnv& env, const jni::String& name) { + if (auto image = map->getStyle().getImage(jni::Make(env, name))) { + return Bitmap::CreateBitmap(env, image->getImage()); + } + return jni::Local>(); +} + +void NativeMapView::setPrefetchTiles(JNIEnv&, jni::jboolean enable) { + map->setPrefetchZoomDelta(enable ? util::DEFAULT_PREFETCH_ZOOM_DELTA : uint8_t(0)); +} + +jni::jboolean NativeMapView::getPrefetchTiles(JNIEnv&) { + return jni::jboolean(map->getPrefetchZoomDelta() > 0); +} + +void NativeMapView::setPrefetchZoomDelta(JNIEnv&, jni::jint delta) { + map->setPrefetchZoomDelta(uint8_t(delta)); +} + +jni::jint NativeMapView::getPrefetchZoomDelta(JNIEnv&) { + return jni::jint(map->getPrefetchZoomDelta()); +} + +mbgl::Map& NativeMapView::getMap() { + return *map; +} + +void NativeMapView::triggerRepaint(JNIEnv&) { + assert(map); + map->triggerRepaint(); +} + +// Static methods // + +void NativeMapView::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer&, + const jni::Object&, + const jni::Object&, + jni::jfloat, + jni::jboolean>, + "nativeInitialize", + "nativeDestroy", + METHOD(&NativeMapView::resizeView, "nativeResizeView"), + METHOD(&NativeMapView::getStyleUrl, "nativeGetStyleUrl"), + METHOD(&NativeMapView::setStyleUrl, "nativeSetStyleUrl"), + METHOD(&NativeMapView::getStyleJson, "nativeGetStyleJson"), + METHOD(&NativeMapView::setStyleJson, "nativeSetStyleJson"), + METHOD(&NativeMapView::cancelTransitions, "nativeCancelTransitions"), + METHOD(&NativeMapView::setGestureInProgress, "nativeSetGestureInProgress"), + METHOD(&NativeMapView::moveBy, "nativeMoveBy"), + METHOD(&NativeMapView::jumpTo, "nativeJumpTo"), + METHOD(&NativeMapView::easeTo, "nativeEaseTo"), + METHOD(&NativeMapView::flyTo, "nativeFlyTo"), + METHOD(&NativeMapView::getLatLng, "nativeGetLatLng"), + METHOD(&NativeMapView::setLatLng, "nativeSetLatLng"), + METHOD(&NativeMapView::getCameraForLatLngBounds, "nativeGetCameraForLatLngBounds"), + METHOD(&NativeMapView::getCameraForGeometry, "nativeGetCameraForGeometry"), + METHOD(&NativeMapView::setReachability, "nativeSetReachability"), + METHOD(&NativeMapView::resetPosition, "nativeResetPosition"), + METHOD(&NativeMapView::getPitch, "nativeGetPitch"), + METHOD(&NativeMapView::setPitch, "nativeSetPitch"), + METHOD(&NativeMapView::getZoom, "nativeGetZoom"), + METHOD(&NativeMapView::setZoom, "nativeSetZoom"), + METHOD(&NativeMapView::resetZoom, "nativeResetZoom"), + METHOD(&NativeMapView::setMinZoom, "nativeSetMinZoom"), + METHOD(&NativeMapView::getMinZoom, "nativeGetMinZoom"), + METHOD(&NativeMapView::setMaxZoom, "nativeSetMaxZoom"), + METHOD(&NativeMapView::getMaxZoom, "nativeGetMaxZoom"), + METHOD(&NativeMapView::setMinPitch, "nativeSetMinPitch"), + METHOD(&NativeMapView::getMinPitch, "nativeGetMinPitch"), + METHOD(&NativeMapView::setMaxPitch, "nativeSetMaxPitch"), + METHOD(&NativeMapView::getMaxPitch, "nativeGetMaxPitch"), + METHOD(&NativeMapView::rotateBy, "nativeRotateBy"), + METHOD(&NativeMapView::setBearing, "nativeSetBearing"), + METHOD(&NativeMapView::setBearingXY, "nativeSetBearingXY"), + METHOD(&NativeMapView::getBearing, "nativeGetBearing"), + METHOD(&NativeMapView::resetNorth, "nativeResetNorth"), + METHOD(&NativeMapView::setVisibleCoordinateBounds, "nativeSetVisibleCoordinateBounds"), + METHOD(&NativeMapView::scheduleSnapshot, "nativeTakeSnapshot"), + METHOD(&NativeMapView::getCameraPosition, "nativeGetCameraPosition"), + METHOD(&NativeMapView::updateMarker, "nativeUpdateMarker"), + METHOD(&NativeMapView::addMarkers, "nativeAddMarkers"), + METHOD(&NativeMapView::setDebug, "nativeSetDebug"), + METHOD(&NativeMapView::getDebug, "nativeGetDebug"), + METHOD(&NativeMapView::isFullyLoaded, "nativeIsFullyLoaded"), + METHOD(&NativeMapView::onLowMemory, "nativeOnLowMemory"), + METHOD(&NativeMapView::getMetersPerPixelAtLatitude, "nativeGetMetersPerPixelAtLatitude"), + METHOD(&NativeMapView::projectedMetersForLatLng, "nativeProjectedMetersForLatLng"), + METHOD(&NativeMapView::pixelForLatLng, "nativePixelForLatLng"), + METHOD(&NativeMapView::pixelsForLatLngs, "nativePixelsForLatLngs"), + METHOD(&NativeMapView::getVisibleCoordinateBounds, "nativeGetVisibleCoordinateBounds"), + METHOD(&NativeMapView::latLngForProjectedMeters, "nativeLatLngForProjectedMeters"), + METHOD(&NativeMapView::latLngForPixel, "nativeLatLngForPixel"), + METHOD(&NativeMapView::latLngsForPixels, "nativeLatLngsForPixels"), + METHOD(&NativeMapView::addPolylines, "nativeAddPolylines"), + METHOD(&NativeMapView::addPolygons, "nativeAddPolygons"), + METHOD(&NativeMapView::updatePolyline, "nativeUpdatePolyline"), + METHOD(&NativeMapView::updatePolygon, "nativeUpdatePolygon"), + METHOD(&NativeMapView::removeAnnotations, "nativeRemoveAnnotations"), + METHOD(&NativeMapView::addAnnotationIcon, "nativeAddAnnotationIcon"), + METHOD(&NativeMapView::removeAnnotationIcon, "nativeRemoveAnnotationIcon"), + METHOD(&NativeMapView::getTopOffsetPixelsForAnnotationSymbol, "nativeGetTopOffsetPixelsForAnnotationSymbol"), + METHOD(&NativeMapView::getTransitionOptions, "nativeGetTransitionOptions"), + METHOD(&NativeMapView::setTransitionOptions, "nativeSetTransitionOptions"), + METHOD(&NativeMapView::queryPointAnnotations, "nativeQueryPointAnnotations"), + METHOD(&NativeMapView::queryShapeAnnotations, "nativeQueryShapeAnnotations"), + METHOD(&NativeMapView::queryRenderedFeaturesForPoint, "nativeQueryRenderedFeaturesForPoint"), + METHOD(&NativeMapView::queryRenderedFeaturesForBox, "nativeQueryRenderedFeaturesForBox"), + METHOD(&NativeMapView::getLight, "nativeGetLight"), + METHOD(&NativeMapView::getLayers, "nativeGetLayers"), + METHOD(&NativeMapView::getLayer, "nativeGetLayer"), + METHOD(&NativeMapView::addLayer, "nativeAddLayer"), + METHOD(&NativeMapView::addLayerAbove, "nativeAddLayerAbove"), + METHOD(&NativeMapView::addLayerAt, "nativeAddLayerAt"), + METHOD(&NativeMapView::removeLayerAt, "nativeRemoveLayerAt"), + METHOD(&NativeMapView::removeLayer, "nativeRemoveLayer"), + METHOD(&NativeMapView::getSources, "nativeGetSources"), + METHOD(&NativeMapView::getSource, "nativeGetSource"), + METHOD(&NativeMapView::addSource, "nativeAddSource"), + METHOD(&NativeMapView::removeSource, "nativeRemoveSource"), + METHOD(&NativeMapView::addImage, "nativeAddImage"), + METHOD(&NativeMapView::addImages, "nativeAddImages"), + METHOD(&NativeMapView::removeImage, "nativeRemoveImage"), + METHOD(&NativeMapView::getImage, "nativeGetImage"), + METHOD(&NativeMapView::setLatLngBounds, "nativeSetLatLngBounds"), + METHOD(&NativeMapView::setPrefetchTiles, "nativeSetPrefetchTiles"), + METHOD(&NativeMapView::getPrefetchTiles, "nativeGetPrefetchTiles"), + METHOD(&NativeMapView::setPrefetchZoomDelta, "nativeSetPrefetchZoomDelta"), + METHOD(&NativeMapView::getPrefetchZoomDelta, "nativeGetPrefetchZoomDelta"), + METHOD(&NativeMapView::triggerRepaint, "nativeTriggerRepaint")); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/native_map_view.hpp b/MapboxGLAndroidSDK/src/cpp/native_map_view.hpp new file mode 100644 index 000000000..be632e242 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/native_map_view.hpp @@ -0,0 +1,279 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +namespace mbgl { +namespace android { + +class AndroidRendererFrontend; +class FileSource; +class MapRenderer; + +class NativeMapView : public MapObserver { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/maps/NativeMapView"; }; + + static void registerNative(jni::JNIEnv&); + + NativeMapView(jni::JNIEnv&, + const jni::Object&, + const jni::Object&, + const jni::Object&, + 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 getStyleUrl(jni::JNIEnv&); + + void setStyleUrl(jni::JNIEnv&, const jni::String&); + + jni::Local getStyleJson(jni::JNIEnv&); + + void setStyleJson(jni::JNIEnv&, const jni::String&); + + void setLatLngBounds(jni::JNIEnv&, const jni::Object&); + + 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&); + + void easeTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble, const jni::Array&, jni::jboolean); + + void flyTo(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jdouble, jni::jlong, jni::jdouble, jni::jdouble, const jni::Array&); + + jni::Local> getLatLng(JNIEnv&); + + void setLatLng(jni::JNIEnv&, jni::jdouble, jni::jdouble, const jni::Array&, jni::jlong); + + jni::Local> getCameraForLatLngBounds(jni::JNIEnv&, const jni::Object&, double top, double left, double bottom, double right, double bearing, double tilt); + + jni::Local> getCameraForGeometry(jni::JNIEnv&, const jni::Object&, 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>&, const jni::Object&, jni::jdouble, jni::jlong); + + void getVisibleCoordinateBounds(JNIEnv& env, jni::Array& output); + + void scheduleSnapshot(jni::JNIEnv&); + + jni::Local> getCameraPosition(jni::JNIEnv&); + + void updateMarker(jni::JNIEnv&, jni::jlong, jni::jdouble, jni::jdouble, const jni::String&); + + jni::Local> addMarkers(jni::JNIEnv&, const jni::Array>&); + + 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> projectedMetersForLatLng(JNIEnv&, jni::jdouble, jni::jdouble); + + jni::Local> pixelForLatLng(JNIEnv&, jdouble, jdouble); + + void pixelsForLatLngs(JNIEnv&, const jni::Array&, jni::Array&, jfloat); + + jni::Local> latLngForProjectedMeters(JNIEnv&, jdouble, jdouble); + + jni::Local> latLngForPixel(JNIEnv&, jfloat, jfloat); + + void latLngsForPixels(JNIEnv&, const jni::Array&, jni::Array&, jfloat); + + jni::Local> addPolylines(JNIEnv&, const jni::Array>&); + + jni::Local> addPolygons(JNIEnv&, const jni::Array>&); + + void updatePolyline(JNIEnv&, jlong, const jni::Object&); + + void updatePolygon(JNIEnv&, jlong, const jni::Object&); + + void removeAnnotations(JNIEnv&, const jni::Array&); + + void addAnnotationIcon(JNIEnv&, const jni::String&, jint, jint, jfloat, const jni::Array&); + + void removeAnnotationIcon(JNIEnv&, const jni::String&); + + jni::jdouble getTopOffsetPixelsForAnnotationSymbol(JNIEnv&, const jni::String&); + + jni::Local> getTransitionOptions(JNIEnv&); + + void setTransitionOptions(JNIEnv&, const jni::Object&); + + jni::Local> queryPointAnnotations(JNIEnv&, const jni::Object&); + + jni::Local> queryShapeAnnotations(JNIEnv&, const jni::Object&); + + jni::Local>> queryRenderedFeaturesForPoint(JNIEnv&, jni::jfloat, jni::jfloat, + const jni::Array&, + const jni::Array>& jfilter); + + jni::Local>> queryRenderedFeaturesForBox(JNIEnv&, jni::jfloat, jni::jfloat, jni::jfloat, + jni::jfloat, const jni::Array&, + const jni::Array>& jfilter); + + jni::Local> getLight(JNIEnv&); + + jni::Local>> getLayers(JNIEnv&); + + jni::Local> 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>> getSources(JNIEnv&); + + jni::Local> getSource(JNIEnv&, const jni::String&); + + void addSource(JNIEnv&, const jni::Object&, jlong nativePtr); + + jni::jboolean removeSource(JNIEnv&, const jni::Object&, jlong nativePtr); + + void addImage(JNIEnv&, const jni::String&, const jni::Object& bitmap, jni::jfloat, jni::jboolean); + + void addImages(JNIEnv&, const jni::Array>&); + + void removeImage(JNIEnv&, const jni::String&); + + jni::Local> 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 rendererFrontend; + + JavaVM *vm = nullptr; + jni::WeakReference> 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 map; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/offline/offline_manager.cpp b/MapboxGLAndroidSDK/src/cpp/offline/offline_manager.cpp new file mode 100644 index 000000000..74176b0e4 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/offline/offline_manager.cpp @@ -0,0 +1,335 @@ +#include "offline_manager.hpp" + +#include +#include +#include + +#include "attach_env.hpp" + +namespace mbgl { +namespace android { + +namespace { +// Reattach, the callback comes from a different thread +void handleException(std::exception_ptr exception, + const jni::Object& callback, + android::UniqueEnv env = android::AttachEnv()) { + if (exception) { + OfflineManager::FileSourceCallback::onError( + *env, callback, jni::Make(*env, mbgl::util::toString(exception))); + } else { + OfflineManager::FileSourceCallback::onSuccess(*env, callback); + } +} +} // namespace + +// OfflineManager // +OfflineManager::OfflineManager(jni::JNIEnv& env, const jni::Object& jFileSource) + : fileSource(std::static_pointer_cast( + std::shared_ptr(mbgl::FileSourceManager::get()->getFileSource( + mbgl::FileSourceType::Database, FileSource::getSharedResourceOptions(env, jFileSource))))) { + if (!fileSource) { + ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Offline functionality is disabled."); + } +} + +OfflineManager::~OfflineManager() {} + +void OfflineManager::setOfflineMapboxTileCountLimit(jni::JNIEnv&, jni::jlong limit) { + fileSource->setOfflineMapboxTileCountLimit(limit); +} + +void OfflineManager::listOfflineRegions(jni::JNIEnv& env_, const jni::Object& jFileSource_, const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + auto globalFilesource = jni::NewGlobal(env_, jFileSource_); + + fileSource->listOfflineRegions([ + //Keep a shared ptr to a global reference of the callback and file source so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback)), + jFileSource = std::make_shared(std::move(globalFilesource)) + ](mbgl::expected regions) mutable { + + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (regions) { + OfflineManager::ListOfflineRegionsCallback::onList( + *env, *jFileSource, *callback, *regions); + } else { + OfflineManager::ListOfflineRegionsCallback::onError( + *env, *callback, regions.error()); + } + }); +} + +void OfflineManager::createOfflineRegion(jni::JNIEnv& env_, + const jni::Object& jFileSource_, + const jni::Object& definition_, + const jni::Array& metadata_, + const jni::Object& callback_) { + // Convert + auto definition = OfflineRegionDefinition::getDefinition(env_, definition_); + + mbgl::OfflineRegionMetadata metadata; + if (metadata_) { + metadata = OfflineRegion::metadata(env_, metadata_); + } + + auto globalCallback = jni::NewGlobal(env_, callback_); + auto globalFilesource = jni::NewGlobal(env_, jFileSource_); + + // Create region + fileSource->createOfflineRegion(definition, metadata, [ + //Keep a shared ptr to a global reference of the callback and file source so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback)), + jFileSource = std::make_shared(std::move(globalFilesource)) + ](mbgl::expected region) mutable { + + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (region) { + OfflineManager::CreateOfflineRegionCallback::onCreate( + *env, *jFileSource, *callback, *region + ); + } else { + OfflineManager::CreateOfflineRegionCallback::onError( + *env, *callback, region.error()); + } + }); +} + +void OfflineManager::mergeOfflineRegions(jni::JNIEnv& env_, const jni::Object& jFileSource_, + const jni::String& jString_, + const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + auto globalFilesource = jni::NewGlobal(env_, jFileSource_); + + auto path = jni::Make(env_, jString_); + fileSource->mergeOfflineRegions(path, [ + //Keep a shared ptr to a global reference of the callback and file source so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback)), + jFileSource = std::make_shared(std::move(globalFilesource)) + ](mbgl::expected regions) mutable { + + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (regions) { + OfflineManager::MergeOfflineRegionsCallback::onMerge( + *env, *jFileSource, *callback, *regions); + } else { + OfflineManager::MergeOfflineRegionsCallback::onError( + *env, *callback, regions.error()); + } + }); +} + +void OfflineManager::resetDatabase(jni::JNIEnv& env_, const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + + fileSource->resetDatabase( + [ + // Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback))]( + std::exception_ptr exception) mutable { handleException(exception, *callback); }); +} + +void OfflineManager::packDatabase(jni::JNIEnv& env_, const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + + fileSource->packDatabase( + [ + // Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback))]( + std::exception_ptr exception) mutable { handleException(exception, *callback); }); +} + +void OfflineManager::invalidateAmbientCache(jni::JNIEnv& env_, const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + + fileSource->invalidateAmbientCache( + [ + // Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback))]( + std::exception_ptr exception) mutable { handleException(exception, *callback); }); +} + +void OfflineManager::clearAmbientCache(jni::JNIEnv& env_, const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + + fileSource->clearAmbientCache( + [ + // Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback))]( + std::exception_ptr exception) mutable { handleException(exception, *callback); }); +} + +void OfflineManager::setMaximumAmbientCacheSize(jni::JNIEnv& env_, const jni::jlong size_, const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + + fileSource->setMaximumAmbientCacheSize( + size_, + [ + // Keep a shared ptr to a global reference of the callback so they are not GC'd in the meanwhile + callback = std::make_shared(std::move(globalCallback))]( + std::exception_ptr exception) mutable { handleException(exception, *callback); }); +} + +void OfflineManager::runPackDatabaseAutomatically(jni::JNIEnv&, jboolean autopack) { + fileSource->runPackDatabaseAutomatically(autopack); +} + +// FileSource::FileSourceCallback // + +void OfflineManager::FileSourceCallback::onSuccess(jni::JNIEnv& env, + const jni::Object& callback) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onSuccess"); + callback.Call(env, method); +} + +void OfflineManager::FileSourceCallback::onError(jni::JNIEnv& env, + const jni::Object& callback, + const jni::String& message) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onError"); + callback.Call(env, method, message); +} + +void OfflineManager::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); + jni::Class::Singleton(env); + jni::Class::Singleton(env); + jni::Class::Singleton(env); + + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer&>, + "initialize", + "finalize", + METHOD(&OfflineManager::setOfflineMapboxTileCountLimit, "setOfflineMapboxTileCountLimit"), + METHOD(&OfflineManager::listOfflineRegions, "listOfflineRegions"), + METHOD(&OfflineManager::createOfflineRegion, "createOfflineRegion"), + METHOD(&OfflineManager::mergeOfflineRegions, "mergeOfflineRegions"), + METHOD(&OfflineManager::resetDatabase, "nativeResetDatabase"), + METHOD(&OfflineManager::packDatabase, "nativePackDatabase"), + METHOD(&OfflineManager::invalidateAmbientCache, "nativeInvalidateAmbientCache"), + METHOD(&OfflineManager::clearAmbientCache, "nativeClearAmbientCache"), + METHOD(&OfflineManager::setMaximumAmbientCacheSize, "nativeSetMaximumAmbientCacheSize"), + METHOD(&OfflineManager::runPackDatabaseAutomatically, "runPackDatabaseAutomatically"), + METHOD(&OfflineManager::putResourceWithUrl, "putResourceWithUrl")); +} + +// OfflineManager::ListOfflineRegionsCallback // + +void OfflineManager::ListOfflineRegionsCallback::onError(jni::JNIEnv& env, + const jni::Object& callback, + std::exception_ptr error) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onError"); + + callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); +} + +void OfflineManager::ListOfflineRegionsCallback::onList(jni::JNIEnv& env, + const jni::Object& jFileSource, + const jni::Object& callback, + mbgl::OfflineRegions& regions) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod>)>(env, "onList"); + + std::size_t index = 0; + auto jregions = jni::Array>::New(env, regions.size()); + for (auto& region : regions) { + jregions.Set(env, index, OfflineRegion::New(env, jFileSource, std::move(region))); + index++; + } + + callback.Call(env, method, jregions); +} + +// OfflineManager::CreateOfflineRegionCallback // + +void OfflineManager::CreateOfflineRegionCallback::onError(jni::JNIEnv& env, + const jni::Object& callback, + std::exception_ptr error) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onError"); + + callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); +} + +void OfflineManager::CreateOfflineRegionCallback::onCreate(jni::JNIEnv& env, + const jni::Object& jFileSource, + const jni::Object& callback, + mbgl::OfflineRegion& region) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod)>(env, "onCreate"); + + callback.Call(env, method, OfflineRegion::New(env, jFileSource, std::move(region))); +} + +// OfflineManager::MergeOfflineRegionsCallback // + +void OfflineManager::MergeOfflineRegionsCallback::onError(jni::JNIEnv& env, + const jni::Object& callback, + std::exception_ptr error) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onError"); + + callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); +} + +void OfflineManager::MergeOfflineRegionsCallback::onMerge(jni::JNIEnv& env, + const jni::Object& jFileSource, + const jni::Object& callback, + mbgl::OfflineRegions& regions) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod>)>(env, "onMerge"); + + std::size_t index = 0; + auto jregions = jni::Array>::New(env, regions.size()); + for (auto& region : regions) { + jregions.Set(env, index, OfflineRegion::New(env, jFileSource, std::move(region))); + index++; + } + + callback.Call(env, method, jregions); +} + +void OfflineManager::putResourceWithUrl(jni::JNIEnv& env, + const jni::String& url_, + const jni::Array& arr, + jlong modified, + jlong expires, + const jni::String& eTag_, + jboolean mustRevalidate) { + auto url = jni::Make(env, url_); + auto data = std::make_shared(arr.Length(env), char()); + jni::GetArrayRegion(env, *arr, 0, data->size(), reinterpret_cast(&(*data)[0])); + mbgl::Resource resource(mbgl::Resource::Kind::Unknown, url); + mbgl::Response response; + response.data = data; + response.mustRevalidate = mustRevalidate; + if (eTag_) { + response.etag = jni::Make(env, eTag_); + } + if (modified > 0) { + response.modified = Timestamp(mbgl::Seconds(modified)); + } + if (expires > 0) { + response.expires = Timestamp(mbgl::Seconds(expires)); + } + + fileSource->put(resource, response); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/offline/offline_manager.hpp b/MapboxGLAndroidSDK/src/cpp/offline/offline_manager.hpp new file mode 100644 index 000000000..84111a742 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/offline/offline_manager.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include +#include + +#include "../file_source.hpp" +#include "offline_region.hpp" +#include "offline_region_definition.hpp" +#include "../java_types.hpp" + +#include + +namespace mbgl { + +namespace android { + +class OfflineManager { +public: + + class ListOfflineRegionsCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$ListOfflineRegionsCallback";} + + static void onError(jni::JNIEnv&, const jni::Object&, std::exception_ptr); + + static void onList(jni::JNIEnv&, + const jni::Object&, + const jni::Object&, + mbgl::OfflineRegions&); + }; + + class CreateOfflineRegionCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$CreateOfflineRegionCallback"; } + + static void onError(jni::JNIEnv&, const jni::Object&, std::exception_ptr); + + static void onCreate(jni::JNIEnv&, + const jni::Object&, + const jni::Object&, + mbgl::OfflineRegion&); + }; + + class MergeOfflineRegionsCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$MergeOfflineRegionsCallback";} + + static void onError(jni::JNIEnv&, const jni::Object&, std::exception_ptr); + + static void onMerge(jni::JNIEnv&, + const jni::Object&, + const jni::Object&, + mbgl::OfflineRegions&); + }; + + struct FileSourceCallback { + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager$FileSourceCallback";} + + static void onSuccess(jni::JNIEnv&, const jni::Object&); + + static void onError(jni::JNIEnv&, const jni::Object&, const jni::String&); + }; + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineManager"; }; + + static void registerNative(jni::JNIEnv&); + + OfflineManager(jni::JNIEnv&, const jni::Object&); + ~OfflineManager(); + + void setOfflineMapboxTileCountLimit(jni::JNIEnv&, jni::jlong limit); + + void listOfflineRegions(jni::JNIEnv&, const jni::Object&, const jni::Object& callback); + + void createOfflineRegion(jni::JNIEnv&, + const jni::Object& jFileSource_, + const jni::Object& definition, + const jni::Array& metadata, + const jni::Object& callback); + + void mergeOfflineRegions(jni::JNIEnv&, + const jni::Object&, + const jni::String&, + const jni::Object&); + + void putResourceWithUrl(jni::JNIEnv&, + const jni::String& url, + const jni::Array& data, + jlong modified, + jlong expires, + const jni::String& eTag, + jboolean mustRevalidate); + + void resetDatabase(jni::JNIEnv&, const jni::Object& callback_); + + void packDatabase(jni::JNIEnv&, const jni::Object& callback_); + + void invalidateAmbientCache(jni::JNIEnv&, const jni::Object& callback_); + + void clearAmbientCache(jni::JNIEnv&, const jni::Object& callback_); + + void setMaximumAmbientCacheSize(jni::JNIEnv&, const jni::jlong size, const jni::Object& callback_); + + void runPackDatabaseAutomatically(jni::JNIEnv&, jboolean autopack); + +private: + std::shared_ptr fileSource; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/offline/offline_region.cpp b/MapboxGLAndroidSDK/src/cpp/offline/offline_region.cpp new file mode 100644 index 000000000..75734b5d1 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/offline/offline_region.cpp @@ -0,0 +1,314 @@ +#include "offline_region.hpp" + +#include +#include +#include + +#include "offline_region_definition.hpp" +#include "offline_region_error.hpp" +#include "offline_region_status.hpp" +#include "attach_env.hpp" + +namespace mbgl { +namespace android { + +// OfflineRegion // + +OfflineRegion::OfflineRegion(jni::JNIEnv& env, jni::jlong offlineRegionPtr, const jni::Object& jFileSource) + : region(reinterpret_cast(offlineRegionPtr)), + fileSource(std::static_pointer_cast( + std::shared_ptr(mbgl::FileSourceManager::get()->getFileSource( + mbgl::FileSourceType::Database, FileSource::getSharedResourceOptions(env, jFileSource))))) { + if (!fileSource) { + ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), "Offline functionality is disabled."); + } +} + +OfflineRegion::~OfflineRegion() {} + +void OfflineRegion::setOfflineRegionObserver(jni::JNIEnv& env_, const jni::Object& callback) { + + // Define the observer + class Observer : public mbgl::OfflineRegionObserver { + public: + Observer(jni::Global, jni::EnvAttachingDeleter> callback_) + : callback(std::move(callback_)) { + } + + void statusChanged(mbgl::OfflineRegionStatus status) override { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + static auto& javaClass = jni::Class::Singleton(*env); + static auto method = javaClass.GetMethod)>(*env, "onStatusChanged"); + + callback.Call(*env, method, OfflineRegionStatus::New(*env, status)); + } + + void responseError(mbgl::Response::Error error) override { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + static auto& javaClass = jni::Class::Singleton(*env); + static auto method = javaClass.GetMethod)>(*env, "onError"); + + callback.Call(*env, method, OfflineRegionError::New(*env, error)); + } + + void mapboxTileCountLimitExceeded(uint64_t limit) override { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + static auto& javaClass = jni::Class::Singleton(*env); + static auto method = javaClass.GetMethod(*env, "mapboxTileCountLimitExceeded"); + + callback.Call(*env, method, jlong(limit)); + } + + jni::Global, jni::EnvAttachingDeleter> callback; + }; + + // Set the observer + fileSource->setOfflineRegionObserver(*region, std::make_unique(jni::NewGlobal(env_, callback))); +} + +void OfflineRegion::setOfflineRegionDownloadState(jni::JNIEnv&, jni::jint jState) { + // State + mbgl::OfflineRegionDownloadState state; + switch (jState) { + case 0: + state = mbgl::OfflineRegionDownloadState::Inactive; + break; + case 1: + state = mbgl::OfflineRegionDownloadState::Active; + break; + default: + mbgl::Log::Error(mbgl::Event::JNI, "State can only be 0 (inactive) or 1 (active)."); + return; + } + + fileSource->setOfflineRegionDownloadState(*region, state); +} + +void OfflineRegion::getOfflineRegionStatus(jni::JNIEnv& env_, const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + + fileSource->getOfflineRegionStatus(*region, [ + //Ensure the object is not gc'd in the meanwhile + callback = std::make_shared(std::move(globalCallback)) + ](mbgl::expected status) mutable { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (status) { + OfflineRegionStatusCallback::onStatus(*env, *callback, std::move(*status)); + } else { + OfflineRegionStatusCallback::onError(*env, *callback, status.error()); + } + }); +} + +void OfflineRegion::deleteOfflineRegion(jni::JNIEnv& env_, const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + + fileSource->deleteOfflineRegion(*region, + [ + // Ensure the object is not gc'd in the meanwhile + callback = std::make_shared( + std::move(globalCallback))](std::exception_ptr error) mutable { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (error) { + OfflineRegionDeleteCallback::onError(*env, *callback, error); + } else { + OfflineRegionDeleteCallback::onDelete(*env, *callback); + } + }); +} + +void OfflineRegion::invalidateOfflineRegion(jni::JNIEnv& env_, + const jni::Object& callback_) { + auto globalCallback = jni::NewGlobal(env_, callback_); + + fileSource->invalidateOfflineRegion(*region, + [ + // Ensure the object is not gc'd in the meanwhile + callback = std::make_shared( + std::move(globalCallback))](std::exception_ptr error) mutable { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (error) { + OfflineRegionInvalidateCallback::onError(*env, *callback, error); + } else { + OfflineRegionInvalidateCallback::onInvalidate(*env, *callback); + } + }); +} + +void OfflineRegion::updateOfflineRegionMetadata(jni::JNIEnv& env_, const jni::Array& jMetadata, const jni::Object& callback_) { + auto metadata = OfflineRegion::metadata(env_, jMetadata); + auto globalCallback = jni::NewGlobal(env_, callback_); + + fileSource->updateOfflineMetadata(region->getID(), metadata, [ + //Ensure the object is not gc'd in the meanwhile + callback = std::make_shared(std::move(globalCallback)) + ](mbgl::expected data) mutable { + // Reattach, the callback comes from a different thread + android::UniqueEnv env = android::AttachEnv(); + + if (data) { + OfflineRegionUpdateMetadataCallback::onUpdate(*env, *callback, std::move(*data)); + } else { + OfflineRegionUpdateMetadataCallback::onError(*env, *callback, data.error()); + } + }); +} + +jni::Local> OfflineRegion::New(jni::JNIEnv& env, + const jni::Object& jFileSource, + mbgl::OfflineRegion region) { + // Definition + auto definition = region.getDefinition().match( + [&](const mbgl::OfflineTilePyramidRegionDefinition def) { + return OfflineTilePyramidRegionDefinition::New(env, def); + }, + [&](const mbgl::OfflineGeometryRegionDefinition def) { + return OfflineGeometryRegionDefinition::New(env, def); + }); + + // Create region java object + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor, jni::jlong, jni::Object, jni::Array>(env); + + return javaClass.New(env, constructor, + reinterpret_cast(new mbgl::OfflineRegion(std::move(region))), //Copy a region to the heap + jFileSource, + jni::jlong(region.getID()), + definition, + OfflineRegion::metadata(env, region.getMetadata())); +} + +jni::Local> OfflineRegion::metadata(jni::JNIEnv& env, mbgl::OfflineRegionMetadata metadata_) { + std::vector convertedMetadata(metadata_.begin(), metadata_.end()); + std::size_t length = static_cast(convertedMetadata.size()); + auto metadata = jni::Array::New(env, length); + metadata.SetRegion>(env, 0, convertedMetadata); + return metadata; +} + +mbgl::OfflineRegionMetadata OfflineRegion::metadata(jni::JNIEnv& env, const jni::Array& metadata_) { + std::size_t length = metadata_.Length(env); + auto metadata_tmp = std::vector(); + metadata_tmp.resize(length); + metadata_.GetRegion>(env, 0, metadata_tmp); + auto metadata = std::vector(metadata_tmp.begin(), metadata_tmp.end()); + return metadata; +} + +void OfflineRegion::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); + jni::Class::Singleton(env); + jni::Class::Singleton(env); + jni::Class::Singleton(env); + jni::Class::Singleton(env); + + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer&>, + "initialize", + "finalize", + METHOD(&OfflineRegion::setOfflineRegionObserver, "setOfflineRegionObserver"), + METHOD(&OfflineRegion::setOfflineRegionDownloadState, "setOfflineRegionDownloadState"), + METHOD(&OfflineRegion::getOfflineRegionStatus, "getOfflineRegionStatus"), + METHOD(&OfflineRegion::deleteOfflineRegion, "deleteOfflineRegion"), + METHOD(&OfflineRegion::invalidateOfflineRegion, "invalidateOfflineRegion"), + METHOD(&OfflineRegion::updateOfflineRegionMetadata, "updateOfflineRegionMetadata")); +} + +// OfflineRegionObserver // + +// OfflineRegionStatusCallback // + +void OfflineRegion::OfflineRegionStatusCallback::onError(jni::JNIEnv& env, + const jni::Object& callback, + std::exception_ptr error) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onError"); + + callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); +} + +void OfflineRegion::OfflineRegionStatusCallback::onStatus(jni::JNIEnv& env, + const jni::Object& callback, + mbgl::optional status) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod)>(env, "onStatus"); + + callback.Call(env, method, OfflineRegionStatus::New(env, std::move(*status))); +} + +// OfflineRegionDeleteCallback // + +void OfflineRegion::OfflineRegionDeleteCallback::onError(jni::JNIEnv& env, + const jni::Object& callback, + std::exception_ptr error) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onError"); + + callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); +} + +void OfflineRegion::OfflineRegionDeleteCallback::onDelete(jni::JNIEnv& env, const jni::Object& callback) { + // Trigger callback + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onDelete"); + + callback.Call(env, method); +} + +// OfflineRegionUpdateMetadataCallback // + +void OfflineRegion::OfflineRegionUpdateMetadataCallback::onError(jni::JNIEnv& env, + const jni::Object& callback, + std::exception_ptr error) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onError"); + + callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); +} + +void OfflineRegion::OfflineRegionUpdateMetadataCallback::onUpdate(jni::JNIEnv& env, + const jni::Object& callback, + mbgl::optional metadata) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod)>(env, "onUpdate"); + + callback.Call(env, method, OfflineRegion::metadata(env, std::move(*metadata))); +} + +// OfflineRegionInvalidateCallback // + +void OfflineRegion::OfflineRegionInvalidateCallback::onError(jni::JNIEnv& env, + const jni::Object& callback, + std::exception_ptr error) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onError"); + callback.Call(env, method, jni::Make(env, mbgl::util::toString(error))); +} + +void OfflineRegion::OfflineRegionInvalidateCallback::onInvalidate(jni::JNIEnv& env, const jni::Object& callback) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetMethod(env, "onInvalidate"); + callback.Call(env, method); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/offline/offline_region.hpp b/MapboxGLAndroidSDK/src/cpp/offline/offline_region.hpp new file mode 100644 index 000000000..6844008bb --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/offline/offline_region.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include + +#include "../file_source.hpp" + +#include + +namespace mbgl { +namespace android { + +class OfflineRegion { +public: + class OfflineRegionObserver { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionObserver"; }; + }; + + class OfflineRegionStatusCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionStatusCallback"; }; + + static void onError(jni::JNIEnv&, const jni::Object&, std::exception_ptr); + + static void onStatus(jni::JNIEnv&, + const jni::Object&, + mbgl::optional); + }; + + class OfflineRegionDeleteCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionDeleteCallback"; }; + + static void onError(jni::JNIEnv&, const jni::Object&, std::exception_ptr); + + static void onDelete(jni::JNIEnv&, const jni::Object&); + }; + + class OfflineRegionInvalidateCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionInvalidateCallback"; }; + + static void onError(jni::JNIEnv&, const jni::Object&, std::exception_ptr); + + static void onInvalidate(jni::JNIEnv&, const jni::Object&); + }; + + class OfflineRegionUpdateMetadataCallback { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion$OfflineRegionUpdateMetadataCallback"; }; + + static void onError(jni::JNIEnv&, const jni::Object&, std::exception_ptr); + + static void onUpdate(jni::JNIEnv&, + const jni::Object&, + mbgl::optional); + }; + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegion"; }; + + OfflineRegion(jni::JNIEnv&, jni::jlong, const jni::Object&); + + ~OfflineRegion(); + + void setOfflineRegionObserver(jni::JNIEnv&, const jni::Object&); + + void setOfflineRegionDownloadState(jni::JNIEnv&, jni::jint); + + void getOfflineRegionStatus(jni::JNIEnv&, const jni::Object&); + + void deleteOfflineRegion(jni::JNIEnv&, const jni::Object&); + + void invalidateOfflineRegion(jni::JNIEnv&, const jni::Object&); + + void updateOfflineRegionMetadata(jni::JNIEnv&, const jni::Array&, const jni::Object&); + + static jni::Local> New(jni::JNIEnv&, const jni::Object&, mbgl::OfflineRegion); + + static jni::Local> metadata(jni::JNIEnv&, mbgl::OfflineRegionMetadata); + + static mbgl::OfflineRegionMetadata metadata(jni::JNIEnv&, const jni::Array&); + + static void registerNative(jni::JNIEnv&); + +private: + std::unique_ptr region; + std::shared_ptr fileSource; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/offline/offline_region_definition.cpp b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_definition.cpp new file mode 100644 index 000000000..bb9dfc8dd --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_definition.cpp @@ -0,0 +1,107 @@ +#include "offline_region_definition.hpp" + +#include "../geometry/lat_lng_bounds.hpp" +#include "../geojson/geometry.hpp" + +#include + +namespace mbgl { +namespace android { + +// OfflineRegionDefinition // + +void OfflineRegionDefinition::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +mbgl::OfflineRegionDefinition OfflineRegionDefinition::getDefinition(JNIEnv& env, + const jni::Object& jDefinition) { + if (jDefinition.IsInstanceOf(env, jni::Class::Singleton(env))) { + return OfflineTilePyramidRegionDefinition::getDefinition(env, jni::Cast(env, jni::Class::Singleton(env), jDefinition)); + } else if (jDefinition.IsInstanceOf(env, jni::Class::Singleton(env))) { + return OfflineGeometryRegionDefinition::getDefinition(env, jni::Cast(env, jni::Class::Singleton(env), jDefinition)); + } + + throw std::runtime_error("Unknown offline region definition java class"); +} + +// OfflineTilePyramidRegionDefinition // + +jni::Local> OfflineTilePyramidRegionDefinition::New(jni::JNIEnv& env, const mbgl::OfflineTilePyramidRegionDefinition& definition) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor, jni::jdouble, jni::jdouble, jni::jfloat, jni::jboolean>(env); + + return javaClass.New(env, constructor, + jni::Make(env, definition.styleURL), + LatLngBounds::New(env, definition.bounds), + definition.minZoom, + definition.maxZoom, + definition.pixelRatio, + jni::jboolean(definition.includeIdeographs)); +} + +mbgl::OfflineTilePyramidRegionDefinition OfflineTilePyramidRegionDefinition::getDefinition(jni::JNIEnv& env, const jni::Object& jDefinition) { + // Field references + static auto& javaClass = jni::Class::Singleton(env); + static auto styleURLF = javaClass.GetField(env, "styleURL"); + static auto boundsF = javaClass.GetField>(env, "bounds"); + static auto minZoomF = javaClass.GetField(env, "minZoom"); + static auto maxZoomF = javaClass.GetField(env, "maxZoom"); + static auto pixelRatioF = javaClass.GetField(env, "pixelRatio"); + static auto includeIdeographsF = javaClass.GetField(env, "includeIdeographs"); + + return mbgl::OfflineTilePyramidRegionDefinition( + jni::Make(env, jDefinition.Get(env, styleURLF)), + LatLngBounds::getLatLngBounds(env, jDefinition.Get(env, boundsF)), + jDefinition.Get(env, minZoomF), + jDefinition.Get(env, maxZoomF), + jDefinition.Get(env, pixelRatioF), + jDefinition.Get(env, includeIdeographsF) + ); +} + +void OfflineTilePyramidRegionDefinition::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +// OfflineGeometryRegionDefinition // + +jni::Local> OfflineGeometryRegionDefinition::New(jni::JNIEnv& env, const mbgl::OfflineGeometryRegionDefinition& definition) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor, jni::jdouble, jni::jdouble, jni::jfloat, jni::jboolean>(env); + + return javaClass.New(env, constructor, + jni::Make(env, definition.styleURL), + geojson::Geometry::New(env, definition.geometry), + definition.minZoom, + definition.maxZoom, + definition.pixelRatio, + jni::jboolean(definition.includeIdeographs)); +} + +mbgl::OfflineGeometryRegionDefinition OfflineGeometryRegionDefinition::getDefinition(jni::JNIEnv& env, const jni::Object& jDefinition) { + // Field references + static auto& javaClass = jni::Class::Singleton(env); + static auto styleURLF = javaClass.GetField(env, "styleURL"); + static auto geometryF = javaClass.GetField>(env, "geometry"); + static auto minZoomF = javaClass.GetField(env, "minZoom"); + static auto maxZoomF = javaClass.GetField(env, "maxZoom"); + static auto pixelRatioF = javaClass.GetField(env, "pixelRatio"); + static auto includeIdeographsF = javaClass.GetField(env, "includeIdeographs"); + + return mbgl::OfflineGeometryRegionDefinition( + jni::Make(env, jDefinition.Get(env, styleURLF)), + geojson::Geometry::convert(env, jDefinition.Get(env, geometryF)), + jDefinition.Get(env, minZoomF), + jDefinition.Get(env, maxZoomF), + jDefinition.Get(env, pixelRatioF), + jDefinition.Get(env, includeIdeographsF) + ); +} + +void OfflineGeometryRegionDefinition::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/offline/offline_region_definition.hpp b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_definition.hpp new file mode 100644 index 000000000..827fac0a8 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_definition.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace android { + +class OfflineRegionDefinition { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegionDefinition"; }; + + static void registerNative(jni::JNIEnv&); + + static mbgl::OfflineRegionDefinition getDefinition(JNIEnv& env, const jni::Object& jDefinition); +}; + +class OfflineTilePyramidRegionDefinition { +public: + using SuperTag = OfflineRegionDefinition; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition"; }; + + static jni::Local> New(jni::JNIEnv&, const mbgl::OfflineTilePyramidRegionDefinition&); + + static mbgl::OfflineTilePyramidRegionDefinition getDefinition(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +class OfflineGeometryRegionDefinition { +public: + using SuperTag = OfflineRegionDefinition; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineGeometryRegionDefinition"; }; + + static jni::Local> New(jni::JNIEnv&, const mbgl::OfflineGeometryRegionDefinition&); + + static mbgl::OfflineGeometryRegionDefinition getDefinition(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/offline/offline_region_error.cpp b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_error.cpp new file mode 100644 index 000000000..199bbafa6 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_error.cpp @@ -0,0 +1,44 @@ +#include "offline_region_error.hpp" + +namespace mbgl { +namespace android { + +jni::Local> OfflineRegionError::New(jni::JNIEnv& env, mbgl::Response::Error error) { + + // Handle the value of reason independently of the underlying int value + std::string reason; + switch(error.reason) { + case mbgl::Response::Error::Reason::Success: + reason = "REASON_SUCCESS"; + break; + case mbgl::Response::Error::Reason::NotFound: + reason = "REASON_NOT_FOUND"; + break; + case mbgl::Response::Error::Reason::Server: + reason = "REASON_SERVER"; + break; + case mbgl::Response::Error::Reason::Connection: + reason = "REASON_CONNECTION"; + break; + case mbgl::Response::Error::Reason::RateLimit: + reason = "REASON_RATE_LIMIT"; + break; + case mbgl::Response::Error::Reason::Other: + reason = "REASON_OTHER"; + break; + } + + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + + return javaClass.New(env, constructor, + jni::Make(env, reason), + jni::Make(env, error.message)); +} + +void OfflineRegionError::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/offline/offline_region_error.hpp b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_error.hpp new file mode 100644 index 000000000..151e05ab1 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_error.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace android { + +class OfflineRegionError { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegionError"; }; + + static jni::Local> New(jni::JNIEnv&, mbgl::Response::Error); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/offline/offline_region_status.cpp b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_status.cpp new file mode 100644 index 000000000..5dd9f20a7 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_status.cpp @@ -0,0 +1,38 @@ +#include "offline_region_status.hpp" + +namespace mbgl { +namespace android { + +jni::Local> OfflineRegionStatus::New(jni::JNIEnv& env, mbgl::OfflineRegionStatus status) { + + // Convert to jint + jint downloadState; + switch(status.downloadState) { + case mbgl::OfflineRegionDownloadState::Inactive: + downloadState = 0; + break; + case mbgl::OfflineRegionDownloadState::Active: + downloadState = 1; + break; + } + + // Create java object + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, + downloadState, + jlong(status.completedResourceCount), + jlong(status.completedResourceSize), + jlong(status.completedTileCount), + jlong(status.completedTileSize), + jlong(status.requiredResourceCount), + jboolean(status.requiredResourceCountIsPrecise) + ); +} + +void OfflineRegionStatus::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/offline/offline_region_status.hpp b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_status.hpp new file mode 100644 index 000000000..9ef48ec35 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/offline/offline_region_status.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +namespace mbgl { +namespace android { + +class OfflineRegionStatus { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/offline/OfflineRegionStatus"; }; + + static jni::Local> New(jni::JNIEnv&, mbgl::OfflineRegionStatus status); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshot.cpp b/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshot.cpp new file mode 100644 index 000000000..03a532a43 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshot.cpp @@ -0,0 +1,65 @@ +#include "map_snapshot.hpp" + +#include "bitmap.hpp" +#include "../conversion/collection.hpp" + +#include + +namespace mbgl { +namespace android { + +MapSnapshot::MapSnapshot(float pixelRatio_, MapSnapshot::PointForFn pointForFn_, MapSnapshot::LatLngForFn latLngForFn_) + : pixelRatio(pixelRatio_) + , pointForFn(std::move(pointForFn_)) + , latLngForFn(std::move(latLngForFn_)) { +} + +MapSnapshot::~MapSnapshot() = default; + +jni::Local> MapSnapshot::pixelForLatLng(jni::JNIEnv& env, jni::Object& jLatLng) { + ScreenCoordinate point = pointForFn(LatLng::getLatLng(env, jLatLng)); + return PointF::New(env, point.x * pixelRatio, point.y * pixelRatio); +} + +jni::Local> MapSnapshot::latLngForPixel(jni::JNIEnv& env, jni::Object& jPoint) { + return LatLng::New(env, latLngForFn(PointF::getScreenCoordinate(env, jPoint))); +} + +// Static methods // + +jni::Local> MapSnapshot::New(JNIEnv& env, + PremultipliedImage&& image, + float pixelRatio, + std::vector attributions, + bool showLogo, + mbgl::MapSnapshotter::PointForFn pointForFn, + mbgl::MapSnapshotter::LatLngForFn latLngForFn) { + // Create the bitmap + auto bitmap = Bitmap::CreateBitmap(env, std::move(image)); + + // Create the Mapsnapshot peers + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor, jni::Array, jni::jboolean>(env); + auto nativePeer = std::make_unique(pixelRatio, pointForFn, latLngForFn); + return javaClass.New(env, constructor, reinterpret_cast(nativePeer.release()), bitmap, conversion::toArray(env, attributions), (jni::jboolean) showLogo); +} + +void MapSnapshot::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer(env, javaClass, + "nativePtr", + std::make_unique, + "initialize", + "finalize", + METHOD(&MapSnapshot::latLngForPixel, "latLngForPixel"), + METHOD(&MapSnapshot::pixelForLatLng, "pixelForLatLng") + ); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshot.hpp b/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshot.hpp new file mode 100644 index 000000000..6b82d0283 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshot.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include + +#include "../geometry/lat_lng.hpp" +#include "../graphics/pointf.hpp" + +#include +#include + +namespace mbgl { +namespace android { + +class MapSnapshot { +public: + + using PointForFn = mbgl::MapSnapshotter::PointForFn; + using LatLngForFn = mbgl::MapSnapshotter::LatLngForFn; + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/snapshotter/MapSnapshot"; }; + + static void registerNative(jni::JNIEnv&); + + static jni::Local> New(JNIEnv& env, + PremultipliedImage&& image, + float pixelRatio, + std::vector attributions, + bool showLogo, + PointForFn pointForFn, + LatLngForFn latLngForFn); + + MapSnapshot(jni::JNIEnv&) {}; + MapSnapshot(float pixelRatio, PointForFn, LatLngForFn); + ~MapSnapshot(); + + jni::Local> pixelForLatLng(jni::JNIEnv&, jni::Object&); + jni::Local> latLngForPixel(jni::JNIEnv&, jni::Object&); + +private: + float pixelRatio; + mbgl::MapSnapshotter::PointForFn pointForFn; + mbgl::MapSnapshotter::LatLngForFn latLngForFn; +}; + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshotter.cpp b/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshotter.cpp new file mode 100644 index 000000000..159da809f --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshotter.cpp @@ -0,0 +1,353 @@ +#include "map_snapshotter.hpp" + +#include +#include +#include +#include +#include + +#include "attach_env.hpp" +#include "../style/layers/layer_manager.hpp" +#include "map_snapshot.hpp" + +namespace mbgl { +namespace android { + +MapSnapshotter::MapSnapshotter(jni::JNIEnv& _env, + const jni::Object& _obj, + const jni::Object& _jFileSource, + jni::jfloat _pixelRatio, + jni::jint width, + jni::jint height, + const jni::String& styleURL, + const jni::String& styleJSON, + const jni::Object& region, + const jni::Object& position, + jni::jboolean _showLogo, + const jni::String& _localIdeographFontFamily) + : javaPeer(_env, _obj) + , pixelRatio(_pixelRatio) { + + // Get a reference to the JavaVM for callbacks + if (_env.GetJavaVM(&vm) < 0) { + _env.ExceptionDescribe(); + return; + } + + weakScheduler = mbgl::Scheduler::GetCurrent()->makeWeakPtr(); + + jFileSource = FileSource::getNativePeer(_env, _jFileSource); + auto size = mbgl::Size { static_cast(width), static_cast(height) }; + + showLogo = _showLogo; + + // Create the core snapshotter + snapshotter = std::make_unique( + size, + pixelRatio, + mbgl::android::FileSource::getSharedResourceOptions(_env, _jFileSource), + *this, + _localIdeographFontFamily ? jni::Make(_env, _localIdeographFontFamily) : optional{}); + + if (position) { + snapshotter->setCameraOptions(CameraPosition::getCameraOptions(_env, position, pixelRatio)); + } + + if (region) { + snapshotter->setRegion(LatLngBounds::getLatLngBounds(_env, region)); + } + + if (styleJSON) { + snapshotter->setStyleJSON(jni::Make(_env, styleJSON)); + } else { + snapshotter->setStyleURL(jni::Make(_env, styleURL)); + } +} + +MapSnapshotter::~MapSnapshotter() { + auto guard = weakScheduler.lock(); + if (weakScheduler && weakScheduler.get() != mbgl::Scheduler::GetCurrent()) { + snapshotter->cancel(); + weakScheduler->schedule([ptr = snapshotter.release()]() mutable { + if (ptr) { + delete ptr; + } + }); + } +} + +void MapSnapshotter::start(JNIEnv& env) { + MBGL_VERIFY_THREAD(tid); + activateFilesource(env); + snapshotter->snapshot([this](std::exception_ptr err, + PremultipliedImage image, + std::vector attributions, + mbgl::MapSnapshotter::PointForFn pointForFn, + mbgl::MapSnapshotter::LatLngForFn latLngForFn) { + MBGL_VERIFY_THREAD(tid); + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + + if (err) { + // error handler callback + static auto onSnapshotFailed = javaClass.GetMethod(*_env, "onSnapshotFailed"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onSnapshotFailed, jni::Make(*_env, util::toString(err))); + } + } else { + // Create the wrapper + auto mapSnapshot = android::MapSnapshot::New(*_env, std::move(image), pixelRatio, attributions, showLogo, pointForFn, latLngForFn); + + // invoke callback + static auto onSnapshotReady = javaClass.GetMethod)>(*_env, "onSnapshotReady"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onSnapshotReady, mapSnapshot); + } + } + + deactivateFilesource(*_env); + }); +} + +void MapSnapshotter::cancel(JNIEnv& env) { + MBGL_VERIFY_THREAD(tid); + snapshotter->cancel(); + deactivateFilesource(env); +} + +void MapSnapshotter::setStyleUrl(JNIEnv& env, const jni::String& styleURL) { + snapshotter->setStyleURL(jni::Make(env, styleURL)); +} + +void MapSnapshotter::setStyleJson(JNIEnv& env, const jni::String& styleJSON) { + snapshotter->setStyleJSON(jni::Make(env, styleJSON)); +} + +void MapSnapshotter::setSize(JNIEnv&, jni::jint width, jni::jint height) { + auto size = mbgl::Size { static_cast(width), static_cast(height) }; + snapshotter->setSize(size); +} + +void MapSnapshotter::setCameraPosition(JNIEnv& env, const jni::Object& position) { + auto options = CameraPosition::getCameraOptions(env, position, pixelRatio); + snapshotter->setCameraOptions(options); +} + +void MapSnapshotter::setRegion(JNIEnv& env, const jni::Object& region) { + snapshotter->setRegion(LatLngBounds::getLatLngBounds(env, region)); +} + +// Private methods // + +void MapSnapshotter::activateFilesource(JNIEnv& env) { + if (!activatedFilesource) { + activatedFilesource = true; + jFileSource->resume(env); + } +} + +void MapSnapshotter::deactivateFilesource(JNIEnv& env) { + if (activatedFilesource) { + activatedFilesource = false; + jFileSource->pause(env); + } +} + +void MapSnapshotter::onDidFailLoadingStyle(const std::string& error) { + MBGL_VERIFY_THREAD(tid); + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onDidFailLoadingStyle = javaClass.GetMethod(*_env, "onDidFailLoadingStyle"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onDidFailLoadingStyle, jni::Make(*_env, error)); + } +} + +void MapSnapshotter::onDidFinishLoadingStyle() { + MBGL_VERIFY_THREAD(tid); + android::UniqueEnv _env = android::AttachEnv(); + + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onDidFinishLoadingStyle = javaClass.GetMethod(*_env, "onDidFinishLoadingStyle"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onDidFinishLoadingStyle); + } +} + +void MapSnapshotter::onStyleImageMissing(const std::string& imageName) { + MBGL_VERIFY_THREAD(tid); + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onStyleImageMissing = javaClass.GetMethod(*_env, "onStyleImageMissing"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + weakReference.Call(*_env, onStyleImageMissing, jni::Make(*_env, imageName)); + } +} + +void MapSnapshotter::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint index) { + assert(nativeLayerPtr != 0); + const auto layers = snapshotter->getStyle().getLayers(); + auto* layer = reinterpret_cast(nativeLayerPtr); + // Check index + const int numLayers = layers.size() - 1; + if (index > numLayers || index < 0) { + Log::Error(Event::JNI, "Index out of range: %i", index); + jni::ThrowNew(env, + jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), + std::string("Invalid index").c_str()); + } + // Insert it below the current at that index + try { + layer->addToStyle(snapshotter->getStyle(), layers.at(index)->getID()); + } catch (const std::runtime_error& error) { + jni::ThrowNew( + env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); + } +} + +void MapSnapshotter::addLayerBelow(JNIEnv& env, jlong nativeLayerPtr, const jni::String& below) { + assert(nativeLayerPtr != 0); + + auto* layer = reinterpret_cast(nativeLayerPtr); + try { + layer->addToStyle( + snapshotter->getStyle(), + below ? mbgl::optional(jni::Make(env, below)) : mbgl::optional()); + } catch (const std::runtime_error& error) { + jni::ThrowNew( + env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); + } +} + +void MapSnapshotter::addLayerAbove(JNIEnv& env, jlong nativeLayerPtr, const jni::String& above) { + assert(nativeLayerPtr != 0); + auto* newLayer = reinterpret_cast(nativeLayerPtr); + + // Find the sibling + const auto snapshotterLayers = snapshotter->getStyle().getLayers(); + auto siblingId = jni::Make(env, above); + + size_t index = 0; + for (auto* snapshotterLayer : snapshotterLayers) { + ++index; + if (snapshotterLayer->getID() == siblingId) { + break; + } + } + + // Check if we found a sibling to place before + mbgl::optional before; + if (index > snapshotterLayers.size()) { + // Not found + jni::ThrowNew(env, + jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), + std::string("Could not find layer: ").append(siblingId).c_str()); + } else if (index < snapshotterLayers.size()) { + // Place before the sibling + before = {snapshotterLayers.at(index)->getID()}; + } + + // Add the layer + try { + newLayer->addToStyle(snapshotter->getStyle(), before); + } catch (const std::runtime_error& error) { + jni::ThrowNew( + env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/layers/CannotAddLayerException"), error.what()); + } +} + +void MapSnapshotter::addSource(JNIEnv& env, const jni::Object& obj, jlong sourcePtr) { + assert(sourcePtr != 0); + + auto* source = reinterpret_cast(sourcePtr); + try { + source->addToStyle(env, obj, snapshotter->getStyle()); + } catch (const std::runtime_error& error) { + jni::ThrowNew( + env, jni::FindClass(env, "com/mapbox/mapboxsdk/style/sources/CannotAddSourceException"), error.what()); + } +} + +void MapSnapshotter::addImages(JNIEnv& env, const jni::Array>& jimages) { + jni::NullCheck(env, &jimages); + std::size_t len = jimages.Length(env); + + for (std::size_t i = 0; i < len; ++i) { + auto image = mbgl::android::Image::getImage(env, jimages.Get(env, i)); + snapshotter->getStyle().addImage(std::make_unique(image)); + } +} + +jni::Local> MapSnapshotter::getLayer(JNIEnv& env, const jni::String& layerId) { + // Find the layer + mbgl::style::Layer* coreLayer = snapshotter->getStyle().getLayer(jni::Make(env, layerId)); + if (!coreLayer) { + mbgl::Log::Debug(mbgl::Event::JNI, "No layer found"); + return jni::Local>(); + } + + // Create and return the layer's native peer + return LayerManagerAndroid::get()->createJavaLayerPeer(env, *coreLayer); +} + +jni::Local> MapSnapshotter::getSource(JNIEnv& env, const jni::String& sourceId) { + // Find the source + mbgl::style::Source* coreSource = snapshotter->getStyle().getSource(jni::Make(env, sourceId)); + if (!coreSource) { + mbgl::Log::Debug(mbgl::Event::JNI, "No source found"); + return jni::Local>(); + } + + // Create and return the source's native peer + return jni::NewLocal(env, Source::peerForCoreSource(env, *coreSource)); +} + +// Static methods // + +void MapSnapshotter::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer(env, + javaClass, + "nativePtr", + jni::MakePeer&, + const jni::Object&, + jni::jfloat, + jni::jint, + jni::jint, + const jni::String&, + const jni::String&, + const jni::Object&, + const jni::Object&, + jni::jboolean, + const jni::String&>, + "nativeInitialize", + "finalize", + METHOD(&MapSnapshotter::setStyleUrl, "setStyleUrl"), + METHOD(&MapSnapshotter::addLayerAt, "nativeAddLayerAt"), + METHOD(&MapSnapshotter::addLayerBelow, "nativeAddLayerBelow"), + METHOD(&MapSnapshotter::addLayerAbove, "nativeAddLayerAbove"), + METHOD(&MapSnapshotter::addSource, "nativeAddSource"), + METHOD(&MapSnapshotter::addImages, "nativeAddImages"), + METHOD(&MapSnapshotter::getLayer, "nativeGetLayer"), + METHOD(&MapSnapshotter::getSource, "nativeGetSource"), + METHOD(&MapSnapshotter::setStyleJson, "setStyleJson"), + METHOD(&MapSnapshotter::setSize, "setSize"), + METHOD(&MapSnapshotter::setCameraPosition, "setCameraPosition"), + METHOD(&MapSnapshotter::setRegion, "setRegion"), + METHOD(&MapSnapshotter::start, "nativeStart"), + METHOD(&MapSnapshotter::cancel, "nativeCancel")); +} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshotter.hpp b/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshotter.hpp new file mode 100644 index 000000000..4d0186e7f --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/snapshotter/map_snapshotter.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include "../file_source.hpp" +#include "../geometry/lat_lng_bounds.hpp" +#include "../map/camera_position.hpp" +#include "../map/image.hpp" +#include "../style/layers/layer.hpp" +#include "../style/sources/source.hpp" + +namespace mbgl { +namespace android { + +class MapSnapshotter final : public mbgl::MapSnapshotterObserver { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/snapshotter/MapSnapshotter"; }; + + static void registerNative(jni::JNIEnv&); + + MapSnapshotter(jni::JNIEnv&, + const jni::Object&, + const jni::Object&, + jni::jfloat pixelRatio, + jni::jint width, + jni::jint height, + const jni::String& styleURL, + const jni::String& styleJSON, + const jni::Object& region, + const jni::Object& position, + jni::jboolean showLogo, + const jni::String& localIdeographFontFamily); + + virtual ~MapSnapshotter() override; + + void setStyleUrl(JNIEnv&, const jni::String& styleURL); + + void setStyleJson(JNIEnv&, const jni::String& styleJSON); + + void setSize(JNIEnv&, jni::jint width, jni::jint height); + + void setCameraPosition(JNIEnv&, const jni::Object& position); + + void setRegion(JNIEnv&, const jni::Object& region); + + void start(JNIEnv&); + + void cancel(JNIEnv&); + + void addLayerAt(JNIEnv&, jlong, jni::jint); + void addLayerBelow(JNIEnv&, jlong, const jni::String&); + void addLayerAbove(JNIEnv&, jlong, const jni::String&); + void addSource(JNIEnv&, const jni::Object&, jlong nativePtr); + void addImages(JNIEnv&, const jni::Array>&); + jni::Local> getLayer(JNIEnv&, const jni::String&); + jni::Local> getSource(JNIEnv&, const jni::String&); + + // MapSnapshotterObserver overrides + void onDidFailLoadingStyle(const std::string&) override; + void onDidFinishLoadingStyle() override; + void onStyleImageMissing(const std::string&) override; + +private: + MBGL_STORE_THREAD(tid); + + JavaVM *vm = nullptr; + jni::WeakReference, jni::EnvAttachingDeleter> javaPeer; + + float pixelRatio; + bool showLogo; + + FileSource *jFileSource; + void activateFilesource(JNIEnv&); + void deactivateFilesource(JNIEnv&); + bool activatedFilesource = false; + mapbox::base::WeakPtr weakScheduler; + std::unique_ptr snapshotter; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/android_conversion.hpp b/MapboxGLAndroidSDK/src/cpp/style/android_conversion.hpp new file mode 100644 index 000000000..6f1680459 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/android_conversion.hpp @@ -0,0 +1,148 @@ +#pragma once + +#include "value.hpp" + +#include +#include +#include +#include +#include + +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +class ConversionTraits { +public: + static bool isUndefined(const mbgl::android::Value& value) { + return value.isNull(); + } + + static bool isArray(const mbgl::android::Value& value) { + return value.isArray(); + } + + static bool isObject(const mbgl::android::Value& value) { + return value.isObject(); + } + + static std::size_t arrayLength(const mbgl::android::Value& value) { + return value.getLength();; + } + + static mbgl::android::Value arrayMember(const mbgl::android::Value& value, std::size_t i) { + return value.get(i); + } + + static optional objectMember(const mbgl::android::Value& value, const char* key) { + mbgl::android::Value member = value.get(key); + if (!member.isNull()) { + return member; + } else { + return {}; + } + } + + template + static optional eachMember(const mbgl::android::Value& value, Fn&& fn) { + assert(value.isObject()); + mbgl::android::Value keys = value.keyArray(); + std::size_t length = arrayLength(keys); + for(std::size_t i = 0; i < length; ++i){ + const auto k = keys.get(i).toString(); + auto v = value.get(k.c_str()); + optional result = fn(k, std::move(v)); + if (result) { + return result; + } + } + return {}; + } + + static optional toBool(const mbgl::android::Value& value) { + if (value.isBool()) { + return value.toBool(); + } else { + return {}; + } + } + + static optional toNumber(const mbgl::android::Value& value) { + if (value.isNumber()) { + auto num = value.toFloat(); + return num; + } else { + return {}; + } + } + + static optional toDouble(const mbgl::android::Value& value) { + if (value.isNumber()) { + return value.toDouble(); + } else { + return {}; + } + } + + static optional toString(const mbgl::android::Value& value) { + if (value.isString()) { + return value.toString(); + } else { + return {}; + } + } + + static optional toValue(const mbgl::android::Value& value) { + if (value.isNull()) { + return {}; + } else if (value.isBool()) { + return { value.toBool() }; + } else if (value.isString()) { + return { value.toString() }; + } else if (value.isNumber()) { + return { value.toDouble() }; + } else { + return {}; + } + } + + static optional toGeoJSON(const mbgl::android::Value &value, Error &error) { + if (value.isNull()) { + error = { "no json data found" }; + return {}; + } + + if (value.isString()) { + return parseGeoJSON(value.toString(), error); + } + + if (value.isObject()) { + mbgl::android::Value keys = value.keyArray(); + std::size_t length = arrayLength(keys); + for (std::size_t i = 0; i < length; ++i) { + if (keys.get(i).toString() == "json") { + auto v = value.get("json"); + if (v.isString()) { + return parseGeoJSON(v.toString(), error); + } else { + break; + } + } + } + } + error = {"no json data found"}; + return {}; + } +}; + +template +optional convert(mbgl::android::Value&& value, Error& error, Args&&...args) { + return convert(Convertible(std::move(value)), error, std::forward(args)...); +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/conversion/filter.cpp b/MapboxGLAndroidSDK/src/cpp/style/conversion/filter.cpp new file mode 100644 index 000000000..d15e2747a --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/conversion/filter.cpp @@ -0,0 +1,26 @@ +#include "filter.hpp" +#include "../android_conversion.hpp" + +#include +#include + +namespace mbgl { +namespace android { +namespace conversion { + +optional toFilter(jni::JNIEnv& env, const jni::Array>& jfilter) { + mbgl::optional filter; + if (jfilter) { + mbgl::style::conversion::Error error; + auto converted = mbgl::style::conversion::convert(Value(env, jfilter), error); + if (!converted) { + mbgl::Log::Error(mbgl::Event::JNI, "Error converting filter: " + error.message); + } + filter = std::move(*converted); + } + return filter; +} + +} // namespace conversion +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/conversion/filter.hpp b/MapboxGLAndroidSDK/src/cpp/style/conversion/filter.hpp new file mode 100644 index 000000000..920d336a1 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/conversion/filter.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { +namespace android { +namespace conversion { + +optional toFilter(jni::JNIEnv&, const jni::Array>&); + +} // namespace conversion +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/style/conversion/position.cpp b/MapboxGLAndroidSDK/src/cpp/style/conversion/position.cpp new file mode 100644 index 000000000..e3c334afd --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/conversion/position.cpp @@ -0,0 +1,24 @@ +#include "position.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +Result>> Converter>, mbgl::style::Position>::operator()(jni::JNIEnv &env, const mbgl::style::Position &value) const { + std::array cartPosition = value.getSpherical(); + return Position::fromPosition(env, cartPosition[0], cartPosition[1], cartPosition[2]); +} + +Result Converter>::operator()(jni::JNIEnv &env, const jni::Object &value) const { + float radialCoordinate = Position::getRadialCoordinate(env, value); + float azimuthalAngle = Position::getAzimuthalAngle(env, value); + float polarAngle = Position::getPolarAngle(env, value); + std::array cartPosition {{radialCoordinate, azimuthalAngle, polarAngle}}; + mbgl::style::Position position{}; + position.set(cartPosition); + return position; +} + +} +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/style/conversion/position.hpp b/MapboxGLAndroidSDK/src/cpp/style/conversion/position.hpp new file mode 100644 index 000000000..90409d527 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/conversion/position.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "../../conversion/conversion.hpp" +#include "../position.hpp" + +#include +#include + +namespace mbgl { +namespace android { +namespace conversion { + +template <> +struct Converter>, mbgl::style::Position> { + Result>> operator()(jni::JNIEnv &env, const mbgl::style::Position &value) const; +}; + +template <> +struct Converter> { + Result operator()(jni::JNIEnv &env, const jni::Object &value) const; +}; + +} +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/style/conversion/property_expression.hpp b/MapboxGLAndroidSDK/src/cpp/style/conversion/property_expression.hpp new file mode 100644 index 000000000..4e26b11f9 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/conversion/property_expression.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "../../conversion/conversion.hpp" +#include "../../gson/json_element.hpp" + +#include + +#include + +namespace mbgl { +namespace android { +namespace conversion { + +template +struct Converter>, mbgl::style::PropertyExpression> { + Result>> operator()(jni::JNIEnv& env, const mbgl::style::PropertyExpression& value) const { + return gson::JsonElement::New(env, value.getExpression().serialize()); + } +}; + +} // namespace conversion +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/conversion/property_value.hpp b/MapboxGLAndroidSDK/src/cpp/style/conversion/property_value.hpp new file mode 100644 index 000000000..317705a28 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/conversion/property_value.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +#include "../../conversion/conversion.hpp" +#include "../../conversion/constant.hpp" +#include "property_expression.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +/** + * Conversion from core property value types to Java property value types + */ +template +class PropertyValueEvaluator { +public: + PropertyValueEvaluator(jni::JNIEnv& _env) : env(_env) {} + + jni::Local> operator()(const mbgl::style::Undefined) const { + return jni::Local>(env, nullptr); + } + + jni::Local> operator()(const T& value) const { + return std::move(*convert>>(env, value)); + } + + jni::Local> operator()(const mbgl::style::PropertyExpression& value) const { + return std::move(*convert>>(env, value)); + } + +private: + jni::JNIEnv& env; +}; + +/** + * Convert core property values to java + */ +template +struct Converter>, mbgl::style::PropertyValue> { + Result>> operator()(jni::JNIEnv& env, const mbgl::style::PropertyValue& value) const { + PropertyValueEvaluator evaluator(env); + return value.evaluate(evaluator); + } +}; + +/** + * Convert core heat map color property value to java + */ +template <> +struct Converter>, mbgl::style::ColorRampPropertyValue> { + Result>> operator()(jni::JNIEnv& env, const mbgl::style::ColorRampPropertyValue& value) const { + PropertyValueEvaluator evaluator(env); + return std::move(*convert>>(env, value.evaluate(evaluator))); + } +}; + +} // namespace conversion +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/conversion/transition_options.cpp b/MapboxGLAndroidSDK/src/cpp/style/conversion/transition_options.cpp new file mode 100644 index 000000000..75d9b4cc0 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/conversion/transition_options.cpp @@ -0,0 +1,17 @@ +#include "transition_options.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +Result>> Converter>, mbgl::style::TransitionOptions>::operator()(jni::JNIEnv& env, const mbgl::style::TransitionOptions& value) const { + return TransitionOptions::fromTransitionOptions(env, + std::chrono::duration_cast(value.duration.value_or(mbgl::Duration::zero())).count(), + std::chrono::duration_cast(value.delay.value_or(mbgl::Duration::zero())).count(), + (jboolean) value.enablePlacementTransitions + ); +} + +} +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/style/conversion/transition_options.hpp b/MapboxGLAndroidSDK/src/cpp/style/conversion/transition_options.hpp new file mode 100644 index 000000000..06c042922 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/conversion/transition_options.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "../../conversion/conversion.hpp" +#include "../transition_options.hpp" + +#include + +#include + +namespace mbgl { +namespace android { +namespace conversion { + +template<> +struct Converter>, mbgl::style::TransitionOptions> { + Result>> operator()(jni::JNIEnv&, const mbgl::style::TransitionOptions&) const; +}; + +} +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/style/conversion/url_or_tileset.cpp b/MapboxGLAndroidSDK/src/cpp/style/conversion/url_or_tileset.cpp new file mode 100644 index 000000000..2ec585675 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/conversion/url_or_tileset.cpp @@ -0,0 +1,30 @@ +#include "url_or_tileset.hpp" +#include "../android_conversion.hpp" + +#include +#include + +namespace mbgl { +namespace android { + +// This conversion is expected not to fail because it's used only in contexts where +// the value was originally a String or TileSet object on the Java side. If it fails +// to convert, it's a bug in our serialization or Java-side static typing. +variant convertURLOrTileset(mbgl::android::Value&& value) { + using namespace mbgl::style::conversion; + + const Convertible convertible(std::move(value)); + if (isObject(convertible)) { + Error error; + optional tileset = convert(convertible, error); + if (!tileset) { + throw std::logic_error(error.message); + } + return { *tileset }; + } else { + return { *toString(convertible) }; + } +} + +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/style/conversion/url_or_tileset.hpp b/MapboxGLAndroidSDK/src/cpp/style/conversion/url_or_tileset.hpp new file mode 100644 index 000000000..f42a9b9a2 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/conversion/url_or_tileset.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#include + +#include "../value.hpp" + +namespace mbgl { +namespace android { + +variant convertURLOrTileset(mbgl::android::Value&& value); + +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/style/formatted.cpp b/MapboxGLAndroidSDK/src/cpp/style/formatted.cpp new file mode 100644 index 000000000..69c3454e2 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/formatted.cpp @@ -0,0 +1,54 @@ +#include "formatted.hpp" +#include "formatted_section.hpp" +#include "../conversion/conversion.hpp" +#include "../conversion/constant.hpp" + +namespace mbgl { +namespace android { + +void Formatted::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +jni::Local> Formatted::New(jni::JNIEnv& env, const style::expression::Formatted& value) { + static auto& formatted = jni::Class::Singleton(env); + static auto formattedConstructor = formatted.GetConstructor>>(env); + static auto& formattedSectionClass = jni::Class::Singleton(env); + + auto sections = jni::Array>::New(env, value.sections.size()); + for (std::size_t i = 0; i < value.sections.size(); i++) { + auto section = value.sections.at(i); + auto text = jni::Make(env, section.text); + static auto formattedSectionConstructor = formattedSectionClass.GetConstructor(env); + auto formattedSection = formattedSectionClass.New(env, formattedSectionConstructor, text); + + if (section.fontScale) { + double fontScale = section.fontScale.value(); + static auto method = formattedSectionClass.GetMethod(env, "setFontScale"); + formattedSection.Call(env, method, jni::Box(env, fontScale)); + } + + if (section.fontStack) { + auto fontStack = jni::Array::New(env, section.fontStack.value().size()); + for (std::size_t j = 0; j < section.fontStack.value().size(); j++) { + fontStack.Set(env, j, jni::Make(env, section.fontStack.value().at(j))); + } + static auto method = formattedSectionClass.GetMethod)>(env, "setFontStack"); + formattedSection.Call(env, method, fontStack); + } + + if (section.textColor) { + using namespace mbgl::android::conversion; + auto textColor = std::move(*convert>>(env, *section.textColor)); + static auto method = formattedSectionClass.GetMethod)>(env, "setTextColor"); + formattedSection.Call(env, method, textColor); + } + + sections.Set(env, i, formattedSection); + } + + return formatted.New(env, formattedConstructor, sections); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/style/formatted.hpp b/MapboxGLAndroidSDK/src/cpp/style/formatted.hpp new file mode 100644 index 000000000..ca91f6562 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/formatted.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include +#include + +namespace mbgl { + namespace android { + + using SuperTag = jni::ObjectTag; + class Formatted : private mbgl::util::noncopyable { + public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/types/Formatted"; }; + + static jni::Local> + New(jni::JNIEnv&, const style::expression::Formatted &value); + + static void registerNative(jni::JNIEnv &); + }; + + } // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/style/formatted_section.cpp b/MapboxGLAndroidSDK/src/cpp/style/formatted_section.cpp new file mode 100644 index 000000000..ddcf76f81 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/formatted_section.cpp @@ -0,0 +1,11 @@ +#include "formatted_section.hpp" + +namespace mbgl { +namespace android { + +void FormattedSection::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/style/formatted_section.hpp b/MapboxGLAndroidSDK/src/cpp/style/formatted_section.hpp new file mode 100644 index 000000000..f759a94e8 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/formatted_section.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace android { + +class FormattedSection : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/types/FormattedSection"; }; + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/background_layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/background_layer.cpp new file mode 100644 index 000000000..3eae30677 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/background_layer.cpp @@ -0,0 +1,148 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "background_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +#include + +namespace mbgl { +namespace android { + + inline mbgl::style::BackgroundLayer& toBackgroundLayer(mbgl::style::Layer& layer) { + return static_cast(layer); + } + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + BackgroundLayer::BackgroundLayer(jni::JNIEnv& env, jni::String& layerId) + : Layer(std::make_unique(jni::Make(env, layerId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + BackgroundLayer::BackgroundLayer(mbgl::style::BackgroundLayer& coreLayer) : Layer(coreLayer) {} + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + BackgroundLayer::BackgroundLayer(std::unique_ptr coreLayer) + : Layer(std::move(coreLayer)) {} + + BackgroundLayer::~BackgroundLayer() = default; + + // Property getters + + jni::Local> BackgroundLayer::getBackgroundColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toBackgroundLayer(layer).getBackgroundColor())); + } + + jni::Local> BackgroundLayer::getBackgroundColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toBackgroundLayer(layer).getBackgroundColorTransition(); + return std::move(*convert>>(env, options)); + } + + void BackgroundLayer::setBackgroundColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toBackgroundLayer(layer).setBackgroundColorTransition(options); + } + + jni::Local> BackgroundLayer::getBackgroundPattern(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toBackgroundLayer(layer).getBackgroundPattern())); + } + + jni::Local> BackgroundLayer::getBackgroundPatternTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toBackgroundLayer(layer).getBackgroundPatternTransition(); + return std::move(*convert>>(env, options)); + } + + void BackgroundLayer::setBackgroundPatternTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toBackgroundLayer(layer).setBackgroundPatternTransition(options); + } + + jni::Local> BackgroundLayer::getBackgroundOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toBackgroundLayer(layer).getBackgroundOpacity())); + } + + jni::Local> BackgroundLayer::getBackgroundOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toBackgroundLayer(layer).getBackgroundOpacityTransition(); + return std::move(*convert>>(env, options)); + } + + void BackgroundLayer::setBackgroundOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toBackgroundLayer(layer).setBackgroundOpacityTransition(options); + } + + + // BackgroundJavaLayerPeerFactory + + BackgroundJavaLayerPeerFactory::~BackgroundJavaLayerPeerFactory() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } // namespace + + jni::Local> BackgroundJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, + mbgl::style::Layer& layer) { + assert(layer.baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new BackgroundLayer(toBackgroundLayer(layer))); + } + + jni::Local> BackgroundJavaLayerPeerFactory::createJavaLayerPeer( + jni::JNIEnv& env, std::unique_ptr layer) { + assert(layer->baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, + new BackgroundLayer(std::unique_ptr( + static_cast(layer.release())))); + } + + void BackgroundJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize", + METHOD(&BackgroundLayer::getBackgroundColorTransition, "nativeGetBackgroundColorTransition"), + METHOD(&BackgroundLayer::setBackgroundColorTransition, "nativeSetBackgroundColorTransition"), + METHOD(&BackgroundLayer::getBackgroundColor, "nativeGetBackgroundColor"), + METHOD(&BackgroundLayer::getBackgroundPatternTransition, "nativeGetBackgroundPatternTransition"), + METHOD(&BackgroundLayer::setBackgroundPatternTransition, "nativeSetBackgroundPatternTransition"), + METHOD(&BackgroundLayer::getBackgroundPattern, "nativeGetBackgroundPattern"), + METHOD(&BackgroundLayer::getBackgroundOpacityTransition, "nativeGetBackgroundOpacityTransition"), + METHOD(&BackgroundLayer::setBackgroundOpacityTransition, "nativeSetBackgroundOpacityTransition"), + METHOD(&BackgroundLayer::getBackgroundOpacity, "nativeGetBackgroundOpacity")); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/background_layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/background_layer.hpp new file mode 100644 index 000000000..e402dc021 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/background_layer.hpp @@ -0,0 +1,58 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include +#include +#include + +namespace mbgl { +namespace android { + +class BackgroundLayer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/BackgroundLayer"; }; + + BackgroundLayer(jni::JNIEnv&, jni::String&); + + BackgroundLayer(mbgl::style::BackgroundLayer&); + + BackgroundLayer(std::unique_ptr); + + ~BackgroundLayer(); + + // Properties + + jni::Local> getBackgroundColor(jni::JNIEnv&); + void setBackgroundColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getBackgroundColorTransition(jni::JNIEnv&); + + jni::Local> getBackgroundPattern(jni::JNIEnv&); + void setBackgroundPatternTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getBackgroundPatternTransition(jni::JNIEnv&); + + jni::Local> getBackgroundOpacity(jni::JNIEnv&); + void setBackgroundOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getBackgroundOpacityTransition(jni::JNIEnv&); + +}; // class BackgroundLayer + +class BackgroundJavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::BackgroundLayerFactory { +public: + ~BackgroundJavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class BackgroundJavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/circle_layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/circle_layer.cpp new file mode 100644 index 000000000..a7fae5e23 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/circle_layer.cpp @@ -0,0 +1,270 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "circle_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +#include + +namespace mbgl { +namespace android { + + inline mbgl::style::CircleLayer& toCircleLayer(mbgl::style::Layer& layer) { + return static_cast(layer); + } + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + CircleLayer::CircleLayer(jni::JNIEnv& env, jni::String& layerId, jni::String& sourceId) + : Layer(std::make_unique(jni::Make(env, layerId), jni::Make(env, sourceId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + CircleLayer::CircleLayer(mbgl::style::CircleLayer& coreLayer) : Layer(coreLayer) {} + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + CircleLayer::CircleLayer(std::unique_ptr coreLayer) : Layer(std::move(coreLayer)) {} + + CircleLayer::~CircleLayer() = default; + + // Property getters + + jni::Local> CircleLayer::getCircleRadius(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCircleRadius())); + } + + jni::Local> CircleLayer::getCircleRadiusTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toCircleLayer(layer).getCircleRadiusTransition(); + return std::move(*convert>>(env, options)); + } + + void CircleLayer::setCircleRadiusTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toCircleLayer(layer).setCircleRadiusTransition(options); + } + + jni::Local> CircleLayer::getCircleColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCircleColor())); + } + + jni::Local> CircleLayer::getCircleColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toCircleLayer(layer).getCircleColorTransition(); + return std::move(*convert>>(env, options)); + } + + void CircleLayer::setCircleColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toCircleLayer(layer).setCircleColorTransition(options); + } + + jni::Local> CircleLayer::getCircleBlur(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCircleBlur())); + } + + jni::Local> CircleLayer::getCircleBlurTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toCircleLayer(layer).getCircleBlurTransition(); + return std::move(*convert>>(env, options)); + } + + void CircleLayer::setCircleBlurTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toCircleLayer(layer).setCircleBlurTransition(options); + } + + jni::Local> CircleLayer::getCircleOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCircleOpacity())); + } + + jni::Local> CircleLayer::getCircleOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toCircleLayer(layer).getCircleOpacityTransition(); + return std::move(*convert>>(env, options)); + } + + void CircleLayer::setCircleOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toCircleLayer(layer).setCircleOpacityTransition(options); + } + + jni::Local> CircleLayer::getCircleTranslate(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCircleTranslate())); + } + + jni::Local> CircleLayer::getCircleTranslateTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toCircleLayer(layer).getCircleTranslateTransition(); + return std::move(*convert>>(env, options)); + } + + void CircleLayer::setCircleTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toCircleLayer(layer).setCircleTranslateTransition(options); + } + + jni::Local> CircleLayer::getCircleTranslateAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCircleTranslateAnchor())); + } + + jni::Local> CircleLayer::getCirclePitchScale(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCirclePitchScale())); + } + + jni::Local> CircleLayer::getCirclePitchAlignment(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCirclePitchAlignment())); + } + + jni::Local> CircleLayer::getCircleStrokeWidth(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCircleStrokeWidth())); + } + + jni::Local> CircleLayer::getCircleStrokeWidthTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toCircleLayer(layer).getCircleStrokeWidthTransition(); + return std::move(*convert>>(env, options)); + } + + void CircleLayer::setCircleStrokeWidthTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toCircleLayer(layer).setCircleStrokeWidthTransition(options); + } + + jni::Local> CircleLayer::getCircleStrokeColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCircleStrokeColor())); + } + + jni::Local> CircleLayer::getCircleStrokeColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toCircleLayer(layer).getCircleStrokeColorTransition(); + return std::move(*convert>>(env, options)); + } + + void CircleLayer::setCircleStrokeColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toCircleLayer(layer).setCircleStrokeColorTransition(options); + } + + jni::Local> CircleLayer::getCircleStrokeOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toCircleLayer(layer).getCircleStrokeOpacity())); + } + + jni::Local> CircleLayer::getCircleStrokeOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toCircleLayer(layer).getCircleStrokeOpacityTransition(); + return std::move(*convert>>(env, options)); + } + + void CircleLayer::setCircleStrokeOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toCircleLayer(layer).setCircleStrokeOpacityTransition(options); + } + + + // CircleJavaLayerPeerFactory + + CircleJavaLayerPeerFactory::~CircleJavaLayerPeerFactory() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } // namespace + + jni::Local> CircleJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, + mbgl::style::Layer& layer) { + assert(layer.baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new CircleLayer(toCircleLayer(layer))); + } + + jni::Local> CircleJavaLayerPeerFactory::createJavaLayerPeer( + jni::JNIEnv& env, std::unique_ptr layer) { + assert(layer->baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, + new CircleLayer(std::unique_ptr( + static_cast(layer.release())))); + } + + void CircleJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize", + METHOD(&CircleLayer::getCircleRadiusTransition, "nativeGetCircleRadiusTransition"), + METHOD(&CircleLayer::setCircleRadiusTransition, "nativeSetCircleRadiusTransition"), + METHOD(&CircleLayer::getCircleRadius, "nativeGetCircleRadius"), + METHOD(&CircleLayer::getCircleColorTransition, "nativeGetCircleColorTransition"), + METHOD(&CircleLayer::setCircleColorTransition, "nativeSetCircleColorTransition"), + METHOD(&CircleLayer::getCircleColor, "nativeGetCircleColor"), + METHOD(&CircleLayer::getCircleBlurTransition, "nativeGetCircleBlurTransition"), + METHOD(&CircleLayer::setCircleBlurTransition, "nativeSetCircleBlurTransition"), + METHOD(&CircleLayer::getCircleBlur, "nativeGetCircleBlur"), + METHOD(&CircleLayer::getCircleOpacityTransition, "nativeGetCircleOpacityTransition"), + METHOD(&CircleLayer::setCircleOpacityTransition, "nativeSetCircleOpacityTransition"), + METHOD(&CircleLayer::getCircleOpacity, "nativeGetCircleOpacity"), + METHOD(&CircleLayer::getCircleTranslateTransition, "nativeGetCircleTranslateTransition"), + METHOD(&CircleLayer::setCircleTranslateTransition, "nativeSetCircleTranslateTransition"), + METHOD(&CircleLayer::getCircleTranslate, "nativeGetCircleTranslate"), + METHOD(&CircleLayer::getCircleTranslateAnchor, "nativeGetCircleTranslateAnchor"), + METHOD(&CircleLayer::getCirclePitchScale, "nativeGetCirclePitchScale"), + METHOD(&CircleLayer::getCirclePitchAlignment, "nativeGetCirclePitchAlignment"), + METHOD(&CircleLayer::getCircleStrokeWidthTransition, "nativeGetCircleStrokeWidthTransition"), + METHOD(&CircleLayer::setCircleStrokeWidthTransition, "nativeSetCircleStrokeWidthTransition"), + METHOD(&CircleLayer::getCircleStrokeWidth, "nativeGetCircleStrokeWidth"), + METHOD(&CircleLayer::getCircleStrokeColorTransition, "nativeGetCircleStrokeColorTransition"), + METHOD(&CircleLayer::setCircleStrokeColorTransition, "nativeSetCircleStrokeColorTransition"), + METHOD(&CircleLayer::getCircleStrokeColor, "nativeGetCircleStrokeColor"), + METHOD(&CircleLayer::getCircleStrokeOpacityTransition, "nativeGetCircleStrokeOpacityTransition"), + METHOD(&CircleLayer::setCircleStrokeOpacityTransition, "nativeSetCircleStrokeOpacityTransition"), + METHOD(&CircleLayer::getCircleStrokeOpacity, "nativeGetCircleStrokeOpacity")); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/circle_layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/circle_layer.hpp new file mode 100644 index 000000000..40dca91e2 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/circle_layer.hpp @@ -0,0 +1,84 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include +#include +#include + +namespace mbgl { +namespace android { + +class CircleLayer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/CircleLayer"; }; + + CircleLayer(jni::JNIEnv&, jni::String&, jni::String&); + + CircleLayer(mbgl::style::CircleLayer&); + + CircleLayer(std::unique_ptr); + + ~CircleLayer(); + + // Properties + + jni::Local> getCircleRadius(jni::JNIEnv&); + void setCircleRadiusTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getCircleRadiusTransition(jni::JNIEnv&); + + jni::Local> getCircleColor(jni::JNIEnv&); + void setCircleColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getCircleColorTransition(jni::JNIEnv&); + + jni::Local> getCircleBlur(jni::JNIEnv&); + void setCircleBlurTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getCircleBlurTransition(jni::JNIEnv&); + + jni::Local> getCircleOpacity(jni::JNIEnv&); + void setCircleOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getCircleOpacityTransition(jni::JNIEnv&); + + jni::Local> getCircleTranslate(jni::JNIEnv&); + void setCircleTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getCircleTranslateTransition(jni::JNIEnv&); + + jni::Local> getCircleTranslateAnchor(jni::JNIEnv&); + + jni::Local> getCirclePitchScale(jni::JNIEnv&); + + jni::Local> getCirclePitchAlignment(jni::JNIEnv&); + + jni::Local> getCircleStrokeWidth(jni::JNIEnv&); + void setCircleStrokeWidthTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getCircleStrokeWidthTransition(jni::JNIEnv&); + + jni::Local> getCircleStrokeColor(jni::JNIEnv&); + void setCircleStrokeColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getCircleStrokeColorTransition(jni::JNIEnv&); + + jni::Local> getCircleStrokeOpacity(jni::JNIEnv&); + void setCircleStrokeOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getCircleStrokeOpacityTransition(jni::JNIEnv&); + +}; // class CircleLayer + +class CircleJavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::CircleLayerFactory { +public: + ~CircleJavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class CircleJavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/custom_layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/custom_layer.cpp new file mode 100644 index 000000000..5a8b239a8 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/custom_layer.cpp @@ -0,0 +1,61 @@ +#include "custom_layer.hpp" + +#include + +#include + +namespace mbgl { +namespace android { + + CustomLayer::CustomLayer(jni::JNIEnv& env, const jni::String& layerId, jni::jlong host) + : Layer(std::make_unique( + jni::Make(env, layerId), + std::unique_ptr(reinterpret_cast(host))) + ) { + } + + CustomLayer::CustomLayer(mbgl::style::CustomLayer& coreLayer) : Layer(coreLayer) {} + + CustomLayer::CustomLayer(std::unique_ptr coreLayer) : Layer(std::move(coreLayer)) {} + + CustomLayer::~CustomLayer() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } + + CustomJavaLayerPeerFactory::~CustomJavaLayerPeerFactory() = default; + + jni::Local> CustomJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, + mbgl::style::Layer& layer) { + return createJavaPeer(env, new CustomLayer(static_cast(layer))); + } + + jni::Local> CustomJavaLayerPeerFactory::createJavaLayerPeer( + jni::JNIEnv& env, std::unique_ptr layer) { + return createJavaPeer(env, + new CustomLayer(std::unique_ptr( + static_cast(layer.release())))); + } + + void CustomJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer(env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize"); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/custom_layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/custom_layer.hpp new file mode 100644 index 000000000..3856ea73a --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/custom_layer.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include "layer.hpp" + +namespace mbgl { +namespace android { + +class CustomLayer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/CustomLayer"; }; + + static void registerNative(jni::JNIEnv&); + + CustomLayer(jni::JNIEnv&, const jni::String&, jni::jlong); + CustomLayer(mbgl::style::CustomLayer&); + CustomLayer(std::unique_ptr); + ~CustomLayer(); + + jni::Local> createJavaPeer(jni::JNIEnv&); +}; // class CustomLayer + +class CustomJavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::CustomLayerFactory { +public: + ~CustomJavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class CustomJavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/fill_extrusion_layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/fill_extrusion_layer.cpp new file mode 100644 index 000000000..55c3d2028 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/fill_extrusion_layer.cpp @@ -0,0 +1,225 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "fill_extrusion_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +#include + +namespace mbgl { +namespace android { + + inline mbgl::style::FillExtrusionLayer& toFillExtrusionLayer(mbgl::style::Layer& layer) { + return static_cast(layer); + } + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + FillExtrusionLayer::FillExtrusionLayer(jni::JNIEnv& env, jni::String& layerId, jni::String& sourceId) + : Layer(std::make_unique(jni::Make(env, layerId), jni::Make(env, sourceId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + FillExtrusionLayer::FillExtrusionLayer(mbgl::style::FillExtrusionLayer& coreLayer) : Layer(coreLayer) {} + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + FillExtrusionLayer::FillExtrusionLayer(std::unique_ptr coreLayer) + : Layer(std::move(coreLayer)) {} + + FillExtrusionLayer::~FillExtrusionLayer() = default; + + // Property getters + + jni::Local> FillExtrusionLayer::getFillExtrusionOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillExtrusionLayer(layer).getFillExtrusionOpacity())); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillExtrusionLayer(layer).getFillExtrusionOpacityTransition(); + return std::move(*convert>>(env, options)); + } + + void FillExtrusionLayer::setFillExtrusionOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillExtrusionLayer(layer).setFillExtrusionOpacityTransition(options); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillExtrusionLayer(layer).getFillExtrusionColor())); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillExtrusionLayer(layer).getFillExtrusionColorTransition(); + return std::move(*convert>>(env, options)); + } + + void FillExtrusionLayer::setFillExtrusionColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillExtrusionLayer(layer).setFillExtrusionColorTransition(options); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionTranslate(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillExtrusionLayer(layer).getFillExtrusionTranslate())); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionTranslateTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillExtrusionLayer(layer).getFillExtrusionTranslateTransition(); + return std::move(*convert>>(env, options)); + } + + void FillExtrusionLayer::setFillExtrusionTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillExtrusionLayer(layer).setFillExtrusionTranslateTransition(options); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionTranslateAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillExtrusionLayer(layer).getFillExtrusionTranslateAnchor())); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionPattern(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillExtrusionLayer(layer).getFillExtrusionPattern())); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionPatternTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillExtrusionLayer(layer).getFillExtrusionPatternTransition(); + return std::move(*convert>>(env, options)); + } + + void FillExtrusionLayer::setFillExtrusionPatternTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillExtrusionLayer(layer).setFillExtrusionPatternTransition(options); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionHeight(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillExtrusionLayer(layer).getFillExtrusionHeight())); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionHeightTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillExtrusionLayer(layer).getFillExtrusionHeightTransition(); + return std::move(*convert>>(env, options)); + } + + void FillExtrusionLayer::setFillExtrusionHeightTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillExtrusionLayer(layer).setFillExtrusionHeightTransition(options); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionBase(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillExtrusionLayer(layer).getFillExtrusionBase())); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionBaseTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillExtrusionLayer(layer).getFillExtrusionBaseTransition(); + return std::move(*convert>>(env, options)); + } + + void FillExtrusionLayer::setFillExtrusionBaseTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillExtrusionLayer(layer).setFillExtrusionBaseTransition(options); + } + + jni::Local> FillExtrusionLayer::getFillExtrusionVerticalGradient(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillExtrusionLayer(layer).getFillExtrusionVerticalGradient())); + } + + + // FillExtrusionJavaLayerPeerFactory + + FillExtrusionJavaLayerPeerFactory::~FillExtrusionJavaLayerPeerFactory() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } // namespace + + jni::Local> FillExtrusionJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, + mbgl::style::Layer& layer) { + assert(layer.baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new FillExtrusionLayer(toFillExtrusionLayer(layer))); + } + + jni::Local> FillExtrusionJavaLayerPeerFactory::createJavaLayerPeer( + jni::JNIEnv& env, std::unique_ptr layer) { + assert(layer->baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, + new FillExtrusionLayer(std::unique_ptr( + static_cast(layer.release())))); + } + + void FillExtrusionJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize", + METHOD(&FillExtrusionLayer::getFillExtrusionOpacityTransition, "nativeGetFillExtrusionOpacityTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionOpacityTransition, "nativeSetFillExtrusionOpacityTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionOpacity, "nativeGetFillExtrusionOpacity"), + METHOD(&FillExtrusionLayer::getFillExtrusionColorTransition, "nativeGetFillExtrusionColorTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionColorTransition, "nativeSetFillExtrusionColorTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionColor, "nativeGetFillExtrusionColor"), + METHOD(&FillExtrusionLayer::getFillExtrusionTranslateTransition, + "nativeGetFillExtrusionTranslateTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionTranslateTransition, + "nativeSetFillExtrusionTranslateTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionTranslate, "nativeGetFillExtrusionTranslate"), + METHOD(&FillExtrusionLayer::getFillExtrusionTranslateAnchor, "nativeGetFillExtrusionTranslateAnchor"), + METHOD(&FillExtrusionLayer::getFillExtrusionPatternTransition, "nativeGetFillExtrusionPatternTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionPatternTransition, "nativeSetFillExtrusionPatternTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionPattern, "nativeGetFillExtrusionPattern"), + METHOD(&FillExtrusionLayer::getFillExtrusionHeightTransition, "nativeGetFillExtrusionHeightTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionHeightTransition, "nativeSetFillExtrusionHeightTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionHeight, "nativeGetFillExtrusionHeight"), + METHOD(&FillExtrusionLayer::getFillExtrusionBaseTransition, "nativeGetFillExtrusionBaseTransition"), + METHOD(&FillExtrusionLayer::setFillExtrusionBaseTransition, "nativeSetFillExtrusionBaseTransition"), + METHOD(&FillExtrusionLayer::getFillExtrusionBase, "nativeGetFillExtrusionBase"), + METHOD(&FillExtrusionLayer::getFillExtrusionVerticalGradient, "nativeGetFillExtrusionVerticalGradient")); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/fill_extrusion_layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/fill_extrusion_layer.hpp new file mode 100644 index 000000000..6f0a24131 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/fill_extrusion_layer.hpp @@ -0,0 +1,74 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include +#include +#include + +namespace mbgl { +namespace android { + +class FillExtrusionLayer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer"; }; + + FillExtrusionLayer(jni::JNIEnv&, jni::String&, jni::String&); + + FillExtrusionLayer(mbgl::style::FillExtrusionLayer&); + + FillExtrusionLayer(std::unique_ptr); + + ~FillExtrusionLayer(); + + // Properties + + jni::Local> getFillExtrusionOpacity(jni::JNIEnv&); + void setFillExtrusionOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillExtrusionOpacityTransition(jni::JNIEnv&); + + jni::Local> getFillExtrusionColor(jni::JNIEnv&); + void setFillExtrusionColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillExtrusionColorTransition(jni::JNIEnv&); + + jni::Local> getFillExtrusionTranslate(jni::JNIEnv&); + void setFillExtrusionTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillExtrusionTranslateTransition(jni::JNIEnv&); + + jni::Local> getFillExtrusionTranslateAnchor(jni::JNIEnv&); + + jni::Local> getFillExtrusionPattern(jni::JNIEnv&); + void setFillExtrusionPatternTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillExtrusionPatternTransition(jni::JNIEnv&); + + jni::Local> getFillExtrusionHeight(jni::JNIEnv&); + void setFillExtrusionHeightTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillExtrusionHeightTransition(jni::JNIEnv&); + + jni::Local> getFillExtrusionBase(jni::JNIEnv&); + void setFillExtrusionBaseTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillExtrusionBaseTransition(jni::JNIEnv&); + + jni::Local> getFillExtrusionVerticalGradient(jni::JNIEnv&); + +}; // class FillExtrusionLayer + +class FillExtrusionJavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::FillExtrusionLayerFactory { +public: + ~FillExtrusionJavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class FillExtrusionJavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/fill_layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/fill_layer.cpp new file mode 100644 index 000000000..6a646fdc7 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/fill_layer.cpp @@ -0,0 +1,207 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "fill_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +#include + +namespace mbgl { +namespace android { + + inline mbgl::style::FillLayer& toFillLayer(mbgl::style::Layer& layer) { + return static_cast(layer); + } + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + FillLayer::FillLayer(jni::JNIEnv& env, jni::String& layerId, jni::String& sourceId) + : Layer(std::make_unique(jni::Make(env, layerId), jni::Make(env, sourceId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + FillLayer::FillLayer(mbgl::style::FillLayer& coreLayer) : Layer(coreLayer) {} + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + FillLayer::FillLayer(std::unique_ptr coreLayer) : Layer(std::move(coreLayer)) {} + + FillLayer::~FillLayer() = default; + + // Property getters + + jni::Local> FillLayer::getFillSortKey(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillLayer(layer).getFillSortKey())); + } + + jni::Local> FillLayer::getFillAntialias(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillLayer(layer).getFillAntialias())); + } + + jni::Local> FillLayer::getFillOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillLayer(layer).getFillOpacity())); + } + + jni::Local> FillLayer::getFillOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillLayer(layer).getFillOpacityTransition(); + return std::move(*convert>>(env, options)); + } + + void FillLayer::setFillOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillLayer(layer).setFillOpacityTransition(options); + } + + jni::Local> FillLayer::getFillColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillLayer(layer).getFillColor())); + } + + jni::Local> FillLayer::getFillColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillLayer(layer).getFillColorTransition(); + return std::move(*convert>>(env, options)); + } + + void FillLayer::setFillColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillLayer(layer).setFillColorTransition(options); + } + + jni::Local> FillLayer::getFillOutlineColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillLayer(layer).getFillOutlineColor())); + } + + jni::Local> FillLayer::getFillOutlineColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillLayer(layer).getFillOutlineColorTransition(); + return std::move(*convert>>(env, options)); + } + + void FillLayer::setFillOutlineColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillLayer(layer).setFillOutlineColorTransition(options); + } + + jni::Local> FillLayer::getFillTranslate(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillLayer(layer).getFillTranslate())); + } + + jni::Local> FillLayer::getFillTranslateTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillLayer(layer).getFillTranslateTransition(); + return std::move(*convert>>(env, options)); + } + + void FillLayer::setFillTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillLayer(layer).setFillTranslateTransition(options); + } + + jni::Local> FillLayer::getFillTranslateAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillLayer(layer).getFillTranslateAnchor())); + } + + jni::Local> FillLayer::getFillPattern(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toFillLayer(layer).getFillPattern())); + } + + jni::Local> FillLayer::getFillPatternTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toFillLayer(layer).getFillPatternTransition(); + return std::move(*convert>>(env, options)); + } + + void FillLayer::setFillPatternTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toFillLayer(layer).setFillPatternTransition(options); + } + + + // FillJavaLayerPeerFactory + + FillJavaLayerPeerFactory::~FillJavaLayerPeerFactory() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } // namespace + + jni::Local> FillJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, + mbgl::style::Layer& layer) { + assert(layer.baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new FillLayer(toFillLayer(layer))); + } + + jni::Local> FillJavaLayerPeerFactory::createJavaLayerPeer( + jni::JNIEnv& env, std::unique_ptr layer) { + assert(layer->baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, + new FillLayer(std::unique_ptr( + static_cast(layer.release())))); + } + + void FillJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize", + METHOD(&FillLayer::getFillSortKey, "nativeGetFillSortKey"), + METHOD(&FillLayer::getFillAntialias, "nativeGetFillAntialias"), + METHOD(&FillLayer::getFillOpacityTransition, "nativeGetFillOpacityTransition"), + METHOD(&FillLayer::setFillOpacityTransition, "nativeSetFillOpacityTransition"), + METHOD(&FillLayer::getFillOpacity, "nativeGetFillOpacity"), + METHOD(&FillLayer::getFillColorTransition, "nativeGetFillColorTransition"), + METHOD(&FillLayer::setFillColorTransition, "nativeSetFillColorTransition"), + METHOD(&FillLayer::getFillColor, "nativeGetFillColor"), + METHOD(&FillLayer::getFillOutlineColorTransition, "nativeGetFillOutlineColorTransition"), + METHOD(&FillLayer::setFillOutlineColorTransition, "nativeSetFillOutlineColorTransition"), + METHOD(&FillLayer::getFillOutlineColor, "nativeGetFillOutlineColor"), + METHOD(&FillLayer::getFillTranslateTransition, "nativeGetFillTranslateTransition"), + METHOD(&FillLayer::setFillTranslateTransition, "nativeSetFillTranslateTransition"), + METHOD(&FillLayer::getFillTranslate, "nativeGetFillTranslate"), + METHOD(&FillLayer::getFillTranslateAnchor, "nativeGetFillTranslateAnchor"), + METHOD(&FillLayer::getFillPatternTransition, "nativeGetFillPatternTransition"), + METHOD(&FillLayer::setFillPatternTransition, "nativeSetFillPatternTransition"), + METHOD(&FillLayer::getFillPattern, "nativeGetFillPattern")); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/fill_layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/fill_layer.hpp new file mode 100644 index 000000000..807b939a7 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/fill_layer.hpp @@ -0,0 +1,72 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include +#include +#include + +namespace mbgl { +namespace android { + +class FillLayer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/FillLayer"; }; + + FillLayer(jni::JNIEnv&, jni::String&, jni::String&); + + FillLayer(mbgl::style::FillLayer&); + + FillLayer(std::unique_ptr); + + ~FillLayer(); + + // Properties + + jni::Local> getFillSortKey(jni::JNIEnv&); + + jni::Local> getFillAntialias(jni::JNIEnv&); + + jni::Local> getFillOpacity(jni::JNIEnv&); + void setFillOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillOpacityTransition(jni::JNIEnv&); + + jni::Local> getFillColor(jni::JNIEnv&); + void setFillColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillColorTransition(jni::JNIEnv&); + + jni::Local> getFillOutlineColor(jni::JNIEnv&); + void setFillOutlineColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillOutlineColorTransition(jni::JNIEnv&); + + jni::Local> getFillTranslate(jni::JNIEnv&); + void setFillTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillTranslateTransition(jni::JNIEnv&); + + jni::Local> getFillTranslateAnchor(jni::JNIEnv&); + + jni::Local> getFillPattern(jni::JNIEnv&); + void setFillPatternTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getFillPatternTransition(jni::JNIEnv&); + +}; // class FillLayer + +class FillJavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::FillLayerFactory { +public: + ~FillJavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class FillJavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/heatmap_layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/heatmap_layer.cpp new file mode 100644 index 000000000..dad3d25e6 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/heatmap_layer.cpp @@ -0,0 +1,163 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "heatmap_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +#include + +namespace mbgl { +namespace android { + + inline mbgl::style::HeatmapLayer& toHeatmapLayer(mbgl::style::Layer& layer) { + return static_cast(layer); + } + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + HeatmapLayer::HeatmapLayer(jni::JNIEnv& env, jni::String& layerId, jni::String& sourceId) + : Layer(std::make_unique(jni::Make(env, layerId), jni::Make(env, sourceId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + HeatmapLayer::HeatmapLayer(mbgl::style::HeatmapLayer& coreLayer) : Layer(coreLayer) {} + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + HeatmapLayer::HeatmapLayer(std::unique_ptr coreLayer) : Layer(std::move(coreLayer)) {} + + HeatmapLayer::~HeatmapLayer() = default; + + // Property getters + + jni::Local> HeatmapLayer::getHeatmapRadius(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toHeatmapLayer(layer).getHeatmapRadius())); + } + + jni::Local> HeatmapLayer::getHeatmapRadiusTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toHeatmapLayer(layer).getHeatmapRadiusTransition(); + return std::move(*convert>>(env, options)); + } + + void HeatmapLayer::setHeatmapRadiusTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toHeatmapLayer(layer).setHeatmapRadiusTransition(options); + } + + jni::Local> HeatmapLayer::getHeatmapWeight(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toHeatmapLayer(layer).getHeatmapWeight())); + } + + jni::Local> HeatmapLayer::getHeatmapIntensity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toHeatmapLayer(layer).getHeatmapIntensity())); + } + + jni::Local> HeatmapLayer::getHeatmapIntensityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toHeatmapLayer(layer).getHeatmapIntensityTransition(); + return std::move(*convert>>(env, options)); + } + + void HeatmapLayer::setHeatmapIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toHeatmapLayer(layer).setHeatmapIntensityTransition(options); + } + + jni::Local> HeatmapLayer::getHeatmapColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + auto propertyValue = toHeatmapLayer(layer).getHeatmapColor(); + if (propertyValue.isUndefined()) { + propertyValue = toHeatmapLayer(layer).getDefaultHeatmapColor(); + } + return std::move(*convert>>(env, propertyValue)); + } + + jni::Local> HeatmapLayer::getHeatmapOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toHeatmapLayer(layer).getHeatmapOpacity())); + } + + jni::Local> HeatmapLayer::getHeatmapOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toHeatmapLayer(layer).getHeatmapOpacityTransition(); + return std::move(*convert>>(env, options)); + } + + void HeatmapLayer::setHeatmapOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toHeatmapLayer(layer).setHeatmapOpacityTransition(options); + } + + + // HeatmapJavaLayerPeerFactory + + HeatmapJavaLayerPeerFactory::~HeatmapJavaLayerPeerFactory() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } // namespace + + jni::Local> HeatmapJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, + mbgl::style::Layer& layer) { + assert(layer.baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new HeatmapLayer(toHeatmapLayer(layer))); + } + + jni::Local> HeatmapJavaLayerPeerFactory::createJavaLayerPeer( + jni::JNIEnv& env, std::unique_ptr layer) { + assert(layer->baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, + new HeatmapLayer(std::unique_ptr( + static_cast(layer.release())))); + } + + void HeatmapJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize", + METHOD(&HeatmapLayer::getHeatmapRadiusTransition, "nativeGetHeatmapRadiusTransition"), + METHOD(&HeatmapLayer::setHeatmapRadiusTransition, "nativeSetHeatmapRadiusTransition"), + METHOD(&HeatmapLayer::getHeatmapRadius, "nativeGetHeatmapRadius"), + METHOD(&HeatmapLayer::getHeatmapWeight, "nativeGetHeatmapWeight"), + METHOD(&HeatmapLayer::getHeatmapIntensityTransition, "nativeGetHeatmapIntensityTransition"), + METHOD(&HeatmapLayer::setHeatmapIntensityTransition, "nativeSetHeatmapIntensityTransition"), + METHOD(&HeatmapLayer::getHeatmapIntensity, "nativeGetHeatmapIntensity"), + METHOD(&HeatmapLayer::getHeatmapColor, "nativeGetHeatmapColor"), + METHOD(&HeatmapLayer::getHeatmapOpacityTransition, "nativeGetHeatmapOpacityTransition"), + METHOD(&HeatmapLayer::setHeatmapOpacityTransition, "nativeSetHeatmapOpacityTransition"), + METHOD(&HeatmapLayer::getHeatmapOpacity, "nativeGetHeatmapOpacity")); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/heatmap_layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/heatmap_layer.hpp new file mode 100644 index 000000000..f56e3eb4a --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/heatmap_layer.hpp @@ -0,0 +1,62 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include +#include +#include + +namespace mbgl { +namespace android { + +class HeatmapLayer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/HeatmapLayer"; }; + + HeatmapLayer(jni::JNIEnv&, jni::String&, jni::String&); + + HeatmapLayer(mbgl::style::HeatmapLayer&); + + HeatmapLayer(std::unique_ptr); + + ~HeatmapLayer(); + + // Properties + + jni::Local> getHeatmapRadius(jni::JNIEnv&); + void setHeatmapRadiusTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getHeatmapRadiusTransition(jni::JNIEnv&); + + jni::Local> getHeatmapWeight(jni::JNIEnv&); + + jni::Local> getHeatmapIntensity(jni::JNIEnv&); + void setHeatmapIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getHeatmapIntensityTransition(jni::JNIEnv&); + + jni::Local> getHeatmapColor(jni::JNIEnv&); + + jni::Local> getHeatmapOpacity(jni::JNIEnv&); + void setHeatmapOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getHeatmapOpacityTransition(jni::JNIEnv&); + +}; // class HeatmapLayer + +class HeatmapJavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::HeatmapLayerFactory { +public: + ~HeatmapJavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class HeatmapJavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/hillshade_layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/hillshade_layer.cpp new file mode 100644 index 000000000..1f5fe1912 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/hillshade_layer.cpp @@ -0,0 +1,181 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "hillshade_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +#include + +namespace mbgl { +namespace android { + + inline mbgl::style::HillshadeLayer& toHillshadeLayer(mbgl::style::Layer& layer) { + return static_cast(layer); + } + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + HillshadeLayer::HillshadeLayer(jni::JNIEnv& env, jni::String& layerId, jni::String& sourceId) + : Layer(std::make_unique(jni::Make(env, layerId), jni::Make(env, sourceId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + HillshadeLayer::HillshadeLayer(mbgl::style::HillshadeLayer& coreLayer) : Layer(coreLayer) {} + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + HillshadeLayer::HillshadeLayer(std::unique_ptr coreLayer) + : Layer(std::move(coreLayer)) {} + + HillshadeLayer::~HillshadeLayer() = default; + + // Property getters + + jni::Local> HillshadeLayer::getHillshadeIlluminationDirection(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toHillshadeLayer(layer).getHillshadeIlluminationDirection())); + } + + jni::Local> HillshadeLayer::getHillshadeIlluminationAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toHillshadeLayer(layer).getHillshadeIlluminationAnchor())); + } + + jni::Local> HillshadeLayer::getHillshadeExaggeration(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toHillshadeLayer(layer).getHillshadeExaggeration())); + } + + jni::Local> HillshadeLayer::getHillshadeExaggerationTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toHillshadeLayer(layer).getHillshadeExaggerationTransition(); + return std::move(*convert>>(env, options)); + } + + void HillshadeLayer::setHillshadeExaggerationTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toHillshadeLayer(layer).setHillshadeExaggerationTransition(options); + } + + jni::Local> HillshadeLayer::getHillshadeShadowColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toHillshadeLayer(layer).getHillshadeShadowColor())); + } + + jni::Local> HillshadeLayer::getHillshadeShadowColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toHillshadeLayer(layer).getHillshadeShadowColorTransition(); + return std::move(*convert>>(env, options)); + } + + void HillshadeLayer::setHillshadeShadowColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toHillshadeLayer(layer).setHillshadeShadowColorTransition(options); + } + + jni::Local> HillshadeLayer::getHillshadeHighlightColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toHillshadeLayer(layer).getHillshadeHighlightColor())); + } + + jni::Local> HillshadeLayer::getHillshadeHighlightColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toHillshadeLayer(layer).getHillshadeHighlightColorTransition(); + return std::move(*convert>>(env, options)); + } + + void HillshadeLayer::setHillshadeHighlightColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toHillshadeLayer(layer).setHillshadeHighlightColorTransition(options); + } + + jni::Local> HillshadeLayer::getHillshadeAccentColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toHillshadeLayer(layer).getHillshadeAccentColor())); + } + + jni::Local> HillshadeLayer::getHillshadeAccentColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toHillshadeLayer(layer).getHillshadeAccentColorTransition(); + return std::move(*convert>>(env, options)); + } + + void HillshadeLayer::setHillshadeAccentColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toHillshadeLayer(layer).setHillshadeAccentColorTransition(options); + } + + + // HillshadeJavaLayerPeerFactory + + HillshadeJavaLayerPeerFactory::~HillshadeJavaLayerPeerFactory() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } // namespace + + jni::Local> HillshadeJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, + mbgl::style::Layer& layer) { + assert(layer.baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new HillshadeLayer(toHillshadeLayer(layer))); + } + + jni::Local> HillshadeJavaLayerPeerFactory::createJavaLayerPeer( + jni::JNIEnv& env, std::unique_ptr layer) { + assert(layer->baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, + new HillshadeLayer(std::unique_ptr( + static_cast(layer.release())))); + } + + void HillshadeJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize", + METHOD(&HillshadeLayer::getHillshadeIlluminationDirection, "nativeGetHillshadeIlluminationDirection"), + METHOD(&HillshadeLayer::getHillshadeIlluminationAnchor, "nativeGetHillshadeIlluminationAnchor"), + METHOD(&HillshadeLayer::getHillshadeExaggerationTransition, "nativeGetHillshadeExaggerationTransition"), + METHOD(&HillshadeLayer::setHillshadeExaggerationTransition, "nativeSetHillshadeExaggerationTransition"), + METHOD(&HillshadeLayer::getHillshadeExaggeration, "nativeGetHillshadeExaggeration"), + METHOD(&HillshadeLayer::getHillshadeShadowColorTransition, "nativeGetHillshadeShadowColorTransition"), + METHOD(&HillshadeLayer::setHillshadeShadowColorTransition, "nativeSetHillshadeShadowColorTransition"), + METHOD(&HillshadeLayer::getHillshadeShadowColor, "nativeGetHillshadeShadowColor"), + METHOD(&HillshadeLayer::getHillshadeHighlightColorTransition, "nativeGetHillshadeHighlightColorTransition"), + METHOD(&HillshadeLayer::setHillshadeHighlightColorTransition, "nativeSetHillshadeHighlightColorTransition"), + METHOD(&HillshadeLayer::getHillshadeHighlightColor, "nativeGetHillshadeHighlightColor"), + METHOD(&HillshadeLayer::getHillshadeAccentColorTransition, "nativeGetHillshadeAccentColorTransition"), + METHOD(&HillshadeLayer::setHillshadeAccentColorTransition, "nativeSetHillshadeAccentColorTransition"), + METHOD(&HillshadeLayer::getHillshadeAccentColor, "nativeGetHillshadeAccentColor")); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/hillshade_layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/hillshade_layer.hpp new file mode 100644 index 000000000..bc210a998 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/hillshade_layer.hpp @@ -0,0 +1,66 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include +#include +#include + +namespace mbgl { +namespace android { + +class HillshadeLayer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/HillshadeLayer"; }; + + HillshadeLayer(jni::JNIEnv&, jni::String&, jni::String&); + + HillshadeLayer(mbgl::style::HillshadeLayer&); + + HillshadeLayer(std::unique_ptr); + + ~HillshadeLayer(); + + // Properties + + jni::Local> getHillshadeIlluminationDirection(jni::JNIEnv&); + + jni::Local> getHillshadeIlluminationAnchor(jni::JNIEnv&); + + jni::Local> getHillshadeExaggeration(jni::JNIEnv&); + void setHillshadeExaggerationTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getHillshadeExaggerationTransition(jni::JNIEnv&); + + jni::Local> getHillshadeShadowColor(jni::JNIEnv&); + void setHillshadeShadowColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getHillshadeShadowColorTransition(jni::JNIEnv&); + + jni::Local> getHillshadeHighlightColor(jni::JNIEnv&); + void setHillshadeHighlightColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getHillshadeHighlightColorTransition(jni::JNIEnv&); + + jni::Local> getHillshadeAccentColor(jni::JNIEnv&); + void setHillshadeAccentColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getHillshadeAccentColorTransition(jni::JNIEnv&); + +}; // class HillshadeLayer + +class HillshadeJavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::HillshadeLayerFactory { +public: + ~HillshadeJavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class HillshadeJavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/layer.cpp new file mode 100644 index 000000000..bc87a9d9b --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/layer.cpp @@ -0,0 +1,178 @@ +#include "layer.hpp" +#include "../android_conversion.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Java -> C++ conversion +#include +#include +#include +#include + +// C++ -> Java conversion +#include "../conversion/property_value.hpp" +#include "custom_layer.hpp" +#include "background_layer.hpp" +#include + +#include + +namespace mbgl { +namespace android { + + /** + * Invoked when the construction is initiated from the jvm through a subclass + */ + Layer::Layer(std::unique_ptr coreLayer) + : ownedLayer(std::move(coreLayer)) + , layer(*ownedLayer) { + } + + /** + * Takes a non-owning reference. For lookup methods + */ + Layer::Layer(mbgl::style::Layer& coreLayer) : layer(coreLayer) {} + + Layer::~Layer() { + } + + void Layer::addToStyle(mbgl::style::Style& style, mbgl::optional before) { + // Check to see if we own the layer first + if (!ownedLayer) { + throw std::runtime_error("Cannot add layer twice"); + } + + // Add layer to map + style.addLayer(releaseCoreLayer(), before); + } + + void Layer::setLayer(std::unique_ptr sourceLayer) { + this->ownedLayer = std::move(sourceLayer); + } + + std::unique_ptr Layer::releaseCoreLayer() { + assert(ownedLayer != nullptr); + return std::move(ownedLayer); + } + + jni::Local Layer::getId(jni::JNIEnv& env) { + return jni::Make(env, layer.getID()); + } + + style::Layer& Layer::get() { + return layer; + } + + void Layer::setProperty(jni::JNIEnv& env, const jni::String& jname, const jni::Object<>& jvalue) { + // Convert and set property + optional error = + layer.setProperty(jni::Make(env, jname), Value(env, jvalue)); + if (error) { + mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make(env, jname) + " " + error->message); + return; + } + } + + void Layer::setFilter(jni::JNIEnv& env, const jni::Array>& jfilter) { + using namespace mbgl::style; + using namespace mbgl::style::conversion; + + Error error; + optional converted = convert(Value(env, jfilter), error); + if (!converted) { + mbgl::Log::Error(mbgl::Event::JNI, "Error setting filter: " + error.message); + return; + } + + layer.setFilter(std::move(*converted)); + } + + jni::Local> Layer::getFilter(jni::JNIEnv& env) { + using namespace mbgl::style; + using namespace mbgl::style::conversion; + + Filter filter = layer.getFilter(); + if (filter.expression) { + mbgl::Value expressionValue = (*filter.expression)->serialize(); + return gson::JsonElement::New(env, expressionValue); + } else { + return jni::Local>(env, nullptr); + } + } + + void Layer::setSourceLayer(jni::JNIEnv& env, const jni::String& sourceLayer) { + layer.setSourceLayer(jni::Make(env, sourceLayer)); + } + + jni::Local Layer::getSourceLayer(jni::JNIEnv& env) { + return jni::Make(env, layer.getSourceLayer()); + } + + jni::Local Layer::getSourceId(jni::JNIEnv& env) { + return jni::Make(env, layer.getSourceID()); + } + + jni::jfloat Layer::getMinZoom(jni::JNIEnv&){ + return layer.getMinZoom(); + } + + jni::jfloat Layer::getMaxZoom(jni::JNIEnv&) { + return layer.getMaxZoom(); + } + + void Layer::setMinZoom(jni::JNIEnv&, jni::jfloat zoom) { + layer.setMinZoom(zoom); + } + + void Layer::setMaxZoom(jni::JNIEnv&, jni::jfloat zoom) { + layer.setMaxZoom(zoom); + } + + jni::Local> Layer::getVisibility(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, layer.getVisibility())); + } + + void Layer::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + METHOD(&Layer::getId, "nativeGetId"), + METHOD(&Layer::setProperty, + "nativeSetLayoutProperty"), // TODO : Export only nativeSetProperty() when #15970 lands. + METHOD(&Layer::setProperty, "nativeSetPaintProperty"), + METHOD(&Layer::setFilter, "nativeSetFilter"), + METHOD(&Layer::getFilter, "nativeGetFilter"), + METHOD(&Layer::setSourceLayer, "nativeSetSourceLayer"), + METHOD(&Layer::getSourceLayer, "nativeGetSourceLayer"), + METHOD(&Layer::getSourceId, "nativeGetSourceId"), + METHOD(&Layer::getMinZoom, "nativeGetMinZoom"), + METHOD(&Layer::getMaxZoom, "nativeGetMaxZoom"), + METHOD(&Layer::setMinZoom, "nativeSetMinZoom"), + METHOD(&Layer::setMaxZoom, "nativeSetMaxZoom"), + METHOD(&Layer::getVisibility, "nativeGetVisibility")); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/layer.cpp.ejs b/MapboxGLAndroidSDK/src/cpp/style/layers/layer.cpp.ejs new file mode 100644 index 000000000..5d241e9a3 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/layer.cpp.ejs @@ -0,0 +1,139 @@ +<% + const type = locals.type; + const properties = locals.properties; +-%> +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "<%- type.replace('-', '_') %>_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +#include + +namespace mbgl { +namespace android { + + inline mbgl::style::<%- camelize(type) %>Layer& to<%- camelize(type) %>Layer(mbgl::style::Layer& layer) { + return static_castLayer&>(layer); + } + +<% if (type === 'background') { -%> + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(jni::JNIEnv& env, jni::String& layerId) + : Layer(std::make_uniqueLayer>(jni::Make(env, layerId))) { +<% } else { -%> + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(jni::JNIEnv& env, jni::String& layerId, jni::String& sourceId) + : Layer(std::make_uniqueLayer>(jni::Make(env, layerId), jni::Make(env, sourceId))) { +<% } -%> + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(mbgl::style::<%- camelize(type) %>Layer& coreLayer) + : Layer(coreLayer) { + } + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(std::unique_ptrLayer> coreLayer) + : Layer(std::move(coreLayer)) { + } + + <%- camelize(type) %>Layer::~<%- camelize(type) %>Layer() = default; + + // Property getters + +<% for (const property of properties) { -%> +<% if (property.name != 'heatmap-color') { -%> + jni::Local> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, to<%- camelize(type) %>Layer(layer).get<%- camelize(property.name) %>())); + } + +<% } else { -%> + jni::Local> HeatmapLayer::getHeatmapColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + auto propertyValue = to<%- camelize(type) %>Layer(layer).getHeatmapColor(); + if (propertyValue.isUndefined()) { + propertyValue = to<%- camelize(type) %>Layer(layer).getDefaultHeatmapColor(); + } + return std::move(*convert>>(env, propertyValue)); + } + +<% } -%> +<% if (property.transition) { -%> + jni::Local> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>Transition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = to<%- camelize(type) %>Layer(layer).get<%- camelize(property.name) %>Transition(); + return std::move(*convert>>(env, options)); + } + + void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>Transition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + to<%- camelize(type) %>Layer(layer).set<%- camelize(property.name) %>Transition(options); + } + +<% } -%> +<% } -%> + + // <%- camelize(type) %>JavaLayerPeerFactory + + <%- camelize(type) %>JavaLayerPeerFactory::~<%- camelize(type) %>JavaLayerPeerFactory() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class<<%- camelize(type) %>Layer>::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } // namespace + + jni::Local> <%- camelize(type) %>JavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, mbgl::style::Layer& layer) { + assert(layer.baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new <%- camelize(type) %>Layer(to<%- camelize(type) %>Layer(layer))); + } + + jni::Local> <%- camelize(type) %>JavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr layer) { + assert(layer->baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new <%- camelize(type) %>Layer(std::unique_ptrLayer>(static_castLayer*>(layer.release())))); + } + + void <%- camelize(type) %>JavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class<<%- camelize(type) %>Layer>::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer<<%- camelize(type) %>Layer>( + env, + javaClass, + "nativePtr", +<% if (type === 'background') { -%> + jni::MakePeer<<%- camelize(type) %>Layer, jni::String&>, +<% } else { -%> + jni::MakePeer<<%- camelize(type) %>Layer, jni::String&, jni::String&>, +<% } -%> + "initialize", + "finalize",<% for(var i = 0; i < properties.length; i++) {%> +<% if (properties[i].transition) { -%> + METHOD(&<%- camelize(type) %>Layer::get<%- camelize(properties[i].name) %>Transition, "nativeGet<%- camelize(properties[i].name) %>Transition"), + METHOD(&<%- camelize(type) %>Layer::set<%- camelize(properties[i].name) %>Transition, "nativeSet<%- camelize(properties[i].name) %>Transition"), +<% } -%> + METHOD(&<%- camelize(type) %>Layer::get<%- camelize(properties[i].name) %>, "nativeGet<%- camelize(properties[i].name) %>")<% if(i != (properties.length -1)) {-%>,<% } -%><% } -%>); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/layer.hpp new file mode 100644 index 000000000..557990844 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/layer.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include +#include "../../gson/json_array.hpp" +#include "../../gson/json_element.hpp" +#include "../value.hpp" + +#include + +namespace mbgl { + +namespace android { + +class Layer { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/Layer"; }; + + static void registerNative(jni::JNIEnv&); + + virtual ~Layer(); + + /** + * Set core layer (ie return ownership after remove) + */ + void setLayer(std::unique_ptr); + + void addToStyle(mbgl::style::Style&, mbgl::optional); + + // Release the owned view and return it + std::unique_ptr releaseCoreLayer(); + + jni::Local getId(jni::JNIEnv&); + + jni::Local getSourceId(jni::JNIEnv&); + + style::Layer& get(); + + void setProperty(jni::JNIEnv&, const jni::String&, const jni::Object<>& value); + + // Zoom + + jni::jfloat getMinZoom(jni::JNIEnv&); + + jni::jfloat getMaxZoom(jni::JNIEnv&); + + void setMinZoom(jni::JNIEnv&, jni::jfloat zoom); + + void setMaxZoom(jni::JNIEnv&, jni::jfloat zoom); + + /* common properties, but not shared by all */ + + void setFilter(jni::JNIEnv&, const jni::Array>&); + + jni::Local> getFilter(jni::JNIEnv&); + + void setSourceLayer(jni::JNIEnv&, const jni::String&); + + jni::Local getSourceLayer(jni::JNIEnv&); + + // Property getters + + jni::Local> getVisibility(jni::JNIEnv&); + +protected: + /* + * Called when a non-owning peer object is created on the c++ side + */ + Layer(mbgl::style::Layer&); + + /* + * Called when a owning peer object is created on the c++ side + */ + Layer(std::unique_ptr); + + // Owned layer is set when creating a new layer, before adding it to the map + std::unique_ptr ownedLayer; + + // Raw reference to the layer + mbgl::style::Layer& layer; +}; + +/** + * @brief A factory class for a layer Java peer objects of a certain type. + */ +class JavaLayerPeerFactory { +public: + virtual ~JavaLayerPeerFactory() = default; + /** + * @brief Create a non-owning peer. + */ + virtual jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) = 0; + + /** + * @brief Create an owning peer. + */ + virtual jni::Local> createJavaLayerPeer(jni::JNIEnv& env, + std::unique_ptr) = 0; + + /** + * @brief Register peer methods. + */ + virtual void registerNative(jni::JNIEnv&) = 0; + + /** + * @brief Get the corresponding layer factory. + * + * @return style::LayerFactory* must not be \c nullptr. + */ + virtual LayerFactory* getLayerFactory() = 0; +}; + + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/layer.hpp.ejs b/MapboxGLAndroidSDK/src/cpp/style/layers/layer.hpp.ejs new file mode 100644 index 000000000..eb8db0620 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/layer.hpp.ejs @@ -0,0 +1,62 @@ +<% + const type = locals.type; + const properties = locals.properties; +-%> +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include _layer_factory.hpp> +#include _layer.hpp> +#include + +namespace mbgl { +namespace android { + +class <%- camelize(type) %>Layer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/<%- camelize(type) %>Layer"; }; + +<% if (type === 'background') { -%> + <%- camelize(type) %>Layer(jni::JNIEnv&, jni::String&); +<% } else { -%> + <%- camelize(type) %>Layer(jni::JNIEnv&, jni::String&, jni::String&); +<% } -%> + + <%- camelize(type) %>Layer(mbgl::style::<%- camelize(type) %>Layer&); + + <%- camelize(type) %>Layer(std::unique_ptrLayer>); + + ~<%- camelize(type) %>Layer(); + + // Properties +<% for (const property of properties) { -%> + + jni::Local> get<%- camelize(property.name) %>(jni::JNIEnv&); +<% if (property.transition) { -%> + void set<%- camelize(property.name) %>Transition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> get<%- camelize(property.name) %>Transition(jni::JNIEnv&); +<% } -%> +<% } -%> + +}; // class <%- camelize(type) %>Layer + +class <%- camelize(type) %>JavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::<%- camelize(type) %>LayerFactory { +public: + ~<%- camelize(type) %>JavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class <%- camelize(type) %>JavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/layer_manager.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/layer_manager.cpp new file mode 100644 index 000000000..2d4172a23 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/layer_manager.cpp @@ -0,0 +1,173 @@ +#include "layer_manager.hpp" + +#include + +#include +#include +#include + +#include "background_layer.hpp" +#include "circle_layer.hpp" +#include "custom_layer.hpp" +#include "fill_extrusion_layer.hpp" +#include "fill_layer.hpp" +#include "heatmap_layer.hpp" +#include "hillshade_layer.hpp" +#include "line_layer.hpp" +#include "raster_layer.hpp" +#include "symbol_layer.hpp" +#include "fill_extrusion_layer.hpp" + +namespace mbgl { + +namespace android { + +LayerManagerAndroid::LayerManagerAndroid() { +#if defined(MBGL_LAYER_FILL_DISABLE_RUNTIME) + addLayerTypeCoreOnly(std::make_unique()); +#elif !defined(MBGL_LAYER_FILL_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif +#if defined(MBGL_LAYER_LINE_DISABLE_RUNTIME) + addLayerTypeCoreOnly(std::make_unique()); +#elif !defined(MBGL_LAYER_LINE_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif +#if defined(MBGL_LAYER_CIRCLE_DISABLE_RUNTIME) + addLayerTypeCoreOnly(std::make_unique()); +#elif !defined(MBGL_LAYER_CIRCLE_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif +#if defined(MBGL_LAYER_SYMBOL_DISABLE_RUNTIME) + addLayerTypeCoreOnly(std::make_unique()); +#elif !defined(MBGL_LAYER_SYMBOL_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif +#if defined(MBGL_LAYER_RASTER_DISABLE_RUNTIME) + addLayerTypeCoreOnly(std::make_unique()); +#elif !defined(MBGL_LAYER_RASTER_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif +#if defined(MBGL_LAYER_BACKGROUND_DISABLE_RUNTIME) + addLayerTypeCoreOnly(std::make_unique()); +#elif !defined(MBGL_LAYER_BACKGROUND_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif +#if defined(MBGL_LAYER_HILLSHADE_DISABLE_RUNTIME) + addLayerTypeCoreOnly(std::make_unique()); +#elif !defined(MBGL_LAYER_HILLSHADE_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif +#if defined(MBGL_LAYER_FILL_EXTRUSION_DISABLE_RUNTIME) + addLayerTypeCoreOnly(std::make_unique()); +#elif !defined(MBGL_LAYER_FILL_EXTRUSION_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif +#if defined(MBGL_LAYER_HEATMAP_DISABLE_RUNTIME) + addLayerTypeCoreOnly(std::make_unique()); +#elif !defined(MBGL_LAYER_HEATMAP_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif +#if defined(MBGL_LAYER_CUSTOM_DISABLE_RUNTIME) + addLayerTypeCoreOnly(std::make_unique()); +#elif !defined(MBGL_LAYER_CUSTOM_DISABLE_ALL) + addLayerType(std::make_unique()); +#endif +} + +LayerManagerAndroid::~LayerManagerAndroid() = default; + +jni::Local> LayerManagerAndroid::createJavaLayerPeer(jni::JNIEnv& env, mbgl::style::Layer& layer) { + if (JavaLayerPeerFactory* factory = getPeerFactory(layer.getTypeInfo())) { + return factory->createJavaLayerPeer(env, layer); + } + return jni::Local>(); +} + +jni::Local> LayerManagerAndroid::createJavaLayerPeer(jni::JNIEnv& env, + std::unique_ptr layer) { + if (JavaLayerPeerFactory* factory = getPeerFactory(layer->getTypeInfo())) { + return factory->createJavaLayerPeer(env, std::move(layer)); + } + return jni::Local>(); +} + +void LayerManagerAndroid::registerNative(jni::JNIEnv& env) { + if (peerFactories.empty()) { + return; + } + + Layer::registerNative(env); + for (const auto& factory: peerFactories) { + factory->registerNative(env); + } +} + +void LayerManagerAndroid::addLayerType(std::unique_ptr factory) { + assert(getFactory(factory->getLayerFactory()->getTypeInfo()) == nullptr); + registerCoreFactory(factory->getLayerFactory()); + peerFactories.emplace_back(std::move(factory)); +} + +void LayerManagerAndroid::addLayerTypeCoreOnly(std::unique_ptr factory) { + assert(getFactory(factory->getTypeInfo()) == nullptr); + registerCoreFactory(factory.get()); + coreFactories.emplace_back(std::move(factory)); +} + +void LayerManagerAndroid::registerCoreFactory(mbgl::LayerFactory* factory) { + std::string type{factory->getTypeInfo()->type}; + if (!type.empty()) { + assert(typeToFactory.find(type) == typeToFactory.end()); + typeToFactory.emplace(std::make_pair(std::move(type), factory)); + } +} + +JavaLayerPeerFactory* LayerManagerAndroid::getPeerFactory(const mbgl::style::LayerTypeInfo* typeInfo) { + assert(typeInfo); + for (const auto& factory: peerFactories) { + if (factory->getLayerFactory()->getTypeInfo() == typeInfo) { + return factory.get(); + } + } + return nullptr; +} + +LayerFactory* LayerManagerAndroid::getFactory(const std::string& type) noexcept { + auto search = typeToFactory.find(type); + return (search != typeToFactory.end()) ? search->second : nullptr; +} + +LayerFactory* LayerManagerAndroid::getFactory(const mbgl::style::LayerTypeInfo* info) noexcept { + if (JavaLayerPeerFactory* peerFactory = getPeerFactory(info)) { + return peerFactory->getLayerFactory(); + } + + for (const auto& factory: coreFactories) { + if (factory->getTypeInfo() == info) { + return factory.get(); + } + } + + return nullptr; +} + +// static +LayerManagerAndroid* LayerManagerAndroid::get() noexcept { + static LayerManagerAndroid impl; + return &impl; +} + +} // namespace android + +LayerManager* LayerManager::get() noexcept { + return android::LayerManagerAndroid::get(); +} + +#if defined(MBGL_LAYER_LINE_DISABLE_ALL) || defined(MBGL_LAYER_SYMBOL_DISABLE_ALL) || defined(MBGL_LAYER_FILL_DISABLE_ALL) +const bool LayerManager::annotationsEnabled = false; +#else +const bool LayerManager::annotationsEnabled = true; +#endif + +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/layer_manager.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/layer_manager.hpp new file mode 100644 index 000000000..f7379f6de --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/layer_manager.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +#include "layer.hpp" + +#include + +#include +#include + +namespace mbgl { +namespace android { + +/** + * @brief A singleton class forwarding calls to the corresponding \c JavaLayerPeerFactory instance. + */ +class LayerManagerAndroid final : public mbgl::LayerManager { +public: + ~LayerManagerAndroid() final; + static LayerManagerAndroid* get() noexcept; + + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&); + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr); + + void registerNative(jni::JNIEnv&); + +private: + LayerManagerAndroid(); + /** + * @brief Enables a layer type for both JSON style and runtime API. + */ + void addLayerType(std::unique_ptr); + /** + * @brief Enables a layer type for JSON style only. + * + * We might not want to expose runtime API for some layer types + * in order to save binary size - JNI glue code for these layer types + * won't be added to the binary. + */ + void addLayerTypeCoreOnly(std::unique_ptr); + + void registerCoreFactory(mbgl::LayerFactory*); + JavaLayerPeerFactory* getPeerFactory(const mbgl::style::LayerTypeInfo*); + // mbgl::LayerManager overrides. + LayerFactory* getFactory(const std::string& type) noexcept final; + LayerFactory* getFactory(const mbgl::style::LayerTypeInfo* info) noexcept final; + + std::vector> peerFactories; + std::vector> coreFactories; + std::map typeToFactory; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/line_layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/line_layer.cpp new file mode 100644 index 000000000..89c6cb532 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/line_layer.cpp @@ -0,0 +1,315 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "line_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +#include + +namespace mbgl { +namespace android { + + inline mbgl::style::LineLayer& toLineLayer(mbgl::style::Layer& layer) { + return static_cast(layer); + } + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + LineLayer::LineLayer(jni::JNIEnv& env, jni::String& layerId, jni::String& sourceId) + : Layer(std::make_unique(jni::Make(env, layerId), jni::Make(env, sourceId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + LineLayer::LineLayer(mbgl::style::LineLayer& coreLayer) : Layer(coreLayer) {} + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + LineLayer::LineLayer(std::unique_ptr coreLayer) : Layer(std::move(coreLayer)) {} + + LineLayer::~LineLayer() = default; + + // Property getters + + jni::Local> LineLayer::getLineCap(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineCap())); + } + + jni::Local> LineLayer::getLineJoin(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineJoin())); + } + + jni::Local> LineLayer::getLineMiterLimit(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineMiterLimit())); + } + + jni::Local> LineLayer::getLineRoundLimit(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineRoundLimit())); + } + + jni::Local> LineLayer::getLineSortKey(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineSortKey())); + } + + jni::Local> LineLayer::getLineOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineOpacity())); + } + + jni::Local> LineLayer::getLineOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toLineLayer(layer).getLineOpacityTransition(); + return std::move(*convert>>(env, options)); + } + + void LineLayer::setLineOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toLineLayer(layer).setLineOpacityTransition(options); + } + + jni::Local> LineLayer::getLineColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineColor())); + } + + jni::Local> LineLayer::getLineColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toLineLayer(layer).getLineColorTransition(); + return std::move(*convert>>(env, options)); + } + + void LineLayer::setLineColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toLineLayer(layer).setLineColorTransition(options); + } + + jni::Local> LineLayer::getLineTranslate(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineTranslate())); + } + + jni::Local> LineLayer::getLineTranslateTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toLineLayer(layer).getLineTranslateTransition(); + return std::move(*convert>>(env, options)); + } + + void LineLayer::setLineTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toLineLayer(layer).setLineTranslateTransition(options); + } + + jni::Local> LineLayer::getLineTranslateAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineTranslateAnchor())); + } + + jni::Local> LineLayer::getLineWidth(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineWidth())); + } + + jni::Local> LineLayer::getLineWidthTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toLineLayer(layer).getLineWidthTransition(); + return std::move(*convert>>(env, options)); + } + + void LineLayer::setLineWidthTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toLineLayer(layer).setLineWidthTransition(options); + } + + jni::Local> LineLayer::getLineGapWidth(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineGapWidth())); + } + + jni::Local> LineLayer::getLineGapWidthTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toLineLayer(layer).getLineGapWidthTransition(); + return std::move(*convert>>(env, options)); + } + + void LineLayer::setLineGapWidthTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toLineLayer(layer).setLineGapWidthTransition(options); + } + + jni::Local> LineLayer::getLineOffset(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineOffset())); + } + + jni::Local> LineLayer::getLineOffsetTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toLineLayer(layer).getLineOffsetTransition(); + return std::move(*convert>>(env, options)); + } + + void LineLayer::setLineOffsetTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toLineLayer(layer).setLineOffsetTransition(options); + } + + jni::Local> LineLayer::getLineBlur(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineBlur())); + } + + jni::Local> LineLayer::getLineBlurTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toLineLayer(layer).getLineBlurTransition(); + return std::move(*convert>>(env, options)); + } + + void LineLayer::setLineBlurTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toLineLayer(layer).setLineBlurTransition(options); + } + + jni::Local> LineLayer::getLineDasharray(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineDasharray())); + } + + jni::Local> LineLayer::getLineDasharrayTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toLineLayer(layer).getLineDasharrayTransition(); + return std::move(*convert>>(env, options)); + } + + void LineLayer::setLineDasharrayTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toLineLayer(layer).setLineDasharrayTransition(options); + } + + jni::Local> LineLayer::getLinePattern(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLinePattern())); + } + + jni::Local> LineLayer::getLinePatternTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toLineLayer(layer).getLinePatternTransition(); + return std::move(*convert>>(env, options)); + } + + void LineLayer::setLinePatternTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toLineLayer(layer).setLinePatternTransition(options); + } + + jni::Local> LineLayer::getLineGradient(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toLineLayer(layer).getLineGradient())); + } + + + // LineJavaLayerPeerFactory + + LineJavaLayerPeerFactory::~LineJavaLayerPeerFactory() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } // namespace + + jni::Local> LineJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, + mbgl::style::Layer& layer) { + assert(layer.baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new LineLayer(toLineLayer(layer))); + } + + jni::Local> LineJavaLayerPeerFactory::createJavaLayerPeer( + jni::JNIEnv& env, std::unique_ptr layer) { + assert(layer->baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, + new LineLayer(std::unique_ptr( + static_cast(layer.release())))); + } + + void LineJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize", + METHOD(&LineLayer::getLineCap, "nativeGetLineCap"), + METHOD(&LineLayer::getLineJoin, "nativeGetLineJoin"), + METHOD(&LineLayer::getLineMiterLimit, "nativeGetLineMiterLimit"), + METHOD(&LineLayer::getLineRoundLimit, "nativeGetLineRoundLimit"), + METHOD(&LineLayer::getLineSortKey, "nativeGetLineSortKey"), + METHOD(&LineLayer::getLineOpacityTransition, "nativeGetLineOpacityTransition"), + METHOD(&LineLayer::setLineOpacityTransition, "nativeSetLineOpacityTransition"), + METHOD(&LineLayer::getLineOpacity, "nativeGetLineOpacity"), + METHOD(&LineLayer::getLineColorTransition, "nativeGetLineColorTransition"), + METHOD(&LineLayer::setLineColorTransition, "nativeSetLineColorTransition"), + METHOD(&LineLayer::getLineColor, "nativeGetLineColor"), + METHOD(&LineLayer::getLineTranslateTransition, "nativeGetLineTranslateTransition"), + METHOD(&LineLayer::setLineTranslateTransition, "nativeSetLineTranslateTransition"), + METHOD(&LineLayer::getLineTranslate, "nativeGetLineTranslate"), + METHOD(&LineLayer::getLineTranslateAnchor, "nativeGetLineTranslateAnchor"), + METHOD(&LineLayer::getLineWidthTransition, "nativeGetLineWidthTransition"), + METHOD(&LineLayer::setLineWidthTransition, "nativeSetLineWidthTransition"), + METHOD(&LineLayer::getLineWidth, "nativeGetLineWidth"), + METHOD(&LineLayer::getLineGapWidthTransition, "nativeGetLineGapWidthTransition"), + METHOD(&LineLayer::setLineGapWidthTransition, "nativeSetLineGapWidthTransition"), + METHOD(&LineLayer::getLineGapWidth, "nativeGetLineGapWidth"), + METHOD(&LineLayer::getLineOffsetTransition, "nativeGetLineOffsetTransition"), + METHOD(&LineLayer::setLineOffsetTransition, "nativeSetLineOffsetTransition"), + METHOD(&LineLayer::getLineOffset, "nativeGetLineOffset"), + METHOD(&LineLayer::getLineBlurTransition, "nativeGetLineBlurTransition"), + METHOD(&LineLayer::setLineBlurTransition, "nativeSetLineBlurTransition"), + METHOD(&LineLayer::getLineBlur, "nativeGetLineBlur"), + METHOD(&LineLayer::getLineDasharrayTransition, "nativeGetLineDasharrayTransition"), + METHOD(&LineLayer::setLineDasharrayTransition, "nativeSetLineDasharrayTransition"), + METHOD(&LineLayer::getLineDasharray, "nativeGetLineDasharray"), + METHOD(&LineLayer::getLinePatternTransition, "nativeGetLinePatternTransition"), + METHOD(&LineLayer::setLinePatternTransition, "nativeSetLinePatternTransition"), + METHOD(&LineLayer::getLinePattern, "nativeGetLinePattern"), + METHOD(&LineLayer::getLineGradient, "nativeGetLineGradient")); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/line_layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/line_layer.hpp new file mode 100644 index 000000000..25b270fbc --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/line_layer.hpp @@ -0,0 +1,96 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include +#include +#include + +namespace mbgl { +namespace android { + +class LineLayer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/LineLayer"; }; + + LineLayer(jni::JNIEnv&, jni::String&, jni::String&); + + LineLayer(mbgl::style::LineLayer&); + + LineLayer(std::unique_ptr); + + ~LineLayer(); + + // Properties + + jni::Local> getLineCap(jni::JNIEnv&); + + jni::Local> getLineJoin(jni::JNIEnv&); + + jni::Local> getLineMiterLimit(jni::JNIEnv&); + + jni::Local> getLineRoundLimit(jni::JNIEnv&); + + jni::Local> getLineSortKey(jni::JNIEnv&); + + jni::Local> getLineOpacity(jni::JNIEnv&); + void setLineOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getLineOpacityTransition(jni::JNIEnv&); + + jni::Local> getLineColor(jni::JNIEnv&); + void setLineColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getLineColorTransition(jni::JNIEnv&); + + jni::Local> getLineTranslate(jni::JNIEnv&); + void setLineTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getLineTranslateTransition(jni::JNIEnv&); + + jni::Local> getLineTranslateAnchor(jni::JNIEnv&); + + jni::Local> getLineWidth(jni::JNIEnv&); + void setLineWidthTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getLineWidthTransition(jni::JNIEnv&); + + jni::Local> getLineGapWidth(jni::JNIEnv&); + void setLineGapWidthTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getLineGapWidthTransition(jni::JNIEnv&); + + jni::Local> getLineOffset(jni::JNIEnv&); + void setLineOffsetTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getLineOffsetTransition(jni::JNIEnv&); + + jni::Local> getLineBlur(jni::JNIEnv&); + void setLineBlurTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getLineBlurTransition(jni::JNIEnv&); + + jni::Local> getLineDasharray(jni::JNIEnv&); + void setLineDasharrayTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getLineDasharrayTransition(jni::JNIEnv&); + + jni::Local> getLinePattern(jni::JNIEnv&); + void setLinePatternTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getLinePatternTransition(jni::JNIEnv&); + + jni::Local> getLineGradient(jni::JNIEnv&); + +}; // class LineLayer + +class LineJavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::LineLayerFactory { +public: + ~LineJavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class LineJavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/raster_layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/raster_layer.cpp new file mode 100644 index 000000000..d9cce949c --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/raster_layer.cpp @@ -0,0 +1,222 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "raster_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +#include + +namespace mbgl { +namespace android { + + inline mbgl::style::RasterLayer& toRasterLayer(mbgl::style::Layer& layer) { + return static_cast(layer); + } + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + RasterLayer::RasterLayer(jni::JNIEnv& env, jni::String& layerId, jni::String& sourceId) + : Layer(std::make_unique(jni::Make(env, layerId), jni::Make(env, sourceId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + RasterLayer::RasterLayer(mbgl::style::RasterLayer& coreLayer) : Layer(coreLayer) {} + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + RasterLayer::RasterLayer(std::unique_ptr coreLayer) : Layer(std::move(coreLayer)) {} + + RasterLayer::~RasterLayer() = default; + + // Property getters + + jni::Local> RasterLayer::getRasterOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toRasterLayer(layer).getRasterOpacity())); + } + + jni::Local> RasterLayer::getRasterOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toRasterLayer(layer).getRasterOpacityTransition(); + return std::move(*convert>>(env, options)); + } + + void RasterLayer::setRasterOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toRasterLayer(layer).setRasterOpacityTransition(options); + } + + jni::Local> RasterLayer::getRasterHueRotate(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toRasterLayer(layer).getRasterHueRotate())); + } + + jni::Local> RasterLayer::getRasterHueRotateTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toRasterLayer(layer).getRasterHueRotateTransition(); + return std::move(*convert>>(env, options)); + } + + void RasterLayer::setRasterHueRotateTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toRasterLayer(layer).setRasterHueRotateTransition(options); + } + + jni::Local> RasterLayer::getRasterBrightnessMin(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toRasterLayer(layer).getRasterBrightnessMin())); + } + + jni::Local> RasterLayer::getRasterBrightnessMinTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toRasterLayer(layer).getRasterBrightnessMinTransition(); + return std::move(*convert>>(env, options)); + } + + void RasterLayer::setRasterBrightnessMinTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toRasterLayer(layer).setRasterBrightnessMinTransition(options); + } + + jni::Local> RasterLayer::getRasterBrightnessMax(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toRasterLayer(layer).getRasterBrightnessMax())); + } + + jni::Local> RasterLayer::getRasterBrightnessMaxTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toRasterLayer(layer).getRasterBrightnessMaxTransition(); + return std::move(*convert>>(env, options)); + } + + void RasterLayer::setRasterBrightnessMaxTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toRasterLayer(layer).setRasterBrightnessMaxTransition(options); + } + + jni::Local> RasterLayer::getRasterSaturation(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toRasterLayer(layer).getRasterSaturation())); + } + + jni::Local> RasterLayer::getRasterSaturationTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toRasterLayer(layer).getRasterSaturationTransition(); + return std::move(*convert>>(env, options)); + } + + void RasterLayer::setRasterSaturationTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toRasterLayer(layer).setRasterSaturationTransition(options); + } + + jni::Local> RasterLayer::getRasterContrast(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toRasterLayer(layer).getRasterContrast())); + } + + jni::Local> RasterLayer::getRasterContrastTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toRasterLayer(layer).getRasterContrastTransition(); + return std::move(*convert>>(env, options)); + } + + void RasterLayer::setRasterContrastTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toRasterLayer(layer).setRasterContrastTransition(options); + } + + jni::Local> RasterLayer::getRasterResampling(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toRasterLayer(layer).getRasterResampling())); + } + + jni::Local> RasterLayer::getRasterFadeDuration(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toRasterLayer(layer).getRasterFadeDuration())); + } + + + // RasterJavaLayerPeerFactory + + RasterJavaLayerPeerFactory::~RasterJavaLayerPeerFactory() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } // namespace + + jni::Local> RasterJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, + mbgl::style::Layer& layer) { + assert(layer.baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new RasterLayer(toRasterLayer(layer))); + } + + jni::Local> RasterJavaLayerPeerFactory::createJavaLayerPeer( + jni::JNIEnv& env, std::unique_ptr layer) { + assert(layer->baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, + new RasterLayer(std::unique_ptr( + static_cast(layer.release())))); + } + + void RasterJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize", + METHOD(&RasterLayer::getRasterOpacityTransition, "nativeGetRasterOpacityTransition"), + METHOD(&RasterLayer::setRasterOpacityTransition, "nativeSetRasterOpacityTransition"), + METHOD(&RasterLayer::getRasterOpacity, "nativeGetRasterOpacity"), + METHOD(&RasterLayer::getRasterHueRotateTransition, "nativeGetRasterHueRotateTransition"), + METHOD(&RasterLayer::setRasterHueRotateTransition, "nativeSetRasterHueRotateTransition"), + METHOD(&RasterLayer::getRasterHueRotate, "nativeGetRasterHueRotate"), + METHOD(&RasterLayer::getRasterBrightnessMinTransition, "nativeGetRasterBrightnessMinTransition"), + METHOD(&RasterLayer::setRasterBrightnessMinTransition, "nativeSetRasterBrightnessMinTransition"), + METHOD(&RasterLayer::getRasterBrightnessMin, "nativeGetRasterBrightnessMin"), + METHOD(&RasterLayer::getRasterBrightnessMaxTransition, "nativeGetRasterBrightnessMaxTransition"), + METHOD(&RasterLayer::setRasterBrightnessMaxTransition, "nativeSetRasterBrightnessMaxTransition"), + METHOD(&RasterLayer::getRasterBrightnessMax, "nativeGetRasterBrightnessMax"), + METHOD(&RasterLayer::getRasterSaturationTransition, "nativeGetRasterSaturationTransition"), + METHOD(&RasterLayer::setRasterSaturationTransition, "nativeSetRasterSaturationTransition"), + METHOD(&RasterLayer::getRasterSaturation, "nativeGetRasterSaturation"), + METHOD(&RasterLayer::getRasterContrastTransition, "nativeGetRasterContrastTransition"), + METHOD(&RasterLayer::setRasterContrastTransition, "nativeSetRasterContrastTransition"), + METHOD(&RasterLayer::getRasterContrast, "nativeGetRasterContrast"), + METHOD(&RasterLayer::getRasterResampling, "nativeGetRasterResampling"), + METHOD(&RasterLayer::getRasterFadeDuration, "nativeGetRasterFadeDuration")); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/raster_layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/raster_layer.hpp new file mode 100644 index 000000000..bbda2e0c5 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/raster_layer.hpp @@ -0,0 +1,74 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include +#include +#include + +namespace mbgl { +namespace android { + +class RasterLayer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/RasterLayer"; }; + + RasterLayer(jni::JNIEnv&, jni::String&, jni::String&); + + RasterLayer(mbgl::style::RasterLayer&); + + RasterLayer(std::unique_ptr); + + ~RasterLayer(); + + // Properties + + jni::Local> getRasterOpacity(jni::JNIEnv&); + void setRasterOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getRasterOpacityTransition(jni::JNIEnv&); + + jni::Local> getRasterHueRotate(jni::JNIEnv&); + void setRasterHueRotateTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getRasterHueRotateTransition(jni::JNIEnv&); + + jni::Local> getRasterBrightnessMin(jni::JNIEnv&); + void setRasterBrightnessMinTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getRasterBrightnessMinTransition(jni::JNIEnv&); + + jni::Local> getRasterBrightnessMax(jni::JNIEnv&); + void setRasterBrightnessMaxTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getRasterBrightnessMaxTransition(jni::JNIEnv&); + + jni::Local> getRasterSaturation(jni::JNIEnv&); + void setRasterSaturationTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getRasterSaturationTransition(jni::JNIEnv&); + + jni::Local> getRasterContrast(jni::JNIEnv&); + void setRasterContrastTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getRasterContrastTransition(jni::JNIEnv&); + + jni::Local> getRasterResampling(jni::JNIEnv&); + + jni::Local> getRasterFadeDuration(jni::JNIEnv&); + +}; // class RasterLayer + +class RasterJavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::RasterLayerFactory { +public: + ~RasterJavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class RasterJavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/symbol_layer.cpp b/MapboxGLAndroidSDK/src/cpp/style/layers/symbol_layer.cpp new file mode 100644 index 000000000..f24fb3542 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/symbol_layer.cpp @@ -0,0 +1,594 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#include "symbol_layer.hpp" + +#include + +#include "../conversion/property_value.hpp" +#include "../conversion/transition_options.hpp" + +#include + +namespace mbgl { +namespace android { + + inline mbgl::style::SymbolLayer& toSymbolLayer(mbgl::style::Layer& layer) { + return static_cast(layer); + } + + /** + * Creates an owning peer object (for layers not attached to the map) from the JVM side + */ + SymbolLayer::SymbolLayer(jni::JNIEnv& env, jni::String& layerId, jni::String& sourceId) + : Layer(std::make_unique(jni::Make(env, layerId), jni::Make(env, sourceId))) { + } + + /** + * Creates a non-owning peer object (for layers currently attached to the map) + */ + SymbolLayer::SymbolLayer(mbgl::style::SymbolLayer& coreLayer) : Layer(coreLayer) {} + + /** + * Creates an owning peer object (for layers not attached to the map) + */ + SymbolLayer::SymbolLayer(std::unique_ptr coreLayer) : Layer(std::move(coreLayer)) {} + + SymbolLayer::~SymbolLayer() = default; + + // Property getters + + jni::Local> SymbolLayer::getSymbolPlacement(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getSymbolPlacement())); + } + + jni::Local> SymbolLayer::getSymbolSpacing(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getSymbolSpacing())); + } + + jni::Local> SymbolLayer::getSymbolAvoidEdges(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getSymbolAvoidEdges())); + } + + jni::Local> SymbolLayer::getSymbolSortKey(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getSymbolSortKey())); + } + + jni::Local> SymbolLayer::getSymbolZOrder(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getSymbolZOrder())); + } + + jni::Local> SymbolLayer::getIconAllowOverlap(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconAllowOverlap())); + } + + jni::Local> SymbolLayer::getIconIgnorePlacement(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconIgnorePlacement())); + } + + jni::Local> SymbolLayer::getIconOptional(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconOptional())); + } + + jni::Local> SymbolLayer::getIconRotationAlignment(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconRotationAlignment())); + } + + jni::Local> SymbolLayer::getIconSize(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconSize())); + } + + jni::Local> SymbolLayer::getIconTextFit(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconTextFit())); + } + + jni::Local> SymbolLayer::getIconTextFitPadding(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconTextFitPadding())); + } + + jni::Local> SymbolLayer::getIconImage(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconImage())); + } + + jni::Local> SymbolLayer::getIconRotate(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconRotate())); + } + + jni::Local> SymbolLayer::getIconPadding(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconPadding())); + } + + jni::Local> SymbolLayer::getIconKeepUpright(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconKeepUpright())); + } + + jni::Local> SymbolLayer::getIconOffset(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconOffset())); + } + + jni::Local> SymbolLayer::getIconAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconAnchor())); + } + + jni::Local> SymbolLayer::getIconPitchAlignment(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconPitchAlignment())); + } + + jni::Local> SymbolLayer::getTextPitchAlignment(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextPitchAlignment())); + } + + jni::Local> SymbolLayer::getTextRotationAlignment(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextRotationAlignment())); + } + + jni::Local> SymbolLayer::getTextField(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextField())); + } + + jni::Local> SymbolLayer::getTextFont(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextFont())); + } + + jni::Local> SymbolLayer::getTextSize(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextSize())); + } + + jni::Local> SymbolLayer::getTextMaxWidth(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextMaxWidth())); + } + + jni::Local> SymbolLayer::getTextLineHeight(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextLineHeight())); + } + + jni::Local> SymbolLayer::getTextLetterSpacing(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextLetterSpacing())); + } + + jni::Local> SymbolLayer::getTextJustify(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextJustify())); + } + + jni::Local> SymbolLayer::getTextRadialOffset(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextRadialOffset())); + } + + jni::Local> SymbolLayer::getTextVariableAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextVariableAnchor())); + } + + jni::Local> SymbolLayer::getTextAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextAnchor())); + } + + jni::Local> SymbolLayer::getTextMaxAngle(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextMaxAngle())); + } + + jni::Local> SymbolLayer::getTextWritingMode(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextWritingMode())); + } + + jni::Local> SymbolLayer::getTextRotate(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextRotate())); + } + + jni::Local> SymbolLayer::getTextPadding(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextPadding())); + } + + jni::Local> SymbolLayer::getTextKeepUpright(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextKeepUpright())); + } + + jni::Local> SymbolLayer::getTextTransform(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextTransform())); + } + + jni::Local> SymbolLayer::getTextOffset(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextOffset())); + } + + jni::Local> SymbolLayer::getTextAllowOverlap(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextAllowOverlap())); + } + + jni::Local> SymbolLayer::getTextIgnorePlacement(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextIgnorePlacement())); + } + + jni::Local> SymbolLayer::getTextOptional(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextOptional())); + } + + jni::Local> SymbolLayer::getIconOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconOpacity())); + } + + jni::Local> SymbolLayer::getIconOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getIconOpacityTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setIconOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setIconOpacityTransition(options); + } + + jni::Local> SymbolLayer::getIconColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconColor())); + } + + jni::Local> SymbolLayer::getIconColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getIconColorTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setIconColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setIconColorTransition(options); + } + + jni::Local> SymbolLayer::getIconHaloColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconHaloColor())); + } + + jni::Local> SymbolLayer::getIconHaloColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getIconHaloColorTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setIconHaloColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setIconHaloColorTransition(options); + } + + jni::Local> SymbolLayer::getIconHaloWidth(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconHaloWidth())); + } + + jni::Local> SymbolLayer::getIconHaloWidthTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getIconHaloWidthTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setIconHaloWidthTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setIconHaloWidthTransition(options); + } + + jni::Local> SymbolLayer::getIconHaloBlur(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconHaloBlur())); + } + + jni::Local> SymbolLayer::getIconHaloBlurTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getIconHaloBlurTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setIconHaloBlurTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setIconHaloBlurTransition(options); + } + + jni::Local> SymbolLayer::getIconTranslate(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconTranslate())); + } + + jni::Local> SymbolLayer::getIconTranslateTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getIconTranslateTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setIconTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setIconTranslateTransition(options); + } + + jni::Local> SymbolLayer::getIconTranslateAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getIconTranslateAnchor())); + } + + jni::Local> SymbolLayer::getTextOpacity(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextOpacity())); + } + + jni::Local> SymbolLayer::getTextOpacityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getTextOpacityTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setTextOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setTextOpacityTransition(options); + } + + jni::Local> SymbolLayer::getTextColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextColor())); + } + + jni::Local> SymbolLayer::getTextColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getTextColorTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setTextColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setTextColorTransition(options); + } + + jni::Local> SymbolLayer::getTextHaloColor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextHaloColor())); + } + + jni::Local> SymbolLayer::getTextHaloColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getTextHaloColorTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setTextHaloColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setTextHaloColorTransition(options); + } + + jni::Local> SymbolLayer::getTextHaloWidth(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextHaloWidth())); + } + + jni::Local> SymbolLayer::getTextHaloWidthTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getTextHaloWidthTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setTextHaloWidthTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setTextHaloWidthTransition(options); + } + + jni::Local> SymbolLayer::getTextHaloBlur(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextHaloBlur())); + } + + jni::Local> SymbolLayer::getTextHaloBlurTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getTextHaloBlurTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setTextHaloBlurTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setTextHaloBlurTransition(options); + } + + jni::Local> SymbolLayer::getTextTranslate(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextTranslate())); + } + + jni::Local> SymbolLayer::getTextTranslateTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = toSymbolLayer(layer).getTextTranslateTransition(); + return std::move(*convert>>(env, options)); + } + + void SymbolLayer::setTextTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + toSymbolLayer(layer).setTextTranslateTransition(options); + } + + jni::Local> SymbolLayer::getTextTranslateAnchor(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + return std::move(*convert>>(env, toSymbolLayer(layer).getTextTranslateAnchor())); + } + + + // SymbolJavaLayerPeerFactory + + SymbolJavaLayerPeerFactory::~SymbolJavaLayerPeerFactory() = default; + + namespace { + jni::Local> createJavaPeer(jni::JNIEnv& env, Layer* layer) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(layer)); + } + } // namespace + + jni::Local> SymbolJavaLayerPeerFactory::createJavaLayerPeer(jni::JNIEnv& env, + mbgl::style::Layer& layer) { + assert(layer.baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, new SymbolLayer(toSymbolLayer(layer))); + } + + jni::Local> SymbolJavaLayerPeerFactory::createJavaLayerPeer( + jni::JNIEnv& env, std::unique_ptr layer) { + assert(layer->baseImpl->getTypeInfo() == getTypeInfo()); + return createJavaPeer(env, + new SymbolLayer(std::unique_ptr( + static_cast(layer.release())))); + } + + void SymbolJavaLayerPeerFactory::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + jni::MakePeer, + "initialize", + "finalize", + METHOD(&SymbolLayer::getSymbolPlacement, "nativeGetSymbolPlacement"), + METHOD(&SymbolLayer::getSymbolSpacing, "nativeGetSymbolSpacing"), + METHOD(&SymbolLayer::getSymbolAvoidEdges, "nativeGetSymbolAvoidEdges"), + METHOD(&SymbolLayer::getSymbolSortKey, "nativeGetSymbolSortKey"), + METHOD(&SymbolLayer::getSymbolZOrder, "nativeGetSymbolZOrder"), + METHOD(&SymbolLayer::getIconAllowOverlap, "nativeGetIconAllowOverlap"), + METHOD(&SymbolLayer::getIconIgnorePlacement, "nativeGetIconIgnorePlacement"), + METHOD(&SymbolLayer::getIconOptional, "nativeGetIconOptional"), + METHOD(&SymbolLayer::getIconRotationAlignment, "nativeGetIconRotationAlignment"), + METHOD(&SymbolLayer::getIconSize, "nativeGetIconSize"), + METHOD(&SymbolLayer::getIconTextFit, "nativeGetIconTextFit"), + METHOD(&SymbolLayer::getIconTextFitPadding, "nativeGetIconTextFitPadding"), + METHOD(&SymbolLayer::getIconImage, "nativeGetIconImage"), + METHOD(&SymbolLayer::getIconRotate, "nativeGetIconRotate"), + METHOD(&SymbolLayer::getIconPadding, "nativeGetIconPadding"), + METHOD(&SymbolLayer::getIconKeepUpright, "nativeGetIconKeepUpright"), + METHOD(&SymbolLayer::getIconOffset, "nativeGetIconOffset"), + METHOD(&SymbolLayer::getIconAnchor, "nativeGetIconAnchor"), + METHOD(&SymbolLayer::getIconPitchAlignment, "nativeGetIconPitchAlignment"), + METHOD(&SymbolLayer::getTextPitchAlignment, "nativeGetTextPitchAlignment"), + METHOD(&SymbolLayer::getTextRotationAlignment, "nativeGetTextRotationAlignment"), + METHOD(&SymbolLayer::getTextField, "nativeGetTextField"), + METHOD(&SymbolLayer::getTextFont, "nativeGetTextFont"), + METHOD(&SymbolLayer::getTextSize, "nativeGetTextSize"), + METHOD(&SymbolLayer::getTextMaxWidth, "nativeGetTextMaxWidth"), + METHOD(&SymbolLayer::getTextLineHeight, "nativeGetTextLineHeight"), + METHOD(&SymbolLayer::getTextLetterSpacing, "nativeGetTextLetterSpacing"), + METHOD(&SymbolLayer::getTextJustify, "nativeGetTextJustify"), + METHOD(&SymbolLayer::getTextRadialOffset, "nativeGetTextRadialOffset"), + METHOD(&SymbolLayer::getTextVariableAnchor, "nativeGetTextVariableAnchor"), + METHOD(&SymbolLayer::getTextAnchor, "nativeGetTextAnchor"), + METHOD(&SymbolLayer::getTextMaxAngle, "nativeGetTextMaxAngle"), + METHOD(&SymbolLayer::getTextWritingMode, "nativeGetTextWritingMode"), + METHOD(&SymbolLayer::getTextRotate, "nativeGetTextRotate"), + METHOD(&SymbolLayer::getTextPadding, "nativeGetTextPadding"), + METHOD(&SymbolLayer::getTextKeepUpright, "nativeGetTextKeepUpright"), + METHOD(&SymbolLayer::getTextTransform, "nativeGetTextTransform"), + METHOD(&SymbolLayer::getTextOffset, "nativeGetTextOffset"), + METHOD(&SymbolLayer::getTextAllowOverlap, "nativeGetTextAllowOverlap"), + METHOD(&SymbolLayer::getTextIgnorePlacement, "nativeGetTextIgnorePlacement"), + METHOD(&SymbolLayer::getTextOptional, "nativeGetTextOptional"), + METHOD(&SymbolLayer::getIconOpacityTransition, "nativeGetIconOpacityTransition"), + METHOD(&SymbolLayer::setIconOpacityTransition, "nativeSetIconOpacityTransition"), + METHOD(&SymbolLayer::getIconOpacity, "nativeGetIconOpacity"), + METHOD(&SymbolLayer::getIconColorTransition, "nativeGetIconColorTransition"), + METHOD(&SymbolLayer::setIconColorTransition, "nativeSetIconColorTransition"), + METHOD(&SymbolLayer::getIconColor, "nativeGetIconColor"), + METHOD(&SymbolLayer::getIconHaloColorTransition, "nativeGetIconHaloColorTransition"), + METHOD(&SymbolLayer::setIconHaloColorTransition, "nativeSetIconHaloColorTransition"), + METHOD(&SymbolLayer::getIconHaloColor, "nativeGetIconHaloColor"), + METHOD(&SymbolLayer::getIconHaloWidthTransition, "nativeGetIconHaloWidthTransition"), + METHOD(&SymbolLayer::setIconHaloWidthTransition, "nativeSetIconHaloWidthTransition"), + METHOD(&SymbolLayer::getIconHaloWidth, "nativeGetIconHaloWidth"), + METHOD(&SymbolLayer::getIconHaloBlurTransition, "nativeGetIconHaloBlurTransition"), + METHOD(&SymbolLayer::setIconHaloBlurTransition, "nativeSetIconHaloBlurTransition"), + METHOD(&SymbolLayer::getIconHaloBlur, "nativeGetIconHaloBlur"), + METHOD(&SymbolLayer::getIconTranslateTransition, "nativeGetIconTranslateTransition"), + METHOD(&SymbolLayer::setIconTranslateTransition, "nativeSetIconTranslateTransition"), + METHOD(&SymbolLayer::getIconTranslate, "nativeGetIconTranslate"), + METHOD(&SymbolLayer::getIconTranslateAnchor, "nativeGetIconTranslateAnchor"), + METHOD(&SymbolLayer::getTextOpacityTransition, "nativeGetTextOpacityTransition"), + METHOD(&SymbolLayer::setTextOpacityTransition, "nativeSetTextOpacityTransition"), + METHOD(&SymbolLayer::getTextOpacity, "nativeGetTextOpacity"), + METHOD(&SymbolLayer::getTextColorTransition, "nativeGetTextColorTransition"), + METHOD(&SymbolLayer::setTextColorTransition, "nativeSetTextColorTransition"), + METHOD(&SymbolLayer::getTextColor, "nativeGetTextColor"), + METHOD(&SymbolLayer::getTextHaloColorTransition, "nativeGetTextHaloColorTransition"), + METHOD(&SymbolLayer::setTextHaloColorTransition, "nativeSetTextHaloColorTransition"), + METHOD(&SymbolLayer::getTextHaloColor, "nativeGetTextHaloColor"), + METHOD(&SymbolLayer::getTextHaloWidthTransition, "nativeGetTextHaloWidthTransition"), + METHOD(&SymbolLayer::setTextHaloWidthTransition, "nativeSetTextHaloWidthTransition"), + METHOD(&SymbolLayer::getTextHaloWidth, "nativeGetTextHaloWidth"), + METHOD(&SymbolLayer::getTextHaloBlurTransition, "nativeGetTextHaloBlurTransition"), + METHOD(&SymbolLayer::setTextHaloBlurTransition, "nativeSetTextHaloBlurTransition"), + METHOD(&SymbolLayer::getTextHaloBlur, "nativeGetTextHaloBlur"), + METHOD(&SymbolLayer::getTextTranslateTransition, "nativeGetTextTranslateTransition"), + METHOD(&SymbolLayer::setTextTranslateTransition, "nativeSetTextTranslateTransition"), + METHOD(&SymbolLayer::getTextTranslate, "nativeGetTextTranslate"), + METHOD(&SymbolLayer::getTextTranslateAnchor, "nativeGetTextTranslateAnchor")); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/layers/symbol_layer.hpp b/MapboxGLAndroidSDK/src/cpp/style/layers/symbol_layer.hpp new file mode 100644 index 000000000..4898c830d --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/layers/symbol_layer.hpp @@ -0,0 +1,180 @@ +// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make android-style-code`. + +#pragma once + +#include "layer.hpp" +#include "../transition_options.hpp" +#include +#include +#include + +namespace mbgl { +namespace android { + +class SymbolLayer : public Layer { +public: + using SuperTag = Layer; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/SymbolLayer"; }; + + SymbolLayer(jni::JNIEnv&, jni::String&, jni::String&); + + SymbolLayer(mbgl::style::SymbolLayer&); + + SymbolLayer(std::unique_ptr); + + ~SymbolLayer(); + + // Properties + + jni::Local> getSymbolPlacement(jni::JNIEnv&); + + jni::Local> getSymbolSpacing(jni::JNIEnv&); + + jni::Local> getSymbolAvoidEdges(jni::JNIEnv&); + + jni::Local> getSymbolSortKey(jni::JNIEnv&); + + jni::Local> getSymbolZOrder(jni::JNIEnv&); + + jni::Local> getIconAllowOverlap(jni::JNIEnv&); + + jni::Local> getIconIgnorePlacement(jni::JNIEnv&); + + jni::Local> getIconOptional(jni::JNIEnv&); + + jni::Local> getIconRotationAlignment(jni::JNIEnv&); + + jni::Local> getIconSize(jni::JNIEnv&); + + jni::Local> getIconTextFit(jni::JNIEnv&); + + jni::Local> getIconTextFitPadding(jni::JNIEnv&); + + jni::Local> getIconImage(jni::JNIEnv&); + + jni::Local> getIconRotate(jni::JNIEnv&); + + jni::Local> getIconPadding(jni::JNIEnv&); + + jni::Local> getIconKeepUpright(jni::JNIEnv&); + + jni::Local> getIconOffset(jni::JNIEnv&); + + jni::Local> getIconAnchor(jni::JNIEnv&); + + jni::Local> getIconPitchAlignment(jni::JNIEnv&); + + jni::Local> getTextPitchAlignment(jni::JNIEnv&); + + jni::Local> getTextRotationAlignment(jni::JNIEnv&); + + jni::Local> getTextField(jni::JNIEnv&); + + jni::Local> getTextFont(jni::JNIEnv&); + + jni::Local> getTextSize(jni::JNIEnv&); + + jni::Local> getTextMaxWidth(jni::JNIEnv&); + + jni::Local> getTextLineHeight(jni::JNIEnv&); + + jni::Local> getTextLetterSpacing(jni::JNIEnv&); + + jni::Local> getTextJustify(jni::JNIEnv&); + + jni::Local> getTextRadialOffset(jni::JNIEnv&); + + jni::Local> getTextVariableAnchor(jni::JNIEnv&); + + jni::Local> getTextAnchor(jni::JNIEnv&); + + jni::Local> getTextMaxAngle(jni::JNIEnv&); + + jni::Local> getTextWritingMode(jni::JNIEnv&); + + jni::Local> getTextRotate(jni::JNIEnv&); + + jni::Local> getTextPadding(jni::JNIEnv&); + + jni::Local> getTextKeepUpright(jni::JNIEnv&); + + jni::Local> getTextTransform(jni::JNIEnv&); + + jni::Local> getTextOffset(jni::JNIEnv&); + + jni::Local> getTextAllowOverlap(jni::JNIEnv&); + + jni::Local> getTextIgnorePlacement(jni::JNIEnv&); + + jni::Local> getTextOptional(jni::JNIEnv&); + + jni::Local> getIconOpacity(jni::JNIEnv&); + void setIconOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getIconOpacityTransition(jni::JNIEnv&); + + jni::Local> getIconColor(jni::JNIEnv&); + void setIconColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getIconColorTransition(jni::JNIEnv&); + + jni::Local> getIconHaloColor(jni::JNIEnv&); + void setIconHaloColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getIconHaloColorTransition(jni::JNIEnv&); + + jni::Local> getIconHaloWidth(jni::JNIEnv&); + void setIconHaloWidthTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getIconHaloWidthTransition(jni::JNIEnv&); + + jni::Local> getIconHaloBlur(jni::JNIEnv&); + void setIconHaloBlurTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getIconHaloBlurTransition(jni::JNIEnv&); + + jni::Local> getIconTranslate(jni::JNIEnv&); + void setIconTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getIconTranslateTransition(jni::JNIEnv&); + + jni::Local> getIconTranslateAnchor(jni::JNIEnv&); + + jni::Local> getTextOpacity(jni::JNIEnv&); + void setTextOpacityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getTextOpacityTransition(jni::JNIEnv&); + + jni::Local> getTextColor(jni::JNIEnv&); + void setTextColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getTextColorTransition(jni::JNIEnv&); + + jni::Local> getTextHaloColor(jni::JNIEnv&); + void setTextHaloColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getTextHaloColorTransition(jni::JNIEnv&); + + jni::Local> getTextHaloWidth(jni::JNIEnv&); + void setTextHaloWidthTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getTextHaloWidthTransition(jni::JNIEnv&); + + jni::Local> getTextHaloBlur(jni::JNIEnv&); + void setTextHaloBlurTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getTextHaloBlurTransition(jni::JNIEnv&); + + jni::Local> getTextTranslate(jni::JNIEnv&); + void setTextTranslateTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getTextTranslateTransition(jni::JNIEnv&); + + jni::Local> getTextTranslateAnchor(jni::JNIEnv&); + +}; // class SymbolLayer + +class SymbolJavaLayerPeerFactory final : public JavaLayerPeerFactory, public mbgl::SymbolLayerFactory { +public: + ~SymbolJavaLayerPeerFactory() override; + + // JavaLayerPeerFactory overrides. + jni::Local> createJavaLayerPeer(jni::JNIEnv&, mbgl::style::Layer&) final; + jni::Local> createJavaLayerPeer(jni::JNIEnv& env, std::unique_ptr) final; + + void registerNative(jni::JNIEnv&) final; + + LayerFactory* getLayerFactory() final { return this; } + +}; // class SymbolJavaLayerPeerFactory + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/light.cpp b/MapboxGLAndroidSDK/src/cpp/style/light.cpp new file mode 100644 index 000000000..d8ed4d9d9 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/light.cpp @@ -0,0 +1,143 @@ +#include +#include "light.hpp" +#include "conversion/transition_options.hpp" +#include "conversion/position.hpp" + +namespace mbgl { +namespace android { + +Light::Light(mbgl::Map& coreMap, mbgl::style::Light& coreLight) + : light(coreLight) , map(&coreMap) { +} + +static Light* initializeLightPeer(mbgl::Map& map, mbgl::style::Light& coreLight) { + return new Light(map, coreLight); +} + +jni::Local> Light::createJavaLightPeer(jni::JNIEnv& env, Map& map, mbgl::style::Light& coreLight) { + std::unique_ptr peerLight = std::unique_ptr(initializeLightPeer(map, coreLight)); + auto result = peerLight->createJavaPeer(env); + peerLight.release(); + return result; +} + +jni::Local> Light::createJavaPeer(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(this)); +} + +void Light::setAnchor(jni::JNIEnv& env, const jni::String& property) { + std::string anchorStr = jni::Make(env, property); + if (anchorStr.compare("map") == 0) { + light.setAnchor(LightAnchorType::Map); + } else if (anchorStr.compare("viewport") == 0) { + light.setAnchor(LightAnchorType::Viewport); + } +} + +jni::Local Light::getAnchor(jni::JNIEnv& env) { + auto anchorType = light.getAnchor(); + if (anchorType == LightAnchorType::Map) { + return jni::Make(env, "map"); + } else { + return jni::Make(env, "viewport"); + } +} + +void Light::setPosition(jni::JNIEnv& env, const jni::Object& jposition) { + using namespace mbgl::android::conversion; + auto position = *convert(env, jposition); + light.setPosition(position); +} + +jni::Local> Light::getPosition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::Position position = light.getPosition().asConstant(); + return std::move(*convert>>(env, position)); +} + +jni::Local> Light::getPositionTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = light.getPositionTransition(); + return std::move(*convert>>(env, options)); +} + +void Light::setPositionTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + light.setPositionTransition(options); +} + +void Light::setColor(jni::JNIEnv& env, const jni::String& property) { + auto color = Color::parse(jni::Make(env, property)); + if (color) { + light.setColor(color.value()); + } +} + +jni::Local Light::getColor(jni::JNIEnv &env) { + auto color = light.getColor().asConstant(); + return jni::Make(env, color.stringify()); +} + +jni::Local> Light::getColorTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = light.getColorTransition(); + return std::move(*convert>>(env, options)); +} + +void Light::setColorTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + light.setColorTransition(options); +} + +void Light::setIntensity(jni::JNIEnv&, jni::jfloat property) { + light.setIntensity(property); +} + +jni::jfloat Light::getIntensity(jni::JNIEnv&) { + return light.getIntensity().asConstant(); +} + +jni::Local> Light::getIntensityTransition(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + mbgl::style::TransitionOptions options = light.getIntensityTransition(); + return std::move(*convert>>(env, options)); +} + +void Light::setIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay) { + mbgl::style::TransitionOptions options; + options.duration.emplace(mbgl::Milliseconds(duration)); + options.delay.emplace(mbgl::Milliseconds(delay)); + light.setIntensityTransition(options); +} + +void Light::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + +#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + // Register the peer + jni::RegisterNativePeer(env, javaClass, "nativePtr", + METHOD(&Light::getAnchor, "nativeGetAnchor"), + METHOD(&Light::setAnchor, "nativeSetAnchor"), + METHOD(&Light::getPositionTransition, "nativeGetPositionTransition"), + METHOD(&Light::setPositionTransition, "nativeSetPositionTransition"), + METHOD(&Light::getPosition, "nativeGetPosition"), + METHOD(&Light::setPosition, "nativeSetPosition"), + METHOD(&Light::getColorTransition, "nativeGetColorTransition"), + METHOD(&Light::setColorTransition, "nativeSetColorTransition"), + METHOD(&Light::getColor, "nativeGetColor"), + METHOD(&Light::setColor, "nativeSetColor"), + METHOD(&Light::getIntensityTransition, "nativeGetIntensityTransition"), + METHOD(&Light::setIntensityTransition, "nativeSetIntensityTransition"), + METHOD(&Light::getIntensity, "nativeGetIntensity"), + METHOD(&Light::setIntensity, "nativeSetIntensity")); +} + +} // namespace android +} // namespace mb diff --git a/MapboxGLAndroidSDK/src/cpp/style/light.hpp b/MapboxGLAndroidSDK/src/cpp/style/light.hpp new file mode 100644 index 000000000..97767c4f7 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/light.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include + +#include +#include +#include "transition_options.hpp" +#include "position.hpp" +#include +#include + +namespace mbgl { +namespace android { + +using namespace style; + +class Light : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/light/Light"; }; + + static void registerNative(jni::JNIEnv&); + + static jni::Local> createJavaLightPeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Light&); + + Light(mbgl::Map&, mbgl::style::Light&); + + void setAnchor(jni::JNIEnv&, const jni::String&); + jni::Local getAnchor(jni::JNIEnv&); + void setPosition(jni::JNIEnv&, const jni::Object&); + jni::Local> getPosition(jni::JNIEnv&); + void setPositionTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getPositionTransition(jni::JNIEnv&); + void setColor(jni::JNIEnv&, const jni::String&); + jni::Local getColor(jni::JNIEnv&); + void setColorTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getColorTransition(jni::JNIEnv&); + void setIntensity(jni::JNIEnv&, jni::jfloat); + jni::jfloat getIntensity(jni::JNIEnv&); + void setIntensityTransition(jni::JNIEnv&, jlong duration, jlong delay); + jni::Local> getIntensityTransition(jni::JNIEnv&); + jni::Local> createJavaPeer(jni::JNIEnv&); + +protected: + + // Raw reference to the light + mbgl::style::Light& light; + + // Map is set when the light is retrieved + mbgl::Map* map; +}; +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/style/position.cpp b/MapboxGLAndroidSDK/src/cpp/style/position.cpp new file mode 100644 index 000000000..326310be0 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/position.cpp @@ -0,0 +1,35 @@ +#include "position.hpp" + +namespace mbgl { +namespace android { + +jni::Local> Position::fromPosition(jni::JNIEnv& env, jfloat radialCoordinate, jfloat azimuthalAngle, jfloat polarAngle) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jfloat, jfloat, jfloat)>(env, "fromPosition"); + return javaClass.Call(env, method, radialCoordinate, azimuthalAngle, polarAngle); +} + +void Position::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +float Position::getRadialCoordinate(jni::JNIEnv& env, const jni::Object& position) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "radialCoordinate"); + return position.Get(env, field); +} + +float Position::getAzimuthalAngle(jni::JNIEnv& env, const jni::Object& position) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "azimuthalAngle"); + return position.Get(env, field); +} + +float Position::getPolarAngle(jni::JNIEnv& env, const jni::Object& position) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "polarAngle"); + return position.Get(env, field); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/style/position.hpp b/MapboxGLAndroidSDK/src/cpp/style/position.hpp new file mode 100644 index 000000000..eb4f5ac67 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/position.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace android { + +class Position : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/light/Position"; }; + + static jni::Local> fromPosition(jni::JNIEnv&, jfloat, jfloat, jfloat); + + static void registerNative(jni::JNIEnv&); + + static float getRadialCoordinate(jni::JNIEnv&, const jni::Object&); + static float getAzimuthalAngle(jni::JNIEnv&, const jni::Object&); + static float getPolarAngle(jni::JNIEnv&, const jni::Object&); +}; + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/custom_geometry_source.cpp b/MapboxGLAndroidSDK/src/cpp/style/sources/custom_geometry_source.cpp new file mode 100644 index 000000000..e43bf8fa1 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/custom_geometry_source.cpp @@ -0,0 +1,209 @@ +#include "custom_geometry_source.hpp" +#include "attach_env.hpp" + +#include + +// Java -> C++ conversion +#include "../android_conversion.hpp" +#include "../conversion/filter.hpp" + +// C++ -> Java conversion +#include "../../conversion/conversion.hpp" +#include "../../conversion/collection.hpp" +#include "../../geojson/feature.hpp" +#include + +#include + +namespace mbgl { +namespace android { + + // This conversion is expected not to fail because it's used only in contexts where + // the value was originally a CustomGeometrySourceOptions object on the Java side. If it fails + // to convert, it's a bug in our serialization or Java-side static typing. + static style::CustomGeometrySource::Options convertCustomGeometrySourceOptions(jni::JNIEnv& env, + const jni::Object<>& options, + style::TileFunction fetchFn, + style::TileFunction cancelFn) { + using namespace mbgl::style::conversion; + if (!options) { + return style::CustomGeometrySource::Options(); + } + Error error; + optional result = convert(Value(env, options), error); + if (!result) { + throw std::logic_error(error.message); + } + result->fetchTileFunction = fetchFn; + result->cancelTileFunction = cancelFn; + return *result; + } + + CustomGeometrySource::CustomGeometrySource(jni::JNIEnv& env, + const jni::String& sourceId, + const jni::Object<>& options) + : Source(env, std::make_unique( + jni::Make(env, sourceId), + convertCustomGeometrySourceOptions(env, options, + std::bind(&CustomGeometrySource::fetchTile, this, std::placeholders::_1), + std::bind(&CustomGeometrySource::cancelTile, this, std::placeholders::_1)))) { + } + + CustomGeometrySource::CustomGeometrySource(jni::JNIEnv& env, + mbgl::style::Source& coreSource, + AndroidRendererFrontend* frontend) + : Source(env, coreSource, createJavaPeer(env), frontend) {} + + CustomGeometrySource::~CustomGeometrySource() { + releaseThreads(); + } + + void CustomGeometrySource::fetchTile (const mbgl::CanonicalTileID& tileID) { + android::UniqueEnv _env = android::AttachEnv(); + + static auto& javaClass = jni::Class::Singleton(*_env); + static auto fetchTile = javaClass.GetMethod(*_env, "fetchTile"); + + // The source is removed on the main thread, but it still exists on the Render thread until the frame is complete. + // This might cause fetchTile/cancelTile invocations when the Java thread is shutting down and the peer has already been released. + // See https://github.com/mapbox/mapbox-gl-native/issues/12551. + if(!javaPeer) { + return; + } + + auto peer = jni::Cast(*_env, javaClass, javaPeer); + peer.Call(*_env, fetchTile, (int)tileID.z, (int)tileID.x, (int)tileID.y); + }; + + void CustomGeometrySource::cancelTile(const mbgl::CanonicalTileID& tileID) { + android::UniqueEnv _env = android::AttachEnv(); + + static auto& javaClass = jni::Class::Singleton(*_env); + static auto cancelTile = javaClass.GetMethod(*_env, "cancelTile"); + + // The source is removed on the main thread, but it still exists on the Render thread until the frame is complete. + // This might cause fetchTile/cancelTile invocations when the Java thread is shutting down and the peer has already been released. + // See https://github.com/mapbox/mapbox-gl-native/issues/12551. + if(!javaPeer) { + return; + } + + auto peer = jni::Cast(*_env, javaClass, javaPeer); + peer.Call(*_env, cancelTile, (int)tileID.z, (int)tileID.x, (int)tileID.y); + }; + + void CustomGeometrySource::startThreads() { + android::UniqueEnv _env = android::AttachEnv(); + + static auto& javaClass = jni::Class::Singleton(*_env); + static auto startThreads = javaClass.GetMethod(*_env, "startThreads"); + + assert(javaPeer); + + auto peer = jni::Cast(*_env, javaClass, javaPeer); + peer.Call(*_env, startThreads); + } + + void CustomGeometrySource::releaseThreads() { + android::UniqueEnv _env = android::AttachEnv(); + + static auto& javaClass = jni::Class::Singleton(*_env); + static auto releaseThreads = javaClass.GetMethod(*_env, "releaseThreads"); + + assert(javaPeer); + + auto peer = jni::Cast(*_env, javaClass, javaPeer); + peer.Call(*_env, releaseThreads); + }; + + bool CustomGeometrySource::isCancelled(jni::jint z, + jni::jint x, + jni::jint y) { + android::UniqueEnv _env = android::AttachEnv(); + + static auto& javaClass = jni::Class::Singleton(*_env); + static auto isCancelled = javaClass.GetMethod(*_env, "isCancelled"); + + assert(javaPeer); + + auto peer = jni::Cast(*_env, javaClass, javaPeer); + return peer.Call(*_env, isCancelled, z, x, y); + }; + + void CustomGeometrySource::setTileData(jni::JNIEnv& env, + jni::jint z, + jni::jint x, + jni::jint y, + const jni::Object& jFeatures) { + using namespace mbgl::android::geojson; + + // Convert the jni object + auto geometry = geojson::FeatureCollection::convert(env, jFeatures); + + // Update the core source if not cancelled + if (!isCancelled(z, x ,y)) { + source.as()->CustomGeometrySource::setTileData(CanonicalTileID(z, x, y), GeoJSON(geometry)); + } + } + + void CustomGeometrySource::invalidateTile(jni::JNIEnv&, jni::jint z, jni::jint x, jni::jint y) { + source.as()->CustomGeometrySource::invalidateTile(CanonicalTileID(z, x, y)); + } + + void CustomGeometrySource::invalidateBounds(jni::JNIEnv& env, const jni::Object& jBounds) { + auto bounds = LatLngBounds::getLatLngBounds(env, jBounds); + source.as()->CustomGeometrySource::invalidateRegion(bounds); + } + + jni::Local>> CustomGeometrySource::querySourceFeatures(jni::JNIEnv& env, + const jni::Array>& jfilter) { + using namespace mbgl::android::conversion; + using namespace mbgl::android::geojson; + + std::vector features; + if (rendererFrontend) { + features = rendererFrontend->querySourceFeatures(source.getID(), { {}, toFilter(env, jfilter) }); + } + return Feature::convert(env, features); + } + + jni::Local> CustomGeometrySource::createJavaPeer(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(this)); + } + + void CustomGeometrySource::addToMap(JNIEnv& env, const jni::Object& obj, mbgl::Map& map, AndroidRendererFrontend& frontend) { + Source::addToMap(env, obj, map, frontend); + startThreads(); + } + + bool CustomGeometrySource::removeFromMap(JNIEnv& env, const jni::Object& source, mbgl::Map& map) { + bool successfullyRemoved = Source::removeFromMap(env, source, map); + if (successfullyRemoved) { + releaseThreads(); + } + return successfullyRemoved; + } + + void CustomGeometrySource::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, javaClass, "nativePtr", + jni::MakePeer&>, + "initialize", + "finalize", + METHOD(&CustomGeometrySource::querySourceFeatures, "querySourceFeatures"), + METHOD(&CustomGeometrySource::setTileData, "nativeSetTileData"), + METHOD(&CustomGeometrySource::invalidateTile, "nativeInvalidateTile"), + METHOD(&CustomGeometrySource::invalidateBounds, "nativeInvalidateBounds") + ); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/custom_geometry_source.hpp b/MapboxGLAndroidSDK/src/cpp/style/sources/custom_geometry_source.hpp new file mode 100644 index 000000000..6979ecc25 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/custom_geometry_source.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "source.hpp" +#include +#include +#include +#include "../../geojson/geometry.hpp" +#include "../../geojson/feature.hpp" +#include "../../geojson/feature_collection.hpp" +#include "../../geometry/lat_lng_bounds.hpp" +#include + +namespace mbgl { +namespace android { + +class CustomGeometrySource : public Source { +public: + using SuperTag = Source; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/CustomGeometrySource"; }; + + static void registerNative(jni::JNIEnv&); + + CustomGeometrySource(jni::JNIEnv&, const jni::String&, const jni::Object<>&); + CustomGeometrySource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*); + ~CustomGeometrySource(); + + bool removeFromMap(JNIEnv&, const jni::Object&, mbgl::Map&) override; + void addToMap(JNIEnv&, const jni::Object&, mbgl::Map&, AndroidRendererFrontend&) override; + + void fetchTile(const mbgl::CanonicalTileID& tileID); + void cancelTile(const mbgl::CanonicalTileID& tileID); + bool isCancelled(jni::jint z, jni::jint x, jni::jint y); + void startThreads(); + void releaseThreads(); + +private: + void setTileData(jni::JNIEnv& env, jni::jint z, jni::jint x, jni::jint y, const jni::Object& jf); + + void invalidateTile(jni::JNIEnv& env, jni::jint z, jni::jint x, jni::jint y); + void invalidateBounds(jni::JNIEnv& env, const jni::Object& bounds); + + jni::Local>> querySourceFeatures(jni::JNIEnv&, + const jni::Array>& ); + + jni::Local> createJavaPeer(jni::JNIEnv&); + +}; // class CustomGeometrySource + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/geojson_source.cpp b/MapboxGLAndroidSDK/src/cpp/style/sources/geojson_source.cpp new file mode 100644 index 000000000..5b699e9c6 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/geojson_source.cpp @@ -0,0 +1,259 @@ +#include "geojson_source.hpp" +#include +#include "attach_env.hpp" + +#include + +// Java -> C++ conversion +#include "../android_conversion.hpp" +#include "../conversion/filter.hpp" +#include +#include +#include + +// C++ -> Java conversion +#include "../../conversion/conversion.hpp" +#include "../../conversion/collection.hpp" +#include "../../geojson/feature.hpp" +#include "../conversion/url_or_tileset.hpp" + +#include + +// GeoJSONSource uses a "coalescing" model for high frequency asynchronous data update calls, +// which in practice means, that any update that started processing is going to finish +// and the last scheduled update is going to finish as well. Any updates scheduled during processing can be canceled. +// Conversion from Java features to core ones is done on a worker thread and once finished, +// the ownership of the converted features is returned to the calling thread. +namespace mbgl { +namespace android { + + // This conversion is expected not to fail because it's used only in contexts where + // the value was originally a GeoJsonOptions object on the Java side. If it fails + // to convert, it's a bug in our serialization or Java-side static typing. +static Immutable convertGeoJSONOptions(jni::JNIEnv& env, const jni::Object<>& options) { + using namespace mbgl::style::conversion; + if (!options) { + return style::GeoJSONOptions::defaultOptions(); + } + Error error; + optional result = convert(mbgl::android::Value(env, options), error); + if (!result) { + throw std::logic_error(error.message); + } + return makeMutable(std::move(*result)); +} + +GeoJSONSource::GeoJSONSource(jni::JNIEnv& env, const jni::String& sourceId, const jni::Object<>& options) + : Source(env, + std::make_unique(jni::Make(env, sourceId), + convertGeoJSONOptions(env, options))), + converter(std::make_unique>(Scheduler::GetBackground(), + source.as()->impl().getOptions())) {} + +GeoJSONSource::GeoJSONSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend) + : Source(env, coreSource, createJavaPeer(env), frontend), + converter(std::make_unique>(Scheduler::GetBackground(), + source.as()->impl().getOptions())) {} + +GeoJSONSource::~GeoJSONSource() = default; + +void GeoJSONSource::setGeoJSONString(jni::JNIEnv& env, const jni::String& jString) { + std::shared_ptr json = std::make_shared(jni::Make(env, jString)); + + Update::Converter converterFn = [this, json](ActorRef _callback) { + converter->self().invoke(&FeatureConverter::convertJson, json, _callback); + }; + + setAsync(converterFn); +} + + void GeoJSONSource::setFeatureCollection(jni::JNIEnv& env, const jni::Object& jFeatures) { + setCollectionAsync(env, jFeatures); + } + + void GeoJSONSource::setFeature(jni::JNIEnv& env, const jni::Object& jFeature) { + setCollectionAsync(env, jFeature); + } + + void GeoJSONSource::setGeometry(jni::JNIEnv& env, const jni::Object& jGeometry) { + setCollectionAsync(env, jGeometry); + } + + void GeoJSONSource::setURL(jni::JNIEnv& env, const jni::String& url) { + // Update the core source + source.as()->setURL(jni::Make(env, url)); + } + + jni::Local GeoJSONSource::getURL(jni::JNIEnv& env) { + optional url = source.as()->getURL(); + return url ? jni::Make(env, *url) : jni::Local(); + } + + jni::Local>> GeoJSONSource::querySourceFeatures(jni::JNIEnv& env, + const jni::Array>& jfilter) { + using namespace mbgl::android::conversion; + using namespace mbgl::android::geojson; + + std::vector features; + if (rendererFrontend) { + features = rendererFrontend->querySourceFeatures(source.getID(), + { {}, toFilter(env, jfilter) }); + } + return Feature::convert(env, features); + } + + jni::Local>> GeoJSONSource::getClusterChildren(jni::JNIEnv& env, const jni::Object& feature) { + using namespace mbgl::android::conversion; + using namespace mbgl::android::geojson; + + if (rendererFrontend) { + mbgl::Feature _feature = Feature::convert(env, feature); + _feature.properties["cluster_id"] = static_cast(_feature.properties["cluster_id"].get()); + const auto featureExtension = rendererFrontend->queryFeatureExtensions(source.getID(), _feature, "supercluster", "children", {}); + if (featureExtension.is()) { + return Feature::convert(env, featureExtension.get()); + } + } + return jni::Array>::New(env, 0); + } + + jni::Local>> GeoJSONSource::getClusterLeaves(jni::JNIEnv& env, const jni::Object& feature, jni::jlong limit, jni::jlong offset) { + using namespace mbgl::android::conversion; + using namespace mbgl::android::geojson; + + if (rendererFrontend) { + mbgl::Feature _feature = Feature::convert(env, feature); + _feature.properties["cluster_id"] = static_cast(_feature.properties["cluster_id"].get()); + const std::map options = { {"limit", static_cast(limit)}, + {"offset", static_cast(offset)} }; + auto featureExtension = rendererFrontend->queryFeatureExtensions(source.getID(), _feature, "supercluster", "leaves", options); + if (featureExtension.is()) { + return Feature::convert(env, featureExtension.get()); + } + } + return jni::Array>::New(env, 0);; + } + + jint GeoJSONSource::getClusterExpansionZoom(jni::JNIEnv& env, const jni::Object& feature) { + using namespace mbgl::android::conversion; + using namespace mbgl::android::geojson; + + if (rendererFrontend) { + mbgl::Feature _feature = Feature::convert(env, feature); + _feature.properties["cluster_id"] = static_cast(_feature.properties["cluster_id"].get()); + auto featureExtension = rendererFrontend->queryFeatureExtensions(source.getID(), _feature, "supercluster", "expansion-zoom", {}); + if (featureExtension.is()) { + auto value = featureExtension.get(); + if (value.is()) { + return value.get(); + } + } + } + return 0; + } + + jni::Local> GeoJSONSource::createJavaPeer(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(this)); + } + + template + void GeoJSONSource::setCollectionAsync(jni::JNIEnv& env, const jni::Object& jObject) { + auto global = jni::NewGlobal(env, jObject); + auto object = std::make_shared(std::move(global)); + + Update::Converter converterFn = [this, object](ActorRef _callback) { + converter->self().invoke(&FeatureConverter::convertObject, object, _callback); + }; + + setAsync(converterFn); + } + + void GeoJSONSource::setAsync(Update::Converter converterFn) { + awaitingUpdate = std::make_unique( + std::move(converterFn), + std::make_unique>( + *Scheduler::GetCurrent(), [this](std::shared_ptr geoJSONData) { + // conversion from Java features to core ones finished + android::UniqueEnv _env = android::AttachEnv(); + + // Update the core source + source.as()->setGeoJSONData(std::move(geoJSONData)); + + // if there is an awaiting update, execute it, otherwise, release resources + if (awaitingUpdate) { + update = std::move(awaitingUpdate); + update->converterFn(update->callback->self()); + } else { + update.reset(); + } + })); + + // If another update is running, wait + if (update) { + return; + } + + // no updates are being processed, execute this one + update = std::move(awaitingUpdate); + update->converterFn(update->callback->self()); + } + + void GeoJSONSource::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, javaClass, "nativePtr", + jni::MakePeer&>, + "initialize", + "finalize", + METHOD(&GeoJSONSource::setGeoJSONString, "nativeSetGeoJsonString"), + METHOD(&GeoJSONSource::setFeatureCollection, "nativeSetFeatureCollection"), + METHOD(&GeoJSONSource::setFeature, "nativeSetFeature"), + METHOD(&GeoJSONSource::setGeometry, "nativeSetGeometry"), + METHOD(&GeoJSONSource::setURL, "nativeSetUrl"), + METHOD(&GeoJSONSource::getURL, "nativeGetUrl"), + METHOD(&GeoJSONSource::querySourceFeatures, "querySourceFeatures"), + METHOD(&GeoJSONSource::getClusterChildren, "nativeGetClusterChildren"), + METHOD(&GeoJSONSource::getClusterLeaves, "nativeGetClusterLeaves"), + METHOD(&GeoJSONSource::getClusterExpansionZoom, "nativeGetClusterExpansionZoom") + ); + } + + void FeatureConverter::convertJson(std::shared_ptr json, ActorRef callback) { + using namespace mbgl::style::conversion; + + android::UniqueEnv _env = android::AttachEnv(); + // Convert the jni object + Error error; + optional converted = parseGeoJSON(*json, error); + if(!converted) { + mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + error.message); + return; + } + + callback.invoke(&GeoJSONDataCallback::operator(), style::GeoJSONData::create(*converted, options)); + } + + template + void FeatureConverter::convertObject( + std::shared_ptr, jni::EnvAttachingDeleter>> jObject, + ActorRef callback) { + using namespace mbgl::android::geojson; + + android::UniqueEnv _env = android::AttachEnv(); + // Convert the jni object + auto geometry = JNIType::convert(*_env, *jObject); + callback.invoke(&GeoJSONDataCallback::operator(), style::GeoJSONData::create(geometry, options)); + } + + Update::Update(Converter _converterFn, std::unique_ptr> _callback) + : converterFn(std::move(_converterFn)), callback(std::move(_callback)) {} + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/geojson_source.hpp b/MapboxGLAndroidSDK/src/cpp/style/sources/geojson_source.hpp new file mode 100644 index 000000000..c7ef6e54d --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/geojson_source.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include "source.hpp" +#include +#include "../../geojson/geometry.hpp" +#include "../../geojson/feature.hpp" +#include "../../geojson/feature_collection.hpp" +#include "../../android_renderer_frontend.hpp" +#include + +namespace mbgl { +namespace android { + +using GeoJSONDataCallback = std::function)>; + +class FeatureConverter { +public: + explicit FeatureConverter(Immutable options_) : options(std::move(options_)) {} + void convertJson(std::shared_ptr, ActorRef); + + template + void convertObject(std::shared_ptr, jni::EnvAttachingDeleter>>, + ActorRef); + +private: + Immutable options; +}; + +struct Update { + using Converter = std::function)>; + Converter converterFn; + + std::unique_ptr> callback; + + Update(Converter, std::unique_ptr>); +}; + +class GeoJSONSource : public Source { +public: + using SuperTag = Source; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/GeoJsonSource"; }; + + static void registerNative(jni::JNIEnv&); + + GeoJSONSource(jni::JNIEnv&, const jni::String&, const jni::Object<>&); + GeoJSONSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*); + ~GeoJSONSource(); + +private: + void setGeoJSONString(jni::JNIEnv&, const jni::String&); + void setFeatureCollection(jni::JNIEnv&, const jni::Object&); + void setFeature(jni::JNIEnv&, const jni::Object&); + void setGeometry(jni::JNIEnv&, const jni::Object&); + void setURL(jni::JNIEnv&, const jni::String&); + + jni::Local>> querySourceFeatures(jni::JNIEnv&, + const jni::Array>&); + + jni::Local>> getClusterChildren(jni::JNIEnv&, const jni::Object&); + jni::Local>> getClusterLeaves(jni::JNIEnv&, const jni::Object&, jni::jlong, jni::jlong); + jint getClusterExpansionZoom(jni::JNIEnv&, const jni::Object&); + + jni::Local getURL(jni::JNIEnv&); + + jni::Local> createJavaPeer(jni::JNIEnv&); + std::unique_ptr awaitingUpdate; + std::unique_ptr update; + std::shared_ptr threadPool; + std::unique_ptr> converter; + + template + void setCollectionAsync(jni::JNIEnv&, const jni::Object&); + + void setAsync(Update::Converter); + +}; // class GeoJSONSource + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/image_source.cpp b/MapboxGLAndroidSDK/src/cpp/style/sources/image_source.cpp new file mode 100644 index 000000000..614c39c21 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/image_source.cpp @@ -0,0 +1,76 @@ +#include "image_source.hpp" + +// Java -> C++ conversion +#include "../android_conversion.hpp" + +// C++ -> Java conversion +#include "../../conversion/conversion.hpp" +#include +#include + +#include "bitmap.hpp" +#include +#include + +namespace mbgl { +namespace android { + + ImageSource::ImageSource(jni::JNIEnv& env, const jni::String& sourceId, const jni::Object& coordinatesObject) + : Source(env, std::make_unique( + jni::Make(env, sourceId), + LatLngQuad::getLatLngArray(env, coordinatesObject) + ) + ) { + } + + ImageSource::ImageSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend) + : Source(env, coreSource, createJavaPeer(env), frontend) {} + + ImageSource::~ImageSource() = default; + + void ImageSource::setURL(jni::JNIEnv& env, const jni::String& url) { + // Update the core source + source.as()->ImageSource::setURL(jni::Make(env, url)); + } + + jni::Local ImageSource::getURL(jni::JNIEnv& env) { + optional url = source.as()->ImageSource::getURL(); + return url ? jni::Make(env, *url) : jni::Local(); + } + + void ImageSource::setImage(jni::JNIEnv& env, const jni::Object& bitmap) { + source.as()->setImage(Bitmap::GetImage(env, bitmap)); + } + + void ImageSource::setCoordinates(jni::JNIEnv& env, const jni::Object& coordinatesObject) { + source.as()->setCoordinates( + LatLngQuad::getLatLngArray(env, coordinatesObject)); + } + + jni::Local> ImageSource::createJavaPeer(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(this)); + } + + void ImageSource::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, javaClass, "nativePtr", + jni::MakePeer&>, + "initialize", + "finalize", + METHOD(&ImageSource::setURL, "nativeSetUrl"), + METHOD(&ImageSource::getURL, "nativeGetUrl"), + METHOD(&ImageSource::setImage, "nativeSetImage"), + METHOD(&ImageSource::setCoordinates, "nativeSetCoordinates") + ); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/image_source.hpp b/MapboxGLAndroidSDK/src/cpp/style/sources/image_source.hpp new file mode 100644 index 000000000..da88feac7 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/image_source.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "source.hpp" +#include "../../geometry/lat_lng_quad.hpp" +#include +#include + +namespace mbgl { +namespace android { + +class Bitmap; + +class ImageSource : public Source { +public: + using SuperTag = Source; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/ImageSource"; }; + + static void registerNative(jni::JNIEnv&); + + ImageSource(jni::JNIEnv&, const jni::String&, const jni::Object&); + + ImageSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*); + + ~ImageSource(); + + void setURL(jni::JNIEnv&, const jni::String&); + jni::Local getURL(jni::JNIEnv&); + + void setImage(jni::JNIEnv&, const jni::Object&); + + void setCoordinates(jni::JNIEnv&, const jni::Object&); + +private: + jni::Local> createJavaPeer(jni::JNIEnv&); + +}; // class ImageSource + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/raster_dem_source.cpp b/MapboxGLAndroidSDK/src/cpp/style/sources/raster_dem_source.cpp new file mode 100644 index 000000000..a33e00259 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/raster_dem_source.cpp @@ -0,0 +1,61 @@ +#include "raster_dem_source.hpp" + +#include "../android_conversion.hpp" +#include "../value.hpp" +#include "../conversion/url_or_tileset.hpp" +#include "source.hpp" + +#include + +#include + +namespace mbgl { +namespace android { + + RasterDEMSource::RasterDEMSource(jni::JNIEnv& env, const jni::String& sourceId, const jni::Object<>& urlOrTileSet, jni::jint tileSize) + : Source( + env, + std::make_unique( + jni::Make(env, sourceId), + convertURLOrTileset(Value(env, urlOrTileSet)), + tileSize + ) + ) { + } + + RasterDEMSource::RasterDEMSource(jni::JNIEnv& env, + mbgl::style::Source& coreSource, + AndroidRendererFrontend* frontend) + : Source(env, coreSource, createJavaPeer(env), frontend) {} + + RasterDEMSource::~RasterDEMSource() = default; + + jni::Local RasterDEMSource::getURL(jni::JNIEnv& env) { + optional url = source.as()->RasterDEMSource::getURL(); + return url ? jni::Make(env, *url) : jni::Local(); + } + + jni::Local> RasterDEMSource::createJavaPeer(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(this)); + } + + void RasterDEMSource::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, javaClass, "nativePtr", + jni::MakePeer&, jni::jint>, + "initialize", + "finalize", + METHOD(&RasterDEMSource::getURL, "nativeGetUrl") + ); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/raster_dem_source.hpp b/MapboxGLAndroidSDK/src/cpp/style/sources/raster_dem_source.hpp new file mode 100644 index 000000000..581e8f8bc --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/raster_dem_source.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "source.hpp" +#include +#include + +namespace mbgl { +namespace android { + +class RasterDEMSource : public Source { +public: + using SuperTag = Source; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/RasterDemSource"; }; + + static void registerNative(jni::JNIEnv&); + + RasterDEMSource(jni::JNIEnv&, const jni::String&, const jni::Object<>&, jni::jint); + RasterDEMSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*); + ~RasterDEMSource(); + + jni::Local getURL(jni::JNIEnv&); + +private: + jni::Local> createJavaPeer(jni::JNIEnv&); + +}; // class RasterDEMSource + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/raster_source.cpp b/MapboxGLAndroidSDK/src/cpp/style/sources/raster_source.cpp new file mode 100644 index 000000000..67603e74c --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/raster_source.cpp @@ -0,0 +1,58 @@ +#include "raster_source.hpp" + +#include "../android_conversion.hpp" +#include "../value.hpp" +#include "../conversion/url_or_tileset.hpp" + +#include + +#include + +namespace mbgl { +namespace android { + + RasterSource::RasterSource(jni::JNIEnv& env, const jni::String& sourceId, const jni::Object<>& urlOrTileSet, jni::jint tileSize) + : Source( + env, + std::make_unique( + jni::Make(env, sourceId), + convertURLOrTileset(Value(env, urlOrTileSet)), + tileSize + ) + ) { + } + + RasterSource::RasterSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend) + : Source(env, coreSource, createJavaPeer(env), frontend) {} + + RasterSource::~RasterSource() = default; + + jni::Local RasterSource::getURL(jni::JNIEnv& env) { + optional url = source.as()->RasterSource::getURL(); + return url ? jni::Make(env, *url) : jni::Local(); + } + + jni::Local> RasterSource::createJavaPeer(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(this)); + } + + void RasterSource::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, javaClass, "nativePtr", + jni::MakePeer&, jni::jint>, + "initialize", + "finalize", + METHOD(&RasterSource::getURL, "nativeGetUrl") + ); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/raster_source.hpp b/MapboxGLAndroidSDK/src/cpp/style/sources/raster_source.hpp new file mode 100644 index 000000000..e1bc5ca7a --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/raster_source.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "source.hpp" +#include +#include + +namespace mbgl { +namespace android { + +class RasterSource : public Source { +public: + using SuperTag = Source; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/RasterSource"; }; + + static void registerNative(jni::JNIEnv&); + + RasterSource(jni::JNIEnv&, const jni::String&, const jni::Object<>&, jni::jint); + RasterSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*); + ~RasterSource(); + + jni::Local getURL(jni::JNIEnv&); + +private: + jni::Local> createJavaPeer(jni::JNIEnv&); + +}; // class RasterSource + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/source.cpp b/MapboxGLAndroidSDK/src/cpp/style/sources/source.cpp new file mode 100644 index 000000000..29e41976c --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/source.cpp @@ -0,0 +1,230 @@ +#include "source.hpp" +#include "../android_conversion.hpp" +#include "attach_env.hpp" + +#include + +#include +#include + +// Java -> C++ conversion +#include +#include + +// C++ -> Java conversion +#include "../conversion/property_value.hpp" + +#include + +// Core Sources +#include +#include +#include +#include + +// Android Source peers +#include "geojson_source.hpp" +#include "image_source.hpp" +#include "raster_source.hpp" +#include "unknown_source.hpp" +#include "vector_source.hpp" +#include "custom_geometry_source.hpp" +#include "raster_dem_source.hpp" + +namespace mbgl { +namespace android { + +static std::unique_ptr createSourcePeer(jni::JNIEnv& env, + mbgl::style::Source& coreSource, + AndroidRendererFrontend* frontend) { + if (coreSource.is()) { + return std::make_unique(env, *coreSource.as(), frontend); + } else if (coreSource.is()) { + return std::make_unique(env, *coreSource.as(), frontend); + } else if (coreSource.is()) { + return std::make_unique(env, *coreSource.as(), frontend); + } else if (coreSource.is()) { + return std::make_unique(env, *coreSource.as(), frontend); + } else { + return std::make_unique(env, coreSource, frontend); + } +} + + const jni::Object& Source::peerForCoreSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend& frontend) { + if (!coreSource.peer.has_value()) { + coreSource.peer = createSourcePeer(env, coreSource, &frontend); + } + return coreSource.peer.get>()->javaPeer; + } + + const jni::Object& Source::peerForCoreSource(jni::JNIEnv& env, mbgl::style::Source& coreSource) { + if (!coreSource.peer.has_value()) { + coreSource.peer = createSourcePeer(env, coreSource, nullptr); + } + return coreSource.peer.get>()->javaPeer; + } + + Source::Source(jni::JNIEnv& env, + mbgl::style::Source& coreSource, + const jni::Object& obj, + AndroidRendererFrontend* frontend) + : source(coreSource), javaPeer(jni::NewGlobal(env, obj)), rendererFrontend(frontend) {} + + Source::Source(jni::JNIEnv&, std::unique_ptr coreSource) + : ownedSource(std::move(coreSource)) + , source(*ownedSource) { + } + + Source::~Source() { + // Before being added to a map, the Java peer owns this C++ peer and cleans + // up after itself correctly through the jni native peer bindings. + // After being added to the map, the ownership is flipped and the C++ peer has a strong reference + // to it's Java peer, preventing the Java peer from being GC'ed. + // In this case, the core source initiates the destruction, which requires releasing the Java peer, + // while also resetting it's nativePtr to 0 to prevent the subsequent GC of the Java peer from + // re-entering this dtor. + if (ownedSource.get() == nullptr && javaPeer.get() != nullptr) { + // Manually clear the java peer + android::UniqueEnv env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*env); + static auto nativePtrField = javaClass.GetField(*env, "nativePtr"); + javaPeer.Set(*env, nativePtrField, (jlong) 0); + javaPeer.reset(); + } + } + + jni::Local Source::getId(jni::JNIEnv& env) { + return jni::Make(env, source.getID()); + } + + jni::Local Source::getAttribution(jni::JNIEnv& env) { + auto attribution = source.getAttribution(); + return attribution ? jni::Make(env, attribution.value()) : jni::Make(env,""); + } + + void Source::setPrefetchZoomDelta(jni::JNIEnv& env, jni::Integer& delta) { + if (!delta) { + source.setPrefetchZoomDelta(nullopt); + } else { + source.setPrefetchZoomDelta(jni::Unbox(env, delta)); + } + } + + jni::Local Source::getPrefetchZoomDelta(jni::JNIEnv& env) { + auto delta = source.getPrefetchZoomDelta(); + if (delta.has_value()) { + return jni::Box(env, jni::jint(delta.value())); + } + return jni::Local(env, nullptr); + } + + void Source::setMaxOverscaleFactorForParentTiles(jni::JNIEnv& env, jni::Integer& maxOverscaleFactor) { + if (!maxOverscaleFactor) { + source.setMaxOverscaleFactorForParentTiles(nullopt); + } else { + source.setMaxOverscaleFactorForParentTiles(jni::Unbox(env, maxOverscaleFactor)); + } + } + + jni::Local Source::getMaxOverscaleFactorForParentTiles(jni::JNIEnv& env) { + auto maxOverscaleFactor = source.getMaxOverscaleFactorForParentTiles(); + if (maxOverscaleFactor) { + return jni::Box(env, jni::jint(*maxOverscaleFactor)); + } + return jni::Local(env, nullptr); + } + + void Source::addToStyle(JNIEnv& env, const jni::Object& obj, mbgl::style::Style& style) { + if (!ownedSource) { + throw std::runtime_error("Cannot add source twice"); + } + + // Add source to style and release ownership + style.addSource(std::move(ownedSource)); + + // Add peer to core source + source.peer = std::unique_ptr(this); + + // Add strong reference to java source + javaPeer = jni::NewGlobal(env, obj); + } + + void Source::addToMap(JNIEnv& env, const jni::Object& obj, mbgl::Map& map, AndroidRendererFrontend& frontend) { + // Check to see if we own the source first + if (!ownedSource) { + throw std::runtime_error("Cannot add source twice"); + } + + // Add source to map and release ownership + map.getStyle().addSource(std::move(ownedSource)); + + // Add peer to core source + source.peer = std::unique_ptr(this); + + // Add strong reference to java source + javaPeer = jni::NewGlobal(env, obj); + + rendererFrontend = &frontend; + } + + bool Source::removeFromMap(JNIEnv&, const jni::Object&, mbgl::Map& map) { + // Cannot remove if not attached yet + if (ownedSource) { + throw std::runtime_error("Cannot remove detached source"); + } + + // Remove the source from the map and take ownership + ownedSource = map.getStyle().removeSource(source.getID()); + + // The source may not be removed if any layers still reference it + return ownedSource != nullptr; + } + + void Source::releaseJavaPeer() { + // We can't release the peer if the source was not removed from the map + if (!ownedSource) { + return; + } + + // Release the peer relationships. These will be re-established when the source is added to a map + assert(ownedSource->peer.has_value()); + ownedSource->peer.get>().release(); + ownedSource->peer = mapbox::base::TypeWrapper(); + + // Release the strong reference to the java peer + assert(javaPeer); + javaPeer.release(); + + rendererFrontend = nullptr; + } + + void Source::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, + javaClass, + "nativePtr", + METHOD(&Source::getId, "nativeGetId"), + METHOD(&Source::getAttribution, "nativeGetAttribution"), + METHOD(&Source::setPrefetchZoomDelta, "nativeSetPrefetchZoomDelta"), + METHOD(&Source::getPrefetchZoomDelta, "nativeGetPrefetchZoomDelta"), + METHOD(&Source::setMaxOverscaleFactorForParentTiles, "nativeSetMaxOverscaleFactorForParentTiles"), + METHOD(&Source::getMaxOverscaleFactorForParentTiles, "nativeGetMaxOverscaleFactorForParentTiles")); + + // Register subclasses + GeoJSONSource::registerNative(env); + ImageSource::registerNative(env); + RasterSource::registerNative(env); + UnknownSource::registerNative(env); + VectorSource::registerNative(env); + CustomGeometrySource::registerNative(env); + RasterDEMSource::registerNative(env); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/source.hpp b/MapboxGLAndroidSDK/src/cpp/style/sources/source.hpp new file mode 100644 index 000000000..fbeb201c7 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/source.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +#include "../value.hpp" +#include "../../android_renderer_frontend.hpp" + +#include + +namespace mbgl { +namespace android { + +class Source : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/Source"; }; + + static void registerNative(jni::JNIEnv&); + + static const jni::Object& peerForCoreSource(jni::JNIEnv&, mbgl::style::Source&); + static const jni::Object& peerForCoreSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&); + + /* + * Called when a Java object is created for a core source that belongs to a map. + */ + Source(jni::JNIEnv&, mbgl::style::Source&, const jni::Object&, AndroidRendererFrontend*); + + /* + * Called when a Java object is created for a new core source that does not belong to a map. + */ + Source(jni::JNIEnv&, std::unique_ptr); + + virtual ~Source(); + + virtual void addToMap(JNIEnv&, const jni::Object&, mbgl::Map&, AndroidRendererFrontend&); + + virtual bool removeFromMap(JNIEnv&, const jni::Object&, mbgl::Map&); + + void releaseJavaPeer(); + + jni::Local getId(jni::JNIEnv&); + + jni::Local getAttribution(jni::JNIEnv&); + + void setPrefetchZoomDelta(jni::JNIEnv& env, jni::Integer& delta); + + jni::Local getPrefetchZoomDelta(jni::JNIEnv&); + + void setMaxOverscaleFactorForParentTiles(jni::JNIEnv& env, jni::Integer& delta); + + jni::Local getMaxOverscaleFactorForParentTiles(jni::JNIEnv&); + + void addToStyle(JNIEnv& env, const jni::Object& obj, mbgl::style::Style& style); + +protected: + // Set on newly created sources until added to the map. + std::unique_ptr ownedSource; + + // Raw pointer that is valid at all times. + mbgl::style::Source& source; + + // Set when the source is added to a map. + jni::Global> javaPeer; + + // RendererFrontend pointer is valid only when added to the map. + AndroidRendererFrontend* rendererFrontend { nullptr }; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/unknown_source.cpp b/MapboxGLAndroidSDK/src/cpp/style/sources/unknown_source.cpp new file mode 100644 index 000000000..acfd52db8 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/unknown_source.cpp @@ -0,0 +1,40 @@ +#include "unknown_source.hpp" + +namespace { + + // Dummy initializer (We don't support initializing this from the JVM) + std::unique_ptr init(jni::JNIEnv&) { + throw std::runtime_error("UnknownSource should not be initialized from the JVM"); + } + +} // namespace + +namespace mbgl { +namespace android { + +UnknownSource::UnknownSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend) + : Source(env, coreSource, createJavaPeer(env), frontend) {} + +jni::Local> UnknownSource::createJavaPeer(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(this)); +} + + void UnknownSource::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, javaClass, "nativePtr", + init, + "initialize", + "finalize" + ); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/unknown_source.hpp b/MapboxGLAndroidSDK/src/cpp/style/sources/unknown_source.hpp new file mode 100644 index 000000000..ab5a2da82 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/unknown_source.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "source.hpp" +#include +#include + +namespace mbgl { +namespace android { + +class UnknownSource : public Source { +public: + using SuperTag = Source; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/UnknownSource"; }; + + static void registerNative(jni::JNIEnv&); + + UnknownSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*); + + ~UnknownSource() = default; + +private: + jni::Local> createJavaPeer(jni::JNIEnv&); + +}; // class UnknownSource + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/vector_source.cpp b/MapboxGLAndroidSDK/src/cpp/style/sources/vector_source.cpp new file mode 100644 index 000000000..2be1a7811 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/vector_source.cpp @@ -0,0 +1,81 @@ +#include "vector_source.hpp" + +#include + +// Java -> C++ conversion +#include "../android_conversion.hpp" +#include "../conversion/filter.hpp" + +// C++ -> Java conversion +#include "../../conversion/conversion.hpp" +#include "../../conversion/collection.hpp" +#include "../../geojson/feature.hpp" +#include "../conversion/url_or_tileset.hpp" + +#include + +#include +#include + +namespace mbgl { +namespace android { + + VectorSource::VectorSource(jni::JNIEnv& env, const jni::String& sourceId, const jni::Object<>& urlOrTileSet) + : Source( + env, + std::make_unique( + jni::Make(env, sourceId), + convertURLOrTileset(Value(env, urlOrTileSet)) + ) + ) { + } + + VectorSource::VectorSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend) + : Source(env, coreSource, createJavaPeer(env), frontend) {} + + VectorSource::~VectorSource() = default; + + jni::Local VectorSource::getURL(jni::JNIEnv& env) { + optional url = source.as()->VectorSource::getURL(); + return url ? jni::Make(env, *url) : jni::Local(); + } + + jni::Local>> VectorSource::querySourceFeatures(jni::JNIEnv& env, + const jni::Array& jSourceLayerIds, + const jni::Array>& jfilter) { + using namespace mbgl::android::conversion; + using namespace mbgl::android::geojson; + + std::vector features; + if (rendererFrontend) { + features = rendererFrontend->querySourceFeatures(source.getID(), + { toVector(env, jSourceLayerIds), toFilter(env, jfilter) }); + } + return Feature::convert(env, features); + } + + jni::Local> VectorSource::createJavaPeer(jni::JNIEnv& env) { + static auto& javaClass = jni::Class::Singleton(env); + static auto constructor = javaClass.GetConstructor(env); + return javaClass.New(env, constructor, reinterpret_cast(this)); + } + + void VectorSource::registerNative(jni::JNIEnv& env) { + // Lookup the class + static auto& javaClass = jni::Class::Singleton(env); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + // Register the peer + jni::RegisterNativePeer( + env, javaClass, "nativePtr", + jni::MakePeer&>, + "initialize", + "finalize", + METHOD(&VectorSource::querySourceFeatures, "querySourceFeatures"), + METHOD(&VectorSource::getURL, "nativeGetUrl") + ); + } + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/sources/vector_source.hpp b/MapboxGLAndroidSDK/src/cpp/style/sources/vector_source.hpp new file mode 100644 index 000000000..7ae2e8c9e --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/sources/vector_source.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "source.hpp" +#include +#include "../../geojson/feature.hpp" +#include + +namespace mbgl { +namespace android { + +class VectorSource : public Source { +public: + using SuperTag = Source; + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/VectorSource"; }; + + static void registerNative(jni::JNIEnv&); + + VectorSource(jni::JNIEnv&, const jni::String&, const jni::Object<>&); + VectorSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*); + ~VectorSource(); + +private: + jni::Local>> querySourceFeatures(jni::JNIEnv&, const jni::Array&, + const jni::Array>& jfilter); + + jni::Local getURL(jni::JNIEnv&); + + jni::Local> createJavaPeer(jni::JNIEnv&); + +}; // class VectorSource + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/style/transition_options.cpp b/MapboxGLAndroidSDK/src/cpp/style/transition_options.cpp new file mode 100644 index 000000000..d70a04e0d --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/transition_options.cpp @@ -0,0 +1,35 @@ +#include "transition_options.hpp" + +namespace mbgl { +namespace android { + +jni::Local> TransitionOptions::fromTransitionOptions(jni::JNIEnv& env, jlong duration, jlong delay, jboolean enablePlacementTransitions) { + static auto& javaClass = jni::Class::Singleton(env); + static auto method = javaClass.GetStaticMethod (jlong, jlong, jboolean)>(env, "fromTransitionOptions"); + return javaClass.Call(env, method, duration, delay, enablePlacementTransitions); +} + +long TransitionOptions::getDuration(jni::JNIEnv& env, const jni::Object& transitionOptions) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "duration"); + return transitionOptions.Get(env, field); +} + +long TransitionOptions::getDelay(jni::JNIEnv& env, const jni::Object& transitionOptions) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "delay"); + return transitionOptions.Get(env, field); +} + +bool TransitionOptions::isEnablePlacementTransitions(jni::JNIEnv& env, const jni::Object& transitionOptions) { + static auto& javaClass = jni::Class::Singleton(env); + static auto field = javaClass.GetField(env, "enablePlacementTransitions"); + return transitionOptions.Get(env, field); +} + +void TransitionOptions::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/style/transition_options.hpp b/MapboxGLAndroidSDK/src/cpp/style/transition_options.hpp new file mode 100644 index 000000000..58656a946 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/transition_options.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace android { + +class TransitionOptions : private mbgl::util::noncopyable { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/TransitionOptions"; }; + + static jni::Local> fromTransitionOptions(jni::JNIEnv&, jlong duration, jlong delay, jboolean enablePlacementTransitions); + + static long getDuration(jni::JNIEnv&, const jni::Object&); + + static long getDelay(jni::JNIEnv&, const jni::Object&); + + static bool isEnablePlacementTransitions(jni::JNIEnv&, const jni::Object&); + + static void registerNative(jni::JNIEnv&); +}; + +} // namespace android +} // namespace mbgl \ No newline at end of file diff --git a/MapboxGLAndroidSDK/src/cpp/style/value.cpp b/MapboxGLAndroidSDK/src/cpp/style/value.cpp new file mode 100644 index 000000000..2f0484072 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/value.cpp @@ -0,0 +1,83 @@ +#include "value.hpp" + +#include "../java_types.hpp" + +namespace mbgl { +namespace android { + // Instance + + Value::Value(jni::JNIEnv& _env, jni::Local> _value) + : env(_env), + value(std::move(_value)) {} + + Value::Value(jni::JNIEnv& _env, const jni::Object<>& _value) + : env(_env), + value(jni::NewLocal(_env, _value)) {} + + bool Value::isNull() const { + return !value; + } + + bool Value::isArray() const { + return jni::IsInstanceOf(env, value.get(), *java::ObjectArray::jclass); + } + + bool Value::isObject() const { + return jni::IsInstanceOf(env, value.get(), *java::Map::jclass); + } + + bool Value::isString() const { + return jni::IsInstanceOf(env, value.get(), *java::String::jclass); + } + + bool Value::isBool() const { + return jni::IsInstanceOf(env, value.get(), *java::Boolean::jclass); + } + + bool Value::isNumber() const { + return jni::IsInstanceOf(env, value.get(), *java::Number::jclass); + } + + std::string Value::toString() const { + return jni::Make(env, jni::Cast(env, jni::Class::Singleton(env), value)); + } + + float Value::toFloat() const { + return jni::CallMethod(env, value.get(), *java::Number::floatValueMethodId); + } + + double Value::toDouble() const { + return jni::CallMethod(env, value.get(), *java::Number::doubleValueMethodId); + } + + long Value::toLong() const { + return jni::CallMethod(env, value.get(), *java::Number::longValueMethodId); + } + + bool Value::toBool() const { + return jni::CallMethod(env, value.get(), *java::Boolean::booleanValueMethodId); + } + + Value Value::get(const char* key) const { + jni::jobject* member = jni::CallMethod(env, value.get(), *java::Map::getMethodId, jni::Make(env, std::string(key)).get()); + return Value(env, jni::Local>(env, member)); + } + + Value Value::keyArray() const{ + jni::jobject* set = jni::CallMethod(env, value.get(), *java::Map::keySetMethodId); + jni::jobject* array = jni::CallMethod(env, set, *java::Set::toArrayMethodId); + return Value(env, jni::Local>(env, array)); + } + + int Value::getLength() const { + auto array = (jni::jarray*) value.get(); + return jni::GetArrayLength(env, *array); + } + + Value Value::get(const int index) const { + auto array = (jni::jarray*) value.get(); + return Value(env, jni::Local>(env, jni::GetObjectArrayElement(env, *array, index))); + } +} +} + diff --git a/MapboxGLAndroidSDK/src/cpp/style/value.hpp b/MapboxGLAndroidSDK/src/cpp/style/value.hpp new file mode 100644 index 000000000..b507c5ed1 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/style/value.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include + +namespace mbgl { +namespace android { + +class Value { +public: + Value(jni::JNIEnv&, jni::Local>); + Value(jni::JNIEnv&, const jni::Object<>&); + + Value(Value&&) = default; + Value& operator=(Value&&) = delete; + + Value(const Value&) = delete; + Value& operator=(const Value&) = delete; + + bool isNull() const; + bool isArray() const; + bool isObject() const; + bool isString() const; + bool isBool() const; + bool isNumber() const; + + std::string toString() const; + float toFloat() const; + double toDouble() const; + long toLong() const; + bool toBool() const; + Value get(const char* key) const; + Value keyArray() const; + int getLength() const; + Value get(const int index ) const; + + jni::JNIEnv& env; + jni::Local> value; +}; + +} +} diff --git a/MapboxGLAndroidSDK/src/cpp/text/local_glyph_rasterizer.cpp b/MapboxGLAndroidSDK/src/cpp/text/local_glyph_rasterizer.cpp new file mode 100644 index 000000000..3c30175bd --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/text/local_glyph_rasterizer.cpp @@ -0,0 +1,131 @@ +#include +#include +#include + +#include + +#include "attach_env.hpp" +#include "bitmap.hpp" + +#include "local_glyph_rasterizer_jni.hpp" + +/* + Android implementation of LocalGlyphRasterizer: + Draws CJK glyphs using locally available fonts. + + Follows pattern of GL JS implementation in that: + - Only CJK glyphs are drawn locally (because we can guess their metrics effectively) + * Render size/metrics determined experimentally using Noto Sans + - Configuration is done at map creation time by setting a "font family" + * JS uses a CSS font-family, this uses android.graphics.Typeface + https://developer.android.com/reference/android/graphics/Typeface.html + - We use heuristics to extract a font-weight based on the incoming font stack + * JS tries to extract multiple weights, this implementation only looks for + "bold" + + mbgl::LocalGlyphRasterizer is the portable interface + mbgl::LocalGlyphRasterizer::Impl stores platform-specific configuration data + mbgl::android::LocalGlyphRasterizer is the JNI wrapper + com.mapbox.mapboxsdk.text.LocalGlyphRasterizer is the Java implementation that + actually does the drawing + */ + +namespace mbgl { +namespace android { + +LocalGlyphRasterizer::LocalGlyphRasterizer() { + UniqueEnv env { AttachEnv() }; + + static auto& javaClass = jni::Class::Singleton(*env); + static auto constructor = javaClass.GetConstructor(*env); + + javaObject = jni::NewGlobal(*env, javaClass.New(*env, constructor)); +} + +PremultipliedImage LocalGlyphRasterizer::drawGlyphBitmap(const std::string& fontFamily, const bool bold, const GlyphID glyphID) { + UniqueEnv env { AttachEnv() }; + + static auto& javaClass = jni::Class::Singleton(*env); + static auto drawGlyphBitmap = javaClass.GetMethod (jni::String, jni::jboolean, jni::jchar)>(*env, "drawGlyphBitmap"); + + return Bitmap::GetImage(*env, + javaObject.Call(*env, + drawGlyphBitmap, + jni::Make(*env, fontFamily), + static_cast(bold), + static_cast(glyphID))); +} + +void LocalGlyphRasterizer::registerNative(jni::JNIEnv& env) { + jni::Class::Singleton(env); +} + +} // namespace android + +class LocalGlyphRasterizer::Impl { +public: + Impl(const optional fontFamily_) + : fontFamily(fontFamily_) + {} + + bool isConfigured() const { + return bool(fontFamily); + } + + PremultipliedImage drawGlyphBitmap(const FontStack& fontStack, GlyphID glyphID) { + bool bold = false; + for (auto font : fontStack) { + std::string lowercaseFont = platform::lowercase(font); + if (lowercaseFont.find("bold") != std::string::npos) { + bold = true; + break; + } + } + return androidLocalGlyphRasterizer.drawGlyphBitmap(*fontFamily, bold, glyphID); + } + +private: + optional fontFamily; + android::LocalGlyphRasterizer androidLocalGlyphRasterizer; +}; + +LocalGlyphRasterizer::LocalGlyphRasterizer(const optional& fontFamily) + : impl(std::make_unique(fontFamily)) {} + +LocalGlyphRasterizer::~LocalGlyphRasterizer() +{} + +bool LocalGlyphRasterizer::canRasterizeGlyph(const FontStack&, GlyphID glyphID) { + return util::i18n::allowsFixedWidthGlyphGeneration(glyphID) && impl->isConfigured(); +} + +Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack& fontStack, GlyphID glyphID) { + Glyph fixedMetrics; + if (!impl->isConfigured()) { + return fixedMetrics; + } + + fixedMetrics.id = glyphID; + + Size size(35, 35); + + fixedMetrics.metrics.width = size.width; + fixedMetrics.metrics.height = size.height; + fixedMetrics.metrics.left = 3; + fixedMetrics.metrics.top = -10; + fixedMetrics.metrics.advance = 24; + + PremultipliedImage rgbaBitmap = impl->drawGlyphBitmap(fontStack, glyphID); + + // Copy alpha values from RGBA bitmap into the AlphaImage output + fixedMetrics.bitmap = AlphaImage(size); + for (uint32_t i = 0; i < size.width * size.height; i++) { + fixedMetrics.bitmap.data[i] = + 0xff - round(0.2126 * rgbaBitmap.data[4 * i] + 0.7152 * rgbaBitmap.data[4 * i + 1] + + 0.0722 * rgbaBitmap.data[4 * i + 2]); + } + + return fixedMetrics; +} + +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/cpp/text/local_glyph_rasterizer_jni.hpp b/MapboxGLAndroidSDK/src/cpp/text/local_glyph_rasterizer_jni.hpp new file mode 100644 index 000000000..e80e84833 --- /dev/null +++ b/MapboxGLAndroidSDK/src/cpp/text/local_glyph_rasterizer_jni.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include + +/* + android::LocalGlyphRasterizer is the JNI wrapper of + com/mapbox/mapboxsdk/text/LocalGlyphRasterizer + + mbgl::LocalGlyphRasterizer is the portable interface + Both implementations are in local_glyph_rasterizer.cpp + */ + +namespace mbgl { +namespace android { + +class LocalGlyphRasterizer { +public: + static constexpr auto Name() { return "com/mapbox/mapboxsdk/text/LocalGlyphRasterizer"; }; + + static void registerNative(jni::JNIEnv&); + + LocalGlyphRasterizer(); + + PremultipliedImage drawGlyphBitmap(const std::string& fontFamily, const bool bold, const char16_t glyphID); + +private: + jni::Global> javaObject; +}; + +} // namespace android +} // namespace mbgl diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java index bc70cb70a..70485a726 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java @@ -117,6 +117,14 @@ public abstract class Source { @Keep protected native void nativeSetPrefetchZoomDelta(Integer delta); + @NonNull + @Keep + protected native void nativeSetMaxOverscaleFactorForParentTiles(Integer maxOverscaleFactor); + + @NonNull + @Keep + protected native Integer nativeGetMaxOverscaleFactorForParentTiles(); + public void setDetached() { detached = true; } diff --git a/gradle/native-build.gradle b/gradle/native-build.gradle index 31813cbe2..8aa760892 100644 --- a/gradle/native-build.gradle +++ b/gradle/native-build.gradle @@ -21,7 +21,7 @@ ext.nativeBuild = { nativeTargets -> if (abi != 'none') { externalNativeBuild { cmake { - path "../vendor/mapbox-gl-native/CMakeLists.txt" + path "../MapboxGLAndroidSDK/src/cpp/CMakeLists.txt" version "3.10.2" } } diff --git a/vendor/mapbox-gl-native b/vendor/mapbox-gl-native index 77466d23d..338cfd1b3 160000 --- a/vendor/mapbox-gl-native +++ b/vendor/mapbox-gl-native @@ -1 +1 @@ -Subproject commit 77466d23d6b2f524c104373d546ac458035b0ec0 +Subproject commit 338cfd1b33dfe26b9402d79cc64609b944a9f1d1