From d7b0a34bddd9914253bcc496f9d62106743a682a Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sat, 1 Nov 2014 22:41:40 -0400 Subject: [PATCH 001/415] mason-based android port --- Makefile | 17 ++++++++++++++++- configure | 6 ++++++ gyp/common.gypi | 1 + include/mbgl/geometry/vao.hpp | 21 +++++++++++++++------ include/mbgl/platform/gl.hpp | 3 +++ src/renderer/painter_prerender.cpp | 2 +- src/renderer/prerendered_texture.cpp | 10 +++++++--- src/storage/response.cpp | 2 +- 8 files changed, 50 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 73e4c8799..5ea12ff3d 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,21 @@ build/ios/mapbox-gl-cocoa/app/mapboxgl-app.xcodeproj: ios/mapbox-gl-cocoa/app/ma build/linux/mapboxgl-app.xcodeproj: linux/mapboxgl-app.gyp config.gypi deps/run_gyp linux/mapboxgl-app.gyp -Iconfig.gypi -Dplatform=linux --depth=. --generator-output=./build -f xcode +.PHONY: android +android: + export CXX=`MASON_PLATFORM=android ~/.mason/mason env CXX` && \ + export CC=`MASON_PLATFORM=android ~/.mason/mason env CC` && \ + export LD=`MASON_PLATFORM=android ~/.mason/mason env LD` && \ + export AR=`MASON_PLATFORM=android ~/.mason/mason env AR` && \ + export RANLIB=`MASON_PLATFORM=android ~/.mason/mason env RANLIB` && \ + export LDFLAGS=`MASON_PLATFORM=android ~/.mason/mason env LDFLAGS` && \ + export CFLAGS=`MASON_PLATFORM=android ~/.mason/mason env CFLAGS` && \ + export CPPFLAGS=`MASON_PLATFORM=android ~/.mason/mason env CPPFLAGS` && \ + export PATH=`MASON_PLATFORM=android ~/.mason/mason env PATH` && \ + MASON_PLATFORM=android ./configure config-android.gypi && \ + deps/run_gyp mapboxgl.gyp -Iconfig-android.gypi -Dplatform=android --depth=. --generator-output=./build/android -f make-android && \ + $(MAKE) -C ./build/android BUILDTYPE=$(BUILDTYPE) V=$(V) mbgl-core + ##### Test cases ############################################################### test: build/test/Makefile @@ -131,7 +146,7 @@ clean: clear_xcode_cache -find ./deps/gyp -name "*.pyc" -exec rm {} \; -rm -rf ./build/ -rm -rf ./macosx/build/ - -rm -rf ./config.gypi ./config-ios.gypi + -rm -rf ./config.gypi ./config-ios.gypi ./config-android.gypi distclean: clean -rm -rf ./mason_packages diff --git a/configure b/configure index 2b5ec80ca..0113ee294 100755 --- a/configure +++ b/configure @@ -24,6 +24,12 @@ case ${MASON_PLATFORM} in ZLIB_VERSION=system BOOST_VERSION=system ;; + 'android') + SQLITE_VERSION=3.8.6 + LIBUV_VERSION=0.11.29 + ZLIB_VERSION=system + BOOST_VERSION=system + ;; *) GLFW_VERSION=a21f2377 SQLITE_VERSION=system diff --git a/gyp/common.gypi b/gyp/common.gypi index b21e8b50d..97b474b4b 100644 --- a/gyp/common.gypi +++ b/gyp/common.gypi @@ -23,6 +23,7 @@ 'cflags_cc': [ '-Wno-unknown-pragmas', # We are using '#pragma mark', but it is only available on Darwin. '-Wno-literal-suffix', # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61653 + '-Wno-unknown-warning-option' ], }], ], diff --git a/include/mbgl/geometry/vao.hpp b/include/mbgl/geometry/vao.hpp index bb1f571ff..e6e199b69 100644 --- a/include/mbgl/geometry/vao.hpp +++ b/include/mbgl/geometry/vao.hpp @@ -69,11 +69,20 @@ private: class VertexArrayObject : public util::noncopyable { public: - template - void bind(Shader& shader, Buffers& buffers, char *offset) { - for (auto &buffer : buffers) { - buffer.bind(); - } + inline VertexArrayObject() {}; + + inline VertexArrayObject(VertexArrayObject &&) noexcept {}; + + template + inline void bind(Shader& shader, VertexBuffer &vertexBuffer, char *offset) { + vertexBuffer.bind(); + shader.bind(offset); + } + + template + inline void bind(Shader& shader, VertexBuffer &vertexBuffer, ElementsBuffer &elementsBuffer, char *offset) { + vertexBuffer.bind(); + elementsBuffer.bind(); shader.bind(offset); } }; @@ -82,4 +91,4 @@ public: } -#endif +#endif \ No newline at end of file diff --git a/include/mbgl/platform/gl.hpp b/include/mbgl/platform/gl.hpp index d5162f2c7..d07b0561d 100644 --- a/include/mbgl/platform/gl.hpp +++ b/include/mbgl/platform/gl.hpp @@ -35,6 +35,9 @@ #else #error Unsupported Apple platform #endif +#elif __ANDROID__ + #include + #include #else #define GL_GLEXT_PROTOTYPES #include diff --git a/src/renderer/painter_prerender.cpp b/src/renderer/painter_prerender.cpp index f38470530..22a2be9b6 100644 --- a/src/renderer/painter_prerender.cpp +++ b/src/renderer/painter_prerender.cpp @@ -10,7 +10,7 @@ void Painter::preparePrerender(RasterBucket &bucket) { glDisable(GL_STENCIL_TEST); // Render the actual tile. -#if GL_EXT_discard_framebuffer +#if GL_EXT_discard_framebuffer && !__ANDROID__ const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); #endif diff --git a/src/renderer/prerendered_texture.cpp b/src/renderer/prerendered_texture.cpp index adb7a5910..2514efaf2 100644 --- a/src/renderer/prerendered_texture.cpp +++ b/src/renderer/prerendered_texture.cpp @@ -49,8 +49,12 @@ void PrerenderedTexture::bindFramebuffer() { // Create depth/stencil buffer glGenRenderbuffers(1, &fbo_depth_stencil); glBindRenderbuffer(GL_RENDERBUFFER, fbo_depth_stencil); +#ifdef GL_ES_VERSION_2_0 + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, properties.size, properties.size); +#else glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, properties.size, properties.size); - glBindRenderbuffer(GL_RENDERBUFFER, 0); +#endif + glBindRenderbuffer(GL_RENDERBUFFER, 0); } if (fbo == 0) { @@ -117,7 +121,7 @@ void PrerenderedTexture::blur(Painter& painter, uint16_t passes) { for (int i = 0; i < passes; i++) { // Render horizontal glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, secondary_texture, 0); -#if GL_EXT_discard_framebuffer +#if GL_EXT_discard_framebuffer && !__ANDROID__ const GLenum discards[] = { GL_COLOR_ATTACHMENT0 }; glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); #endif @@ -132,7 +136,7 @@ void PrerenderedTexture::blur(Painter& painter, uint16_t passes) { // Render vertical glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, original_texture, 0); -#if GL_EXT_discard_framebuffer +#if GL_EXT_discard_framebuffer && !__ANDROID__ glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); #endif glClear(GL_COLOR_BUFFER_BIT); diff --git a/src/storage/response.cpp b/src/storage/response.cpp index f1e1c3587..cdaf33e4e 100644 --- a/src/storage/response.cpp +++ b/src/storage/response.cpp @@ -9,7 +9,7 @@ int64_t Response::parseCacheControl(const char *value) { uint64_t seconds = 0; // TODO: cache-control may contain other information as well: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 - if (std::sscanf(value, "max-age=%lu", &seconds) == 1) { + if (std::sscanf(value, "max-age=%llu", &seconds) == 1) { return std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count() + seconds; From 766343e304b9dd8e2f27a37d4233d821efe76f23 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sat, 1 Nov 2014 22:49:57 -0400 Subject: [PATCH 002/415] update mason --- .mason | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mason b/.mason index 462fe2847..a2b2daee1 160000 --- a/.mason +++ b/.mason @@ -1 +1 @@ -Subproject commit 462fe28479ea700943abd2fde15b03038a168da0 +Subproject commit a2b2daee1a6d63dec250a585936d49dcaaba1dd1 From 969ef9a663b3502c8f6ffb8063d6f52cf19b8f53 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sat, 1 Nov 2014 22:52:33 -0400 Subject: [PATCH 003/415] test android target on travis/linux --- .travis.yml | 3 ++- scripts/travis_script.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a7c7109f0..905bc4a66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ matrix: - os: linux include: - os: linux - env: BUILDTYPE=Release JOBS=16 + env: BUILDTYPE=Release JOBS=16 MASON_PLATFORM=android compiler: clang - os: linux env: BUILDTYPE=Debug JOBS=16 @@ -25,6 +25,7 @@ env: - LD_LIBRARY_PATH: '/usr/local/lib' before_install: +- if [ "${MASON_PLATFORM}" = "android" ]; then MASON_PLATFORM= ~/.mason/mason install 7z 9.20; export PATH=$(MASON_PLATFORM= ~/.mason/mason prefix 7z 9.20)/bin:${PATH}; wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin; chmod a+x ./android-ndk-r10c-linux-x86_64.bin; 7za x ./android-ndk-r10c-linux-x86_64.bin > /dev/null; export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c; ls -l;ls -l $ANDROID_NDK_PATH; fi; - source ./scripts/local_mason.sh - source ./scripts/travis_helper.sh - source ./scripts/flags.sh diff --git a/scripts/travis_script.sh b/scripts/travis_script.sh index ea6097dc5..f3da79842 100755 --- a/scripts/travis_script.sh +++ b/scripts/travis_script.sh @@ -11,7 +11,7 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then # build & test Linux # mapbox_time "compile_program" \ - make linux -j$JOBS BUILDTYPE=${BUILDTYPE} + make android -j$JOBS BUILDTYPE=${BUILDTYPE} mapbox_time "compile_tests" \ make test -j$JOBS BUILDTYPE=${BUILDTYPE} From fbd71f69291383452ce667b15314ac913eb9150b Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sat, 1 Nov 2014 22:55:18 -0400 Subject: [PATCH 004/415] fix path to mason --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 905bc4a66..6681932aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,8 @@ env: - LD_LIBRARY_PATH: '/usr/local/lib' before_install: -- if [ "${MASON_PLATFORM}" = "android" ]; then MASON_PLATFORM= ~/.mason/mason install 7z 9.20; export PATH=$(MASON_PLATFORM= ~/.mason/mason prefix 7z 9.20)/bin:${PATH}; wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin; chmod a+x ./android-ndk-r10c-linux-x86_64.bin; 7za x ./android-ndk-r10c-linux-x86_64.bin > /dev/null; export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c; ls -l;ls -l $ANDROID_NDK_PATH; fi; - source ./scripts/local_mason.sh +- if [ "${MASON_PLATFORM}" = "android" ]; then MASON_PLATFORM= ./.mason/mason install 7z 9.20; export PATH=$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin:${PATH}; wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin; chmod a+x ./android-ndk-r10c-linux-x86_64.bin; 7za x ./android-ndk-r10c-linux-x86_64.bin > /dev/null; export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c; ls -l;ls -l $ANDROID_NDK_PATH; fi; - source ./scripts/travis_helper.sh - source ./scripts/flags.sh - ./scripts/travis_before_install.sh From cdcc30fa4adf7ffb393e3cd885f8c9ed4281fdc8 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sat, 1 Nov 2014 22:57:14 -0400 Subject: [PATCH 005/415] fix another path to mason --- Makefile | 18 +++++++++--------- scripts/travis_script.sh | 6 ++++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 5ea12ff3d..e611b4072 100644 --- a/Makefile +++ b/Makefile @@ -71,15 +71,15 @@ build/linux/mapboxgl-app.xcodeproj: linux/mapboxgl-app.gyp config.gypi .PHONY: android android: - export CXX=`MASON_PLATFORM=android ~/.mason/mason env CXX` && \ - export CC=`MASON_PLATFORM=android ~/.mason/mason env CC` && \ - export LD=`MASON_PLATFORM=android ~/.mason/mason env LD` && \ - export AR=`MASON_PLATFORM=android ~/.mason/mason env AR` && \ - export RANLIB=`MASON_PLATFORM=android ~/.mason/mason env RANLIB` && \ - export LDFLAGS=`MASON_PLATFORM=android ~/.mason/mason env LDFLAGS` && \ - export CFLAGS=`MASON_PLATFORM=android ~/.mason/mason env CFLAGS` && \ - export CPPFLAGS=`MASON_PLATFORM=android ~/.mason/mason env CPPFLAGS` && \ - export PATH=`MASON_PLATFORM=android ~/.mason/mason env PATH` && \ + export CXX=`MASON_PLATFORM=android ./.mason/mason env CXX` && \ + export CC=`MASON_PLATFORM=android ./.mason/mason env CC` && \ + export LD=`MASON_PLATFORM=android ./.mason/mason env LD` && \ + export AR=`MASON_PLATFORM=android ./.mason/mason env AR` && \ + export RANLIB=`MASON_PLATFORM=android ./.mason/mason env RANLIB` && \ + export LDFLAGS=`MASON_PLATFORM=android ./.mason/mason env LDFLAGS` && \ + export CFLAGS=`MASON_PLATFORM=android ./.mason/mason env CFLAGS` && \ + export CPPFLAGS=`MASON_PLATFORM=android ./.mason/mason env CPPFLAGS` && \ + export PATH=`MASON_PLATFORM=android ./.mason/mason env PATH` && \ MASON_PLATFORM=android ./configure config-android.gypi && \ deps/run_gyp mapboxgl.gyp -Iconfig-android.gypi -Dplatform=android --depth=. --generator-output=./build/android -f make-android && \ $(MAKE) -C ./build/android BUILDTYPE=$(BUILDTYPE) V=$(V) mbgl-core diff --git a/scripts/travis_script.sh b/scripts/travis_script.sh index f3da79842..562d6fad2 100755 --- a/scripts/travis_script.sh +++ b/scripts/travis_script.sh @@ -10,8 +10,10 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then # # build & test Linux # - mapbox_time "compile_program" \ - make android -j$JOBS BUILDTYPE=${BUILDTYPE} + if [[ $MASON_PLATFORM == "android" ]]; then + mapbox_time "compile_program" \ + make android -j$JOBS BUILDTYPE=${BUILDTYPE} + fi mapbox_time "compile_tests" \ make test -j$JOBS BUILDTYPE=${BUILDTYPE} From 68556362b31f934ccef6026acfe6e20284fe39c1 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 11:52:29 -0500 Subject: [PATCH 006/415] avoid -Wformat warning --- src/storage/response.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage/response.cpp b/src/storage/response.cpp index cdaf33e4e..d00d6d335 100644 --- a/src/storage/response.cpp +++ b/src/storage/response.cpp @@ -9,7 +9,7 @@ int64_t Response::parseCacheControl(const char *value) { uint64_t seconds = 0; // TODO: cache-control may contain other information as well: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 - if (std::sscanf(value, "max-age=%llu", &seconds) == 1) { + if (std::sscanf(value, "max-age=%llu", static_cast(&seconds)) == 1) { return std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count() + seconds; From 72f1581b77742c3c8cb5d07c4c83b129b25e4ab0 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 11:52:51 -0500 Subject: [PATCH 007/415] install mesa for the HOST platform --- scripts/travis_before_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 2af2f7ae0..f93bb15c5 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -33,7 +33,7 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then mapbox_time "install_mesa" \ - mason install mesa 10.3.1 + MASON_PLATFORM= mason install mesa 10.3.1 mapbox_time "install_awscli" \ sudo pip install awscli From c686cc4613b6287110e5e32d6e86dc7b2a5aadec Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 12:00:18 -0500 Subject: [PATCH 008/415] another try at ensuring mesa installs only for HOST arch --- scripts/travis_before_install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index f93bb15c5..0a01cbcd9 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -32,8 +32,9 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then llvm-3.4 # required for mesa + unset MASON_PLATFORM mapbox_time "install_mesa" \ - MASON_PLATFORM= mason install mesa 10.3.1 + mason install mesa 10.3.1 mapbox_time "install_awscli" \ sudo pip install awscli From 3e38fcb7b98c3a4ab7f0674ab707409b3aa30688 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 14:58:11 -0500 Subject: [PATCH 009/415] test without setting up NDK --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6681932aa..5f44a01a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ env: before_install: - source ./scripts/local_mason.sh -- if [ "${MASON_PLATFORM}" = "android" ]; then MASON_PLATFORM= ./.mason/mason install 7z 9.20; export PATH=$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin:${PATH}; wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin; chmod a+x ./android-ndk-r10c-linux-x86_64.bin; 7za x ./android-ndk-r10c-linux-x86_64.bin > /dev/null; export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c; ls -l;ls -l $ANDROID_NDK_PATH; fi; +#- if [ "${MASON_PLATFORM}" = "android" ]; then MASON_PLATFORM= ./.mason/mason install 7z 9.20; export PATH=$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin:${PATH}; wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin; chmod a+x ./android-ndk-r10c-linux-x86_64.bin; 7za x ./android-ndk-r10c-linux-x86_64.bin > /dev/null; export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c; ls -l;ls -l $ANDROID_NDK_PATH; fi; - source ./scripts/travis_helper.sh - source ./scripts/flags.sh - ./scripts/travis_before_install.sh From 7a45bfa09c0899367625404b2309d670fb333c51 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 15:14:50 -0500 Subject: [PATCH 010/415] move NDK setup to travis_before_install.sh --- .travis.yml | 1 - scripts/travis_before_install.sh | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5f44a01a0..f2860cdb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,6 @@ env: before_install: - source ./scripts/local_mason.sh -#- if [ "${MASON_PLATFORM}" = "android" ]; then MASON_PLATFORM= ./.mason/mason install 7z 9.20; export PATH=$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin:${PATH}; wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin; chmod a+x ./android-ndk-r10c-linux-x86_64.bin; 7za x ./android-ndk-r10c-linux-x86_64.bin > /dev/null; export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c; ls -l;ls -l $ANDROID_NDK_PATH; fi; - source ./scripts/travis_helper.sh - source ./scripts/flags.sh - ./scripts/travis_before_install.sh diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 0a01cbcd9..1d21c1edf 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -3,6 +3,17 @@ set -e set -o pipefail +if [[ "${MASON_PLATFORM}" == "android" ]]; then + MASON_PLATFORM= ./.mason/mason install 7z 9.20 + 7Z_PATH=$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin/7za + mapbox_time "fetching NDK" \ + wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin + chmod a+x ./android-ndk-r10c-linux-x86_64.bin + mapbox_time "unpacking NDK" \ + 7Z_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null; + export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c; +fi + if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then # # install Linux dependencies @@ -32,9 +43,9 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then llvm-3.4 # required for mesa - unset MASON_PLATFORM - mapbox_time "install_mesa" \ - mason install mesa 10.3.1 + # TODO - figure out how to unset MASON_PLATFORM for just this command + #mapbox_time "install_mesa" \ + MASON_PLATFORM= mason install mesa 10.3.1 mapbox_time "install_awscli" \ sudo pip install awscli From ec006819f950a59aeb8fefd10e8dfbd28ad71381 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 15:25:51 -0500 Subject: [PATCH 011/415] bash variables cannot start with numeral --- .travis.yml | 2 +- scripts/travis_before_install.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f2860cdb3..3c4d76652 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ before_install: - source ./scripts/local_mason.sh - source ./scripts/travis_helper.sh - source ./scripts/flags.sh -- ./scripts/travis_before_install.sh +- source ./scripts/travis_before_install.sh - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then export LD_LIBRARY_PATH=`mason prefix mesa 10.3.1`/lib; fi - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then glxinfo; fi diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 1d21c1edf..5aaed855c 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -5,12 +5,12 @@ set -o pipefail if [[ "${MASON_PLATFORM}" == "android" ]]; then MASON_PLATFORM= ./.mason/mason install 7z 9.20 - 7Z_PATH=$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin/7za + SEVEN_ZIP_PATH="$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin/7za" mapbox_time "fetching NDK" \ wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin chmod a+x ./android-ndk-r10c-linux-x86_64.bin mapbox_time "unpacking NDK" \ - 7Z_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null; + SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null; export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c; fi From 2d809127b7de072864afce0f502821b0453b2aae Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 15:43:57 -0500 Subject: [PATCH 012/415] bash subshell to try to get mapbox-time working with custom commands --- scripts/travis_before_install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 5aaed855c..65be1c29d 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -4,13 +4,14 @@ set -e set -o pipefail if [[ "${MASON_PLATFORM}" == "android" ]]; then - MASON_PLATFORM= ./.mason/mason install 7z 9.20 + mapbox_time "unpacking NDK" \ + $(MASON_PLATFORM= ./.mason/mason install 7z 9.20) SEVEN_ZIP_PATH="$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin/7za" mapbox_time "fetching NDK" \ wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin chmod a+x ./android-ndk-r10c-linux-x86_64.bin mapbox_time "unpacking NDK" \ - SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null; + $(SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null); export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c; fi From 203a42d83a5fdb95774a4c280552ac8cb8256c1f Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 15:44:33 -0500 Subject: [PATCH 013/415] patch travis_fold and travis_nanoseconds to make it possible to test travis_helper.sh off travis --- scripts/travis_helper.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/scripts/travis_helper.sh b/scripts/travis_helper.sh index 040ef2944..c42ce215e 100755 --- a/scripts/travis_helper.sh +++ b/scripts/travis_helper.sh @@ -32,6 +32,26 @@ function mapbox_time { mapbox_time_finish $name $timer_id } +if [[ "${TRAVIS_COMMIT:-false}" == false ]]; then +function travis_fold { + local action=$1 + local name=$2 + echo -en "travis_fold:${action}:${name}\r${ANSI_CLEAR}" +} +function travis_nanoseconds { + local cmd="date" + local format="+%s%N" + local os=$(uname) + + if hash gdate > /dev/null 2>&1; then + cmd="gdate" # use gdate if available + elif [[ "$os" = Darwin ]]; then + format="+%s000000000" # fallback to second precision on darwin (does not support %N) + fi + + $cmd -u $format +} +fi export ANSI_CLEAR export -f travis_fold From d5dfd6e6d5712a324334a669fd14c236d4161581 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 16:00:09 -0500 Subject: [PATCH 014/415] debug failing travis script --- scripts/travis_before_install.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 65be1c29d..6e598570a 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -4,15 +4,28 @@ set -e set -o pipefail if [[ "${MASON_PLATFORM}" == "android" ]]; then + echo "debug: unpacking" mapbox_time "unpacking NDK" \ $(MASON_PLATFORM= ./.mason/mason install 7z 9.20) + + echo "debug: setting 7z path" SEVEN_ZIP_PATH="$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin/7za" + + echo "debug: fetching NDK" mapbox_time "fetching NDK" \ wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin + + echo "debug: chmod NDK" chmod a+x ./android-ndk-r10c-linux-x86_64.bin + + echo "debug: upacking NDK" mapbox_time "unpacking NDK" \ - $(SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null); - export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c; + $(SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null) + + echo "debug: setting ANDROID_NDK_PATH" + export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c + + echo "debug: $ANDROID_NDK_PATH" fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From 0afa58cbde85463c2c4099048119d86f3fd91085 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 16:11:09 -0500 Subject: [PATCH 015/415] give up on mapbox-time --- scripts/travis_before_install.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 6e598570a..bc9e3a2e3 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -5,22 +5,22 @@ set -o pipefail if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: unpacking" - mapbox_time "unpacking NDK" \ - $(MASON_PLATFORM= ./.mason/mason install 7z 9.20) + #mapbox_time "unpacking NDK" \ + MASON_PLATFORM= ./.mason/mason install 7z 9.20 echo "debug: setting 7z path" SEVEN_ZIP_PATH="$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin/7za" echo "debug: fetching NDK" - mapbox_time "fetching NDK" \ - wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin + #mapbox_time "fetching NDK" \ + wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin echo "debug: chmod NDK" chmod a+x ./android-ndk-r10c-linux-x86_64.bin echo "debug: upacking NDK" - mapbox_time "unpacking NDK" \ - $(SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null) + #mapbox_time "unpacking NDK" \ + SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null echo "debug: setting ANDROID_NDK_PATH" export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c From 80b13ff361d5e4ea18e7abc5f5178452d6d1d217 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 16:27:24 -0500 Subject: [PATCH 016/415] fix SEVEN_ZIP_PATH variable --- scripts/travis_before_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index bc9e3a2e3..2a94cde1d 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -20,7 +20,7 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: upacking NDK" #mapbox_time "unpacking NDK" \ - SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null + $SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null echo "debug: setting ANDROID_NDK_PATH" export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c From 7ea305d02177e9fcba351012440f6453974b42ff Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 19:37:48 -0500 Subject: [PATCH 017/415] reinterpret_cast for parseCacheControl to avoid -Wformat warning on all platforms --- src/storage/response.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/storage/response.cpp b/src/storage/response.cpp index d00d6d335..e3cc66e60 100644 --- a/src/storage/response.cpp +++ b/src/storage/response.cpp @@ -9,7 +9,7 @@ int64_t Response::parseCacheControl(const char *value) { uint64_t seconds = 0; // TODO: cache-control may contain other information as well: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 - if (std::sscanf(value, "max-age=%llu", static_cast(&seconds)) == 1) { + if (std::sscanf(value, "max-age=%llu", reinterpret_cast(&seconds)) == 1) { return std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()).count() + seconds; From bb80fe90fc6f7562bc334486158784fec322aab9 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 19:42:58 -0500 Subject: [PATCH 018/415] travis: wtf is breaking apt-get update: try removing previous steps --- scripts/travis_before_install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 2a94cde1d..8a7c44b20 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -20,10 +20,10 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: upacking NDK" #mapbox_time "unpacking NDK" \ - $SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null + #$SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null echo "debug: setting ANDROID_NDK_PATH" - export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c + #export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c echo "debug: $ANDROID_NDK_PATH" fi From fa2559bb2058f26f0fd2a40a45080bf55b3712a4 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 19:50:05 -0500 Subject: [PATCH 019/415] Add back 7z extract --- scripts/travis_before_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 8a7c44b20..1e9d581ed 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -20,7 +20,7 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: upacking NDK" #mapbox_time "unpacking NDK" \ - #$SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null + $SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null echo "debug: setting ANDROID_NDK_PATH" #export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c From 40c0959ec2822998fa4498ff6e6417dbd2906819 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 20:06:09 -0500 Subject: [PATCH 020/415] shuffle ANDROID_NDK_PATH setting (somehow it breaks apt-get update) --- .travis.yml | 3 ++- scripts/travis_before_install.sh | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3c4d76652..952405ea0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,8 @@ before_install: - source ./scripts/local_mason.sh - source ./scripts/travis_helper.sh - source ./scripts/flags.sh -- source ./scripts/travis_before_install.sh +- export ANDROID_NDK_PATH="$(pwd)/android-ndk-r10c" +- ./scripts/travis_before_install.sh - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then export LD_LIBRARY_PATH=`mason prefix mesa 10.3.1`/lib; fi - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then glxinfo; fi diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 1e9d581ed..be8125221 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -21,11 +21,6 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: upacking NDK" #mapbox_time "unpacking NDK" \ $SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null - - echo "debug: setting ANDROID_NDK_PATH" - #export ANDROID_NDK_PATH=$(pwd)/android-ndk-r10c - - echo "debug: $ANDROID_NDK_PATH" fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From 86944f3b46b0d6e8c82b59cd3b01980826498c86 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 20:34:35 -0500 Subject: [PATCH 021/415] quote android env variables --- Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index e611b4072..ba1fc7875 100644 --- a/Makefile +++ b/Makefile @@ -71,15 +71,15 @@ build/linux/mapboxgl-app.xcodeproj: linux/mapboxgl-app.gyp config.gypi .PHONY: android android: - export CXX=`MASON_PLATFORM=android ./.mason/mason env CXX` && \ - export CC=`MASON_PLATFORM=android ./.mason/mason env CC` && \ - export LD=`MASON_PLATFORM=android ./.mason/mason env LD` && \ - export AR=`MASON_PLATFORM=android ./.mason/mason env AR` && \ - export RANLIB=`MASON_PLATFORM=android ./.mason/mason env RANLIB` && \ - export LDFLAGS=`MASON_PLATFORM=android ./.mason/mason env LDFLAGS` && \ - export CFLAGS=`MASON_PLATFORM=android ./.mason/mason env CFLAGS` && \ - export CPPFLAGS=`MASON_PLATFORM=android ./.mason/mason env CPPFLAGS` && \ - export PATH=`MASON_PLATFORM=android ./.mason/mason env PATH` && \ + export CXX="`MASON_PLATFORM=android ./.mason/mason env CXX`" && \ + export CC="`MASON_PLATFORM=android ./.mason/mason env CC`" && \ + export LD="`MASON_PLATFORM=android ./.mason/mason env LD`" && \ + export AR="`MASON_PLATFORM=android ./.mason/mason env AR`" && \ + export RANLIB="`MASON_PLATFORM=android ./.mason/mason env RANLIB`" && \ + export LDFLAGS="`MASON_PLATFORM=android ./.mason/mason env LDFLAGS` ${LDFLAGS}" && \ + export CFLAGS="`MASON_PLATFORM=android ./.mason/mason env CFLAGS` ${CFLAGS}" && \ + export CPPFLAGS="`MASON_PLATFORM=android ./.mason/mason env CPPFLAGS` ${CPPFLAGS}" && \ + export PATH="`MASON_PLATFORM=android ./.mason/mason env PATH`:${PATH}" && \ MASON_PLATFORM=android ./configure config-android.gypi && \ deps/run_gyp mapboxgl.gyp -Iconfig-android.gypi -Dplatform=android --depth=. --generator-output=./build/android -f make-android && \ $(MAKE) -C ./build/android BUILDTYPE=$(BUILDTYPE) V=$(V) mbgl-core From 7b9a0965684a87c5745a709d0c1f1e400119166f Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 20:50:59 -0500 Subject: [PATCH 022/415] try unpackging to temp file --- scripts/travis_before_install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index be8125221..e74c258df 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -20,7 +20,8 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: upacking NDK" #mapbox_time "unpacking NDK" \ - $SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > /dev/null + $SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > .tmp-ndk-log + rm .tmp-ndk-log fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From 8da80c2b5ece59533a762e17424e801257d5e45a Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 21:04:41 -0500 Subject: [PATCH 023/415] continue past failed apt-get update --- scripts/travis_before_install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index e74c258df..20c0709a1 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -33,8 +33,8 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then fi sudo add-apt-repository --yes ppa:boost-latest/ppa - mapbox_time "apt_update" \ - sudo apt-get update -y + #mapbox_time "apt_update" \ + sudo apt-get update -y || true mapbox_time "install_gcc" \ sudo apt-get -y install gcc-4.8 g++-4.8 From 6eafca787453915ef6b9e897fd2673f81047ad84 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 21:09:44 -0500 Subject: [PATCH 024/415] disable more of mapbox_time --- scripts/travis_before_install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 20c0709a1..2e0e77b84 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -34,18 +34,18 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then sudo add-apt-repository --yes ppa:boost-latest/ppa #mapbox_time "apt_update" \ - sudo apt-get update -y || true + sudo apt-get update -y - mapbox_time "install_gcc" \ + #mapbox_time "install_gcc" \ sudo apt-get -y install gcc-4.8 g++-4.8 - mapbox_time "install_build_tools" \ + #mapbox_time "install_build_tools" \ sudo apt-get -y install git build-essential zlib1g-dev automake gdb \ libtool xutils-dev make cmake pkg-config python-pip \ libboost1.55-dev libcurl4-openssl-dev \ libpng-dev libsqlite3-dev - mapbox_time "install_opengl" \ + #mapbox_time "install_opengl" \ sudo apt-get -y install mesa-utils libxi-dev x11proto-randr-dev \ x11proto-xext-dev libxrandr-dev \ x11proto-xf86vidmode-dev libxxf86vm-dev \ @@ -57,7 +57,7 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then #mapbox_time "install_mesa" \ MASON_PLATFORM= mason install mesa 10.3.1 - mapbox_time "install_awscli" \ + #mapbox_time "install_awscli" \ sudo pip install awscli elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then # From 490a4bb17f96777b7514e296d215be4de674344e Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sun, 2 Nov 2014 22:55:23 -0500 Subject: [PATCH 025/415] centralize android ifdefs --- include/mbgl/platform/gl.hpp | 2 ++ src/renderer/painter_prerender.cpp | 2 +- src/renderer/prerendered_texture.cpp | 10 +++------- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/mbgl/platform/gl.hpp b/include/mbgl/platform/gl.hpp index d07b0561d..5706fa0ab 100644 --- a/include/mbgl/platform/gl.hpp +++ b/include/mbgl/platform/gl.hpp @@ -36,8 +36,10 @@ #error Unsupported Apple platform #endif #elif __ANDROID__ + #define GL_GLEXT_PROTOTYPES #include #include + #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES #else #define GL_GLEXT_PROTOTYPES #include diff --git a/src/renderer/painter_prerender.cpp b/src/renderer/painter_prerender.cpp index 22a2be9b6..f38470530 100644 --- a/src/renderer/painter_prerender.cpp +++ b/src/renderer/painter_prerender.cpp @@ -10,7 +10,7 @@ void Painter::preparePrerender(RasterBucket &bucket) { glDisable(GL_STENCIL_TEST); // Render the actual tile. -#if GL_EXT_discard_framebuffer && !__ANDROID__ +#if GL_EXT_discard_framebuffer const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); #endif diff --git a/src/renderer/prerendered_texture.cpp b/src/renderer/prerendered_texture.cpp index 2514efaf2..adb7a5910 100644 --- a/src/renderer/prerendered_texture.cpp +++ b/src/renderer/prerendered_texture.cpp @@ -49,12 +49,8 @@ void PrerenderedTexture::bindFramebuffer() { // Create depth/stencil buffer glGenRenderbuffers(1, &fbo_depth_stencil); glBindRenderbuffer(GL_RENDERBUFFER, fbo_depth_stencil); -#ifdef GL_ES_VERSION_2_0 - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, properties.size, properties.size); -#else glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, properties.size, properties.size); -#endif - glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindRenderbuffer(GL_RENDERBUFFER, 0); } if (fbo == 0) { @@ -121,7 +117,7 @@ void PrerenderedTexture::blur(Painter& painter, uint16_t passes) { for (int i = 0; i < passes; i++) { // Render horizontal glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, secondary_texture, 0); -#if GL_EXT_discard_framebuffer && !__ANDROID__ +#if GL_EXT_discard_framebuffer const GLenum discards[] = { GL_COLOR_ATTACHMENT0 }; glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); #endif @@ -136,7 +132,7 @@ void PrerenderedTexture::blur(Painter& painter, uint16_t passes) { // Render vertical glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, original_texture, 0); -#if GL_EXT_discard_framebuffer && !__ANDROID__ +#if GL_EXT_discard_framebuffer glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); #endif glClear(GL_COLOR_BUFFER_BIT); From 574c0f5fa009239a3474e3dc19a1835ac41bee8b Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 4 Nov 2014 22:36:04 +1100 Subject: [PATCH 026/415] Fix file not found errors --- Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index ba1fc7875..54ee9e749 100644 --- a/Makefile +++ b/Makefile @@ -71,15 +71,15 @@ build/linux/mapboxgl-app.xcodeproj: linux/mapboxgl-app.gyp config.gypi .PHONY: android android: - export CXX="`MASON_PLATFORM=android ./.mason/mason env CXX`" && \ - export CC="`MASON_PLATFORM=android ./.mason/mason env CC`" && \ - export LD="`MASON_PLATFORM=android ./.mason/mason env LD`" && \ - export AR="`MASON_PLATFORM=android ./.mason/mason env AR`" && \ - export RANLIB="`MASON_PLATFORM=android ./.mason/mason env RANLIB`" && \ - export LDFLAGS="`MASON_PLATFORM=android ./.mason/mason env LDFLAGS` ${LDFLAGS}" && \ - export CFLAGS="`MASON_PLATFORM=android ./.mason/mason env CFLAGS` ${CFLAGS}" && \ - export CPPFLAGS="`MASON_PLATFORM=android ./.mason/mason env CPPFLAGS` ${CPPFLAGS}" && \ - export PATH="`MASON_PLATFORM=android ./.mason/mason env PATH`:${PATH}" && \ + export CXX="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ + export CC="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CC`" && \ + export LD="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env LD`" && \ + export AR="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env AR`" && \ + export RANLIB="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env RANLIB`" && \ + export LDFLAGS="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env LDFLAGS` ${LDFLAGS}" && \ + export CFLAGS="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CFLAGS` ${CFLAGS}" && \ + export CPPFLAGS="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CPPFLAGS` ${CPPFLAGS}" && \ + export PATH="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env PATH`:${PATH}" && \ MASON_PLATFORM=android ./configure config-android.gypi && \ deps/run_gyp mapboxgl.gyp -Iconfig-android.gypi -Dplatform=android --depth=. --generator-output=./build/android -f make-android && \ $(MAKE) -C ./build/android BUILDTYPE=$(BUILDTYPE) V=$(V) mbgl-core From 30235e58329f3a1c917b57be60084c8406538eb5 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 4 Nov 2014 22:36:19 +1100 Subject: [PATCH 027/415] Ignore config-android.gypi --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c907dbdec..abed3bbf5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ /mason_packages /config.gypi /config-ios.gypi +/config-android.gypi /config/constants_local.cpp /build /include/mbgl/shader/shaders.hpp From 991c497e43723ac584813986ccd69bc808fc98f7 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 4 Nov 2014 23:13:58 +1100 Subject: [PATCH 028/415] Use local boost --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index d23d53909..a0ea9eea5 100755 --- a/configure +++ b/configure @@ -28,7 +28,7 @@ case ${MASON_PLATFORM} in SQLITE_VERSION=3.8.6 LIBUV_VERSION=0.11.29 ZLIB_VERSION=system - BOOST_VERSION=system + BOOST_VERSION=1.57.0 NUNICODE_VERSION=1.4 ;; *) From 278a1407a87cf130c39fb491dcf7bb0722c56080 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 4 Nov 2014 21:42:39 -0500 Subject: [PATCH 029/415] try without set -e and hope I'm lucky - refs #535 --- scripts/travis_before_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 2e0e77b84..260ae1d27 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -e +#set -e set -o pipefail if [[ "${MASON_PLATFORM}" == "android" ]]; then From 492221f2d078133d3045b264417d85747b5cac72 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 4 Nov 2014 22:16:31 -0500 Subject: [PATCH 030/415] update mason so that boost can actually be cleared out with travis dying --- .mason | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mason b/.mason index a2b2daee1..4c53b0a3a 160000 --- a/.mason +++ b/.mason @@ -1 +1 @@ -Subproject commit a2b2daee1a6d63dec250a585936d49dcaaba1dd1 +Subproject commit 4c53b0a3a6c92987e16733d3dc2744810c6f7ba3 From b5021ea5fc7cd057bb02773bba9e395e46760214 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 4 Nov 2014 23:10:13 -0500 Subject: [PATCH 031/415] debug prints around failing apt-get install --- scripts/travis_before_install.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 260ae1d27..528f571ff 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -33,31 +33,29 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then fi sudo add-apt-repository --yes ppa:boost-latest/ppa - #mapbox_time "apt_update" \ + echo "apt_update" sudo apt-get update -y - #mapbox_time "install_gcc" \ + echo "install_gcc" sudo apt-get -y install gcc-4.8 g++-4.8 - #mapbox_time "install_build_tools" \ + echo "install_build_tools" sudo apt-get -y install git build-essential zlib1g-dev automake gdb \ libtool xutils-dev make cmake pkg-config python-pip \ libboost1.55-dev libcurl4-openssl-dev \ libpng-dev libsqlite3-dev - #mapbox_time "install_opengl" \ + echo "install_opengl" sudo apt-get -y install mesa-utils libxi-dev x11proto-randr-dev \ x11proto-xext-dev libxrandr-dev \ x11proto-xf86vidmode-dev libxxf86vm-dev \ libxcursor-dev libxinerama-dev \ llvm-3.4 # required for mesa - - # TODO - figure out how to unset MASON_PLATFORM for just this command - #mapbox_time "install_mesa" \ + echo "install_mesa" MASON_PLATFORM= mason install mesa 10.3.1 - #mapbox_time "install_awscli" \ + echo "install_awscli" sudo pip install awscli elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then # From 25483001e50d52c4fda8d77a13b4b8a28b7bde3a Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 4 Nov 2014 23:37:30 -0500 Subject: [PATCH 032/415] android: for now just build mbgl-core --- scripts/travis_script.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/travis_script.sh b/scripts/travis_script.sh index 562d6fad2..5a4e6b312 100755 --- a/scripts/travis_script.sh +++ b/scripts/travis_script.sh @@ -6,14 +6,13 @@ set -o pipefail mapbox_time "checkout_styles" \ git submodule update --init styles -if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then +if [[ $MASON_PLATFORM == "android" ]]; then + mapbox_time "compile_program" \ + make android -j$JOBS BUILDTYPE=${BUILDTYPE} +elif [[ ${TRAVIS_OS_NAME} == "linux" ]]; then # # build & test Linux # - if [[ $MASON_PLATFORM == "android" ]]; then - mapbox_time "compile_program" \ - make android -j$JOBS BUILDTYPE=${BUILDTYPE} - fi mapbox_time "compile_tests" \ make test -j$JOBS BUILDTYPE=${BUILDTYPE} From da49d0adb707383f2075fada59d17a0a45d44955 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 5 Nov 2014 20:17:28 +1100 Subject: [PATCH 033/415] Workaround for GL extension. Need to come back to this. --- src/renderer/painter_prerender.cpp | 2 +- src/renderer/prerendered_texture.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/painter_prerender.cpp b/src/renderer/painter_prerender.cpp index f38470530..22a2be9b6 100644 --- a/src/renderer/painter_prerender.cpp +++ b/src/renderer/painter_prerender.cpp @@ -10,7 +10,7 @@ void Painter::preparePrerender(RasterBucket &bucket) { glDisable(GL_STENCIL_TEST); // Render the actual tile. -#if GL_EXT_discard_framebuffer +#if GL_EXT_discard_framebuffer && !__ANDROID__ const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); #endif diff --git a/src/renderer/prerendered_texture.cpp b/src/renderer/prerendered_texture.cpp index adb7a5910..0b8b9a386 100644 --- a/src/renderer/prerendered_texture.cpp +++ b/src/renderer/prerendered_texture.cpp @@ -117,7 +117,7 @@ void PrerenderedTexture::blur(Painter& painter, uint16_t passes) { for (int i = 0; i < passes; i++) { // Render horizontal glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, secondary_texture, 0); -#if GL_EXT_discard_framebuffer +#if GL_EXT_discard_framebuffer && !__ANDROID__ const GLenum discards[] = { GL_COLOR_ATTACHMENT0 }; glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); #endif @@ -132,7 +132,7 @@ void PrerenderedTexture::blur(Painter& painter, uint16_t passes) { // Render vertical glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, original_texture, 0); -#if GL_EXT_discard_framebuffer +#if GL_EXT_discard_framebuffer && !__ANDROID__ glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); #endif glClear(GL_COLOR_BUFFER_BIT); From ecda8fb509716ba61e4e86744b02f378a1b902d5 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 5 Nov 2014 20:18:01 +1100 Subject: [PATCH 034/415] Add temporary cache dir. Need to change to Java call. --- platform/android/cache_database_data.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 platform/android/cache_database_data.cpp diff --git a/platform/android/cache_database_data.cpp b/platform/android/cache_database_data.cpp new file mode 100644 index 000000000..e1765efcd --- /dev/null +++ b/platform/android/cache_database_data.cpp @@ -0,0 +1,13 @@ +#include + +namespace mbgl { +namespace platform { + +// Returns the path to the default cache database on this system. +std::string defaultCacheDatabase() { +// TODO need to use get cache dir from java + return "/data/data/com.mapbox.mapboxgl.app/mbgl-cache.db"; +} + +} +} From 0cc43afab3295726259c6226e3239d3e3a8b90be Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 5 Nov 2014 21:11:16 +1100 Subject: [PATCH 035/415] Add missing libs to android --- configure | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/configure b/configure index a0ea9eea5..10f8508bf 100755 --- a/configure +++ b/configure @@ -26,6 +26,9 @@ case ${MASON_PLATFORM} in ;; 'android') SQLITE_VERSION=3.8.6 + LIBPNG_VERSION=1.6.13 + OPENSSL_VERSION=1.0.1i + LIBCURL_VERSION=7.38.0 LIBUV_VERSION=0.11.29 ZLIB_VERSION=system BOOST_VERSION=1.57.0 @@ -75,6 +78,13 @@ if [ ! -z ${BOOST_VERSION} ]; then CONFIG+=" 'boost_root': '$(mason prefix boost ${BOOST_VERSION})',"$LN fi +if [ ! -z ${OPENSSL_VERSION} ]; then + mason install openssl ${OPENSSL_VERSION} + CONFIG+=" 'openssl_static_libs': $(quote_flags $(mason static_libs openssl ${OPENSSL_VERSION})),"$LN + CONFIG+=" 'openssl_cflags': $(quote_flags $(mason cflags openssl ${OPENSSL_VERSION})),"$LN + CONFIG+=" 'openssl_ldflags': $(quote_flags $(mason ldflags openssl ${OPENSSL_VERSION})),"$LN +fi + if [ ! -z ${LIBCURL_VERSION} ]; then mason install libcurl ${LIBCURL_VERSION} CONFIG+=" 'curl_static_libs': $(quote_flags $(mason static_libs libcurl ${LIBCURL_VERSION})),"$LN From fcab29cab0abe0142770d817c64c0f711e7f7ae0 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 5 Nov 2014 21:11:51 +1100 Subject: [PATCH 036/415] Fix libuv ifdef bug --- platform/default/http_request_baton_curl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index 80eed2767..37b6a03a4 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -266,7 +266,11 @@ void on_timeout(uv_timer_t *) { void start_timeout(CURLM *, long timeout_ms, void *) { if (timeout_ms <= 0) { +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 on_timeout(&timeout, 0); +#else + on_timeout(&timeout); +#endif } else { uv_timer_start(&timeout, on_timeout, timeout_ms, 0); } From 9476b7a66f4864a1a10b083f6afe3bececd6fa84 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 5 Nov 2014 22:53:48 +1100 Subject: [PATCH 037/415] Add JNI code to build system --- Makefile | 4 +- android/JNI.cpp | 853 ++++++++++++++++++++++++++++++++++++++ android/NativeMapView.cpp | 528 +++++++++++++++++++++++ android/NativeMapView.hpp | 111 +++++ android/log.h | 18 + android/mapboxgl-app.gyp | 42 ++ gyp/mbgl-android.gypi | 69 +++ gyp/mbgl-platform.gypi | 4 + 8 files changed, 1627 insertions(+), 2 deletions(-) create mode 100644 android/JNI.cpp create mode 100644 android/NativeMapView.cpp create mode 100644 android/NativeMapView.hpp create mode 100644 android/log.h create mode 100644 android/mapboxgl-app.gyp create mode 100644 gyp/mbgl-android.gypi diff --git a/Makefile b/Makefile index 54ee9e749..20a1cc870 100644 --- a/Makefile +++ b/Makefile @@ -81,8 +81,8 @@ android: export CPPFLAGS="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CPPFLAGS` ${CPPFLAGS}" && \ export PATH="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env PATH`:${PATH}" && \ MASON_PLATFORM=android ./configure config-android.gypi && \ - deps/run_gyp mapboxgl.gyp -Iconfig-android.gypi -Dplatform=android --depth=. --generator-output=./build/android -f make-android && \ - $(MAKE) -C ./build/android BUILDTYPE=$(BUILDTYPE) V=$(V) mbgl-core + deps/run_gyp android/mapboxgl-app.gyp -Iconfig-android.gypi -Dplatform=android --depth=. --generator-output=./build/android -f make-android && \ + $(MAKE) -C build/android BUILDTYPE=$(BUILDTYPE) V=$(V) androidapp ##### Test cases ############################################################### diff --git a/android/JNI.cpp b/android/JNI.cpp new file mode 100644 index 000000000..2dbfedcbe --- /dev/null +++ b/android/JNI.cpp @@ -0,0 +1,853 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include "log.h" + +#include "NativeMapView.hpp" + +#pragma clang diagnostic ignored "-Wunused-parameter" + +namespace mbgl { +namespace android { + +jmethodID on_map_changed_id = nullptr; + +jclass lon_lat_class = nullptr; +jmethodID lon_lat_constructor_id = nullptr; +jfieldID lon_lat_lon_id = nullptr; +jfieldID lon_lat_lat_id = nullptr; + +jclass lon_lat_zoom_class = nullptr; +jmethodID lon_lat_zoom_constructor_id = nullptr; +jfieldID lon_lat_zoom_lon_id = nullptr; +jfieldID lon_lat_zoom_lat_id = nullptr; +jfieldID lon_lat_zoom_zoom_id = nullptr; + +jclass runtime_exception_class = nullptr; + +jmethodID set_to_array_id = nullptr; + +jclass tree_set_class = nullptr; +jmethodID tree_set_constructor_id = nullptr; +jmethodID tree_set_add_id = nullptr; + +bool throw_error(JNIEnv* env, const char* msg) { + if (env->ThrowNew(runtime_exception_class, msg) < 0) { + env->ExceptionDescribe(); + return false; + } + + return true; +} + +std::string std_string_from_jstring(JNIEnv* env, jstring jstr) { + std::string str; + jsize len = env->GetStringLength(jstr); + if (len < 0) { + env->ExceptionDescribe(); + return str; + } + const jchar* chars = env->GetStringChars(jstr, nullptr); + if (chars == nullptr) { + env->ExceptionDescribe(); + return str; + } + std::u16string ustr(reinterpret_cast(chars), len); + env->ReleaseStringChars(jstr, chars); + chars = nullptr; + str = std::wstring_convert,char16_t>().to_bytes(ustr); + return str; +} + +jstring std_string_to_jstring(JNIEnv* env, std::string str) { + std::u16string ustr = std::wstring_convert,char16_t>().from_bytes(str); + + jstring jstr = env->NewString(reinterpret_cast(ustr.c_str()), ustr.size()); + if (jstr == nullptr) { + env->ExceptionDescribe(); + return nullptr; + } + + return jstr; +} + + +// TODO: change from Java TreeSet to ArrayList +std::vector std_vector_string_from_jobject(JNIEnv* env, jobject jset) { + std::vector vector; + + jobjectArray array = reinterpret_cast(env->CallObjectMethod(jset, set_to_array_id)); + if (env->ExceptionCheck() || (array == nullptr)) { + env->ExceptionDescribe(); + return vector; + } + + jsize len = env->GetArrayLength(array); + if (len < 0) { + env->ExceptionDescribe(); + return vector; + } + + for (jsize i = 0; i < len; i++) { + jstring jstr = reinterpret_cast(env->GetObjectArrayElement(array, i)); + if (jstr == nullptr) { + env->ExceptionDescribe(); + return vector; + } + + vector.push_back(std_string_from_jstring(env, jstr)); + } + + return vector; +} + +// TODO: change from Java TreeSet to ArrayList +jobject std_vector_string_to_jobject(JNIEnv* env, std::vector vector) { + jobject jset = env->NewObject(tree_set_class, tree_set_constructor_id); + if (jset == nullptr) { + env->ExceptionDescribe(); + return nullptr; + } + + for (std::string str : vector) { + env->CallBooleanMethod(jset, tree_set_add_id, std_string_to_jstring(env, str)); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return nullptr; + } + } + + return jset; +} + +} // namespace android +} // namespace mbgl + +namespace { + +using namespace mbgl::android; + +// TODO: wrap C++ exceptions? +// TODO: wrap other sorts of exceptions? eg coffee catch + +jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring default_style_json) { + VERBOSE("nativeCreate"); + NativeMapView* native_map_view = new NativeMapView(env, obj, std_string_from_jstring(env, default_style_json)); + if (native_map_view == nullptr) { + throw_error(env, "Unable to create NativeMapView."); + return 0; + } + jlong map_view_ptr = reinterpret_cast(native_map_view); + return map_view_ptr; +} + +void JNICALL nativeDestroy(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeDestroy"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + delete native_map_view; + native_map_view = nullptr; +} + +void JNICALL nativeInitializeDisplay(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeInitializeDisplay"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + if (!native_map_view->initializeDisplay()) { + throw_error(env, "Unable to initialize GL display."); + } +} + +void JNICALL nativeTerminateDisplay(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeTerminateDisplay"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->terminateDisplay(); +} + +void JNICALL nativeInitializeContext(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeInitializeContext"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + if (!native_map_view->initializeContext()) { + throw_error(env, "Unable to initialize GL context."); + } +} + +void JNICALL nativeTerminateContext(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeTerminateContext"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->terminateContext(); +} + +void JNICALL nativeCreateSurface(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject surface) { + VERBOSE("nativeCreateSurface"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + if (!native_map_view->createSurface(ANativeWindow_fromSurface(env, surface))) { + throw_error(env, "Unable to create GL surface."); + } +} + +void JNICALL nativeDestroySurface(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeDestroySurface"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->destroySurface(); +} + +void JNICALL nativeStart(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStart"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->start(); +} + +void JNICALL nativeStop(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStop"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->stop(); +} + +void JNICALL nativeRerender(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeRerender"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->rerender(); +} + +void JNICALL nativeUpdate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeUpdate"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->update(); +} + +void JNICALL nativeCleanup(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeCleanup"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->cleanup(); +} + +// TODO remove these? +void JNICALL nativeAddDefaultSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeAddDefaultSource"); + ASSERT(native_map_view_ptr != 0); +// NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); +// native_map_view->getMap()->addDefaultSource(); +} + +void JNICALL nativeRemoveDefaultSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeRemoveDefaultSource"); + ASSERT(native_map_view_ptr != 0); +// NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); +// native_map_view->getMap()->removeDefaultSource(); +} + +void JNICALL nativeAddSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring name, jstring url) { + VERBOSE("nativeAddSource"); + ASSERT(native_map_view_ptr != 0); +// NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); +// native_map_view->getMap()->addSource(std_string_from_jstring(env, name), std_string_from_jstring(env, url)); +} + +void JNICALL nativeRemoveSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring name) { + VERBOSE("nativeRemoveSource"); + ASSERT(native_map_view_ptr != 0); +// NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); +// native_map_view->getMap()->removeSource(std_string_from_jstring(env, name)); +} + +void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio) { + VERBOSE("nativeResize"); + ASSERT(native_map_view_ptr != 0); + ASSERT(width >= 0); + ASSERT(height >= 0); + ASSERT(width <= UINT16_MAX); + ASSERT(height <= UINT16_MAX); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->resize(width, height, ratio); +} + +void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio, jint fb_width, jint fb_height) { + VERBOSE("nativeResize"); + ASSERT(native_map_view_ptr != 0); + ASSERT(width >= 0); + ASSERT(height >= 0); + ASSERT(width <= UINT16_MAX); + ASSERT(height <= UINT16_MAX); + ASSERT(fb_width >= 0); + ASSERT(fb_height >= 0); + ASSERT(fb_width <= UINT16_MAX); + ASSERT(fb_height <= UINT16_MAX); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->resize(width, height, ratio, fb_width, fb_height); +} + +void JNICALL nativeSetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject applied_classes) { + VERBOSE("nativeSetAppliedClasses"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->setAppliedClasses(std_vector_string_from_jobject(env, applied_classes)); +} + +jobject JNICALL nativeGetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetAppliedClasses"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + return std_vector_string_to_jobject(env, native_map_view->getMap()->getAppliedClasses()); +} + +void JNICALL nativeSetDefaultTransitionDuration(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jlong duration_milliseconds) { + VERBOSE("nativeSetDefaultTransitionDuration"); + ASSERT(native_map_view_ptr != 0); + ASSERT(duration_milliseconds >= 0); + //ASSERT(duration_milliseconds <= UINT64_MAX); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->setDefaultTransitionDuration(duration_milliseconds); +} + +void JNICALL nativeSetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring new_style_json) { + VERBOSE("nativeSetStyleJSON"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->setStyleJSON(std_string_from_jstring(env, new_style_json)); +} + +jstring JNICALL nativeGetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetStyleJSON"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + return std_string_to_jstring(env, native_map_view->getMap()->getStyleJSON()); +} + +void JNICALL nativeCancelTransitions(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeCancelTransitions"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->cancelTransitions(); +} + +void JNICALL nativeMoveBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble dx, jdouble dy, jdouble duration) { + VERBOSE("nativeMoveBy"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->moveBy(dx, dy, duration); +} + +void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat, jdouble duration) { + VERBOSE("nativeSetLonLat"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + + double lon = env->GetDoubleField(lon_lat, lon_lat_lon_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } + + double lat = env->GetDoubleField(lon_lat, lon_lat_lat_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } + + // TODO remove when fixed + DEBUG("lon lat %f %f", lon, lat); + native_map_view->getMap()->setLonLat(lon, lat, duration); +} + +jobject JNICALL nativeGetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetLonLat"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + double lon, lat; + native_map_view->getMap()->getLonLat(lon, lat); + + jobject ret = env->NewObject(lon_lat_class, lon_lat_constructor_id, lon, lat); + if (ret == nullptr) { + env->ExceptionDescribe(); + return nullptr; + } + + return ret; +} + +void JNICALL nativeStartPanning(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStartPanning"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->startPanning(); +} + +void JNICALL nativeStopPanning(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStopPanning"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->stopPanning(); +} + +void JNICALL nativeResetPosition(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeResetPosition"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->resetPosition(); +} + +void JNICALL nativeScaleBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble ds, jdouble cx, jdouble cy, jdouble duration) { + VERBOSE("nativeScaleBy"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->scaleBy(ds, cx, cy, duration); +} + +void JNICALL nativeSetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble scale, jdouble cx, jdouble cy, jdouble duration) { + VERBOSE("nativeSetScale"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->setScale(scale, cx, cy, duration); +} + +jdouble JNICALL nativeGetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetScale"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + return native_map_view->getMap()->getScale(); +} + +void JNICALL nativeSetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble zoom, jdouble duration) { + VERBOSE("nativeSetZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->setZoom(zoom, duration); +} + +jdouble JNICALL nativeGetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + return native_map_view->getMap()->getZoom(); +} + +void JNICALL nativeSetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat_zoom, jdouble duration) { + VERBOSE("nativeSetLonLatZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + + double lon = env->GetDoubleField(lon_lat_zoom, lon_lat_zoom_lon_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } + + double lat = env->GetDoubleField(lon_lat_zoom, lon_lat_zoom_lat_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } + + double zoom = env->GetDoubleField(lon_lat_zoom, lon_lat_zoom_zoom_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } + + native_map_view->getMap()->setLonLatZoom(lon, lat, zoom, duration); +} + +jobject JNICALL nativeGetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetLonLatZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + double lon, lat, zoom; + native_map_view->getMap()->getLonLatZoom(lon, lat, zoom); + + jobject ret = env->NewObject(lon_lat_zoom_class, lon_lat_zoom_constructor_id, lon, lat, zoom); + if (ret == nullptr) { + env->ExceptionDescribe(); + return nullptr; + } + + return ret; +} + +void JNICALL nativeResetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeResetZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->resetZoom(); +} + +void JNICALL nativeStartScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStartScaling"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->startScaling(); +} + +void JNICALL nativeStopScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStopScaling"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->stopScaling(); +} + +jdouble JNICALL nativeGetMinZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetMinZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + return native_map_view->getMap()->getMinZoom(); +} + +jdouble JNICALL nativeGetMaxZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetMaxZoom"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + return native_map_view->getMap()->getMaxZoom(); +} + +void JNICALL nativeRotateBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble sx, jdouble sy, jdouble ex, jdouble ey, jdouble duration) { + VERBOSE("nativeRotateBy"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->rotateBy(sx, sy, ex, ey, duration); +} + +void JNICALL nativeSetAngle(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble angle, jdouble duration) { + VERBOSE("nativeSetAngle"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + // TODO update java class + //native_map_view->getMap()->setAngle(angle, duration); + double degrees = -angle / M_PI * 180; + native_map_view->getMap()->setBearing(degrees, duration); +} + +void JNICALL nativeSetAngle(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble angle, jdouble cx, jdouble cy) { + VERBOSE("nativeSetAngle"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + // TODO update java class + //native_map_view->getMap()->setAngle(angle, cx, cy); + double degrees = -angle / M_PI * 180; + native_map_view->getMap()->setBearing(degrees, cx, cy); +} + +jdouble JNICALL nativeGetAngle(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetAngle"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + // TODO update java class + //return native_map_view->getMap()->getAngle(); + double degrees = native_map_view->getMap()->getBearing(); + return -degrees * M_PI / 180; +} + +void JNICALL nativeResetNorth(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeResetNorth"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->resetNorth(); +} + +void JNICALL nativeStartRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStartRotating"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->startRotating(); +} + +void JNICALL nativeStopRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeStopRotating"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->stopRotating(); +} + +jboolean JNICALL nativeCanRotate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeCanRotate"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + return native_map_view->getMap()->canRotate(); +} + +void JNICALL nativeSetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jboolean debug) { + VERBOSE("nativeSetDebug"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->setDebug(debug); +} + +void JNICALL nativeToggleDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeToggleDebug"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->toggleDebug(); +} + +jboolean JNICALL nativeGetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetDebug"); + ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + return native_map_view->getMap()->getDebug(); +} + +} // namespace + +extern "C" { + +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { + VERBOSE("JNI_OnLoad"); + + JNIEnv* env = nullptr; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + ERROR("GetEnv() failed"); + return JNI_ERR; + } + + lon_lat_class = env->FindClass("com/mapbox/mapboxgl/lib/LonLat"); + if (lon_lat_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_constructor_id = env->GetMethodID(lon_lat_class, "", "(DD)V"); + if (lon_lat_constructor_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_lon_id = env->GetFieldID(lon_lat_class, "lon", "D"); + if (lon_lat_lon_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_lat_id = env->GetFieldID(lon_lat_class, "lat", "D"); + if (lon_lat_lat_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_class = env->FindClass("com/mapbox/mapboxgl/lib/LonLatZoom"); + if (lon_lat_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_constructor_id = env->GetMethodID(lon_lat_zoom_class, "", "(DDD)V"); + if (lon_lat_zoom_constructor_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_lon_id = env->GetFieldID(lon_lat_zoom_class, "lon", "D"); + if (lon_lat_zoom_lon_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_lat_id = env->GetFieldID(lon_lat_zoom_class, "lat", "D"); + if (lon_lat_zoom_lat_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_zoom_id = env->GetFieldID(lon_lat_zoom_class, "zoom", "D"); + if (lon_lat_zoom_zoom_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + jclass native_map_view_class = env->FindClass("com/mapbox/mapboxgl/lib/NativeMapView"); + if (native_map_view_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + on_map_changed_id = env->GetMethodID(native_map_view_class, "onMapChanged", "()V"); + if (on_map_changed_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + runtime_exception_class = env->FindClass("java/lang/RuntimeException"); + if (runtime_exception_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + jclass set_class = env->FindClass("java/util/Set"); + if (set_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + set_to_array_id = env->GetMethodID(set_class, "toArray", "()[Ljava/lang/Object;"); + if (set_to_array_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + tree_set_class = env->FindClass("java/util/TreeSet"); + if (tree_set_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + tree_set_constructor_id = env->GetMethodID(tree_set_class, "", "()V"); + if (tree_set_constructor_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + tree_set_add_id = env->GetMethodID(tree_set_class, "add", "(Ljava/lang/Object;)Z"); + if (tree_set_add_id == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + std::array methods = {{ // Can remove the extra brace in C++14 + { "nativeCreate", "(Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, + { "nativeDestroy", "(J)V", reinterpret_cast(&nativeDestroy) }, + { "nativeInitializeDisplay", "(J)V", reinterpret_cast(&nativeInitializeDisplay) }, + { "nativeTerminateDisplay", "(J)V", reinterpret_cast(&nativeTerminateDisplay) }, + { "nativeInitializeContext", "(J)V", reinterpret_cast(&nativeInitializeContext) }, + { "nativeTerminateContext", "(J)V", reinterpret_cast(&nativeTerminateContext) }, + { "nativeCreateSurface", "(JLandroid/view/Surface;)V", reinterpret_cast(&nativeCreateSurface) }, + { "nativeDestroySurface", "(J)V", reinterpret_cast(&nativeDestroySurface) }, + { "nativeStart", "(J)V", reinterpret_cast(&nativeStart) }, + { "nativeStop", "(J)V", reinterpret_cast(&nativeStop) }, + { "nativeRerender", "(J)V", reinterpret_cast(&nativeRerender) }, + { "nativeUpdate", "(J)V", reinterpret_cast(&nativeUpdate) }, + { "nativeCleanup", "(J)V", reinterpret_cast(&nativeCleanup) }, + { "nativeAddDefaultSource", "(J)V", reinterpret_cast(&nativeAddDefaultSource) }, + { "nativeRemoveDefaultSource", "(J)V", reinterpret_cast(&nativeRemoveDefaultSource) }, + { "nativeAddSource", "(JLjava/lang/String;Ljava/lang/String;)V", reinterpret_cast(&nativeAddSource) }, + { "nativeRemoveSource", "(JLjava/lang/String;)V", reinterpret_cast(&nativeRemoveSource) }, + { "nativeResize", "(JIIF)V", reinterpret_cast(static_cast(&nativeResize)) }, + { "nativeResize", "(JIIFII)V", reinterpret_cast(static_cast(&nativeResize)) }, + { "nativeSetAppliedClasses", "(JLjava/util/Set;)V", reinterpret_cast(&nativeSetAppliedClasses) }, + { "nativeGetAppliedClasses", "(J)Ljava/util/Set;", reinterpret_cast(&nativeGetAppliedClasses) }, + { "nativeSetDefaultTransitionDuration", "(JJ)V", reinterpret_cast(&nativeSetDefaultTransitionDuration) }, + { "nativeSetStyleJSON", "(JLjava/lang/String;)V", reinterpret_cast(&nativeSetStyleJSON) }, + { "nativeGetStyleJSON", "(J)Ljava/lang/String;", reinterpret_cast(&nativeGetStyleJSON) }, + { "nativeCancelTransitions", "(J)V", reinterpret_cast(&nativeCancelTransitions) }, + { "nativeMoveBy", "(JDDD)V", reinterpret_cast(&nativeMoveBy) }, + { "nativeSetLonLat", "(JLcom/mapbox/mapboxgl/lib/LonLat;D)V", reinterpret_cast(&nativeSetLonLat) }, + { "nativeGetLonLat", "(J)Lcom/mapbox/mapboxgl/lib/LonLat;", reinterpret_cast(&nativeGetLonLat) }, + { "nativeStartPanning", "(J)V", reinterpret_cast(&nativeStartPanning) }, + { "nativeStopPanning", "(J)V", reinterpret_cast(&nativeStopPanning) }, + { "nativeResetPosition", "(J)V", reinterpret_cast(&nativeResetPosition) }, + { "nativeScaleBy", "(JDDDD)V", reinterpret_cast(&nativeScaleBy) }, + { "nativeSetScale", "(JDDDD)V", reinterpret_cast(&nativeSetScale) }, + { "nativeGetScale", "(J)D", reinterpret_cast(&nativeGetScale) }, + { "nativeSetZoom", "(JDD)V", reinterpret_cast(&nativeSetZoom) }, + { "nativeGetZoom", "(J)D", reinterpret_cast(&nativeGetZoom) }, + { "nativeSetLonLatZoom", "(JLcom/mapbox/mapboxgl/lib/LonLatZoom;D)V", reinterpret_cast(&nativeSetLonLatZoom) }, + { "nativeGetLonLatZoom", "(J)Lcom/mapbox/mapboxgl/lib/LonLatZoom;", reinterpret_cast(&nativeGetLonLatZoom) }, + { "nativeResetZoom", "(J)V", reinterpret_cast(&nativeResetZoom) }, + { "nativeStartPanning", "(J)V", reinterpret_cast(&nativeStartScaling) }, + { "nativeStopPanning", "(J)V", reinterpret_cast(&nativeStopScaling) }, + { "nativeGetMinZoom", "(J)D", reinterpret_cast(&nativeGetMinZoom) }, + { "nativeGetMaxZoom", "(J)D", reinterpret_cast(&nativeGetMaxZoom) }, + { "nativeRotateBy", "(JDDDDD)V", reinterpret_cast(&nativeRotateBy) }, + { "nativeSetAngle", "(JDD)V", reinterpret_cast(static_cast(&nativeSetAngle)) }, + { "nativeSetAngle", "(JDDD)V", reinterpret_cast(static_cast(&nativeSetAngle)) }, + { "nativeGetAngle", "(J)D", reinterpret_cast(&nativeGetAngle) }, + { "nativeResetNorth", "(J)V", reinterpret_cast(&nativeResetNorth) }, + { "nativeStartRotating", "(J)V", reinterpret_cast(&nativeStartRotating) }, + { "nativeStopRotating", "(J)V", reinterpret_cast(&nativeStopRotating) }, + { "nativeCanRotate", "(J)Z", reinterpret_cast(&nativeCanRotate) }, + { "nativeSetDebug", "(JZ)V", reinterpret_cast(&nativeSetDebug) }, + { "nativeToggleDebug", "(J)V", reinterpret_cast(&nativeToggleDebug) }, + { "nativeGetDebug", "(J)Z", reinterpret_cast(&nativeGetDebug) } + }}; + + if (env->RegisterNatives(native_map_view_class, methods.data(), methods.size()) < 0) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_class = reinterpret_cast(env->NewGlobalRef(lon_lat_class)); + if (lon_lat_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + + lon_lat_zoom_class = reinterpret_cast(env->NewGlobalRef(lon_lat_zoom_class)); + if (lon_lat_zoom_class == nullptr) { + env->ExceptionDescribe(); + env->DeleteGlobalRef(lon_lat_class); + return JNI_ERR; + } + + runtime_exception_class = reinterpret_cast(env->NewGlobalRef(runtime_exception_class)); + if (runtime_exception_class == nullptr) { + env->ExceptionDescribe(); + env->DeleteGlobalRef(lon_lat_class); + env->DeleteGlobalRef(lon_lat_zoom_class); + return JNI_ERR; + } + + tree_set_class = reinterpret_cast(env->NewGlobalRef(tree_set_class)); + if (tree_set_class == nullptr) { + env->ExceptionDescribe(); + env->DeleteGlobalRef(lon_lat_class); + env->DeleteGlobalRef(lon_lat_zoom_class); + env->DeleteGlobalRef(runtime_exception_class); + return JNI_ERR; + } + + return JNI_VERSION_1_6; +} + +extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { + VERBOSE("JNI_OnUnload"); + + JNIEnv* env = nullptr; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + ERROR("GetEnv() failed"); + return; + } + + env->DeleteGlobalRef(lon_lat_class); + lon_lat_class = nullptr; + lon_lat_constructor_id = nullptr; + lon_lat_lon_id = nullptr; + lon_lat_lat_id = nullptr; + + env->DeleteGlobalRef(lon_lat_zoom_class); + lon_lat_zoom_class = nullptr; + lon_lat_zoom_constructor_id = nullptr; + lon_lat_zoom_lon_id = nullptr; + lon_lat_zoom_lat_id = nullptr; + lon_lat_zoom_zoom_id = nullptr; + + on_map_changed_id = nullptr; + + env->DeleteGlobalRef(runtime_exception_class); + runtime_exception_class = nullptr; + + set_to_array_id = nullptr; + + env->DeleteGlobalRef(tree_set_class); + tree_set_class = nullptr; + tree_set_constructor_id = nullptr; + tree_set_add_id = nullptr; +} + +} // extern "C" diff --git a/android/NativeMapView.cpp b/android/NativeMapView.cpp new file mode 100644 index 000000000..7ced2fa68 --- /dev/null +++ b/android/NativeMapView.cpp @@ -0,0 +1,528 @@ +//#include + +#include + +#include + +#include + +#include "log.h" + +#include "NativeMapView.hpp" + +namespace mbgl { +namespace android { + +void log_egl_string(EGLDisplay display, EGLint name, const char* label) { + const char* str = eglQueryString(display, name); + if (str == nullptr) { + ERROR("eglQueryString(%d) returned error %d", name, eglGetError()); + } else { + INFO("EGL %s: %s", label, str); + } +} + +void log_gl_string(GLenum name, const char* label) { + const GLubyte* str = glGetString(name); + if (str == nullptr) { + ERROR("glGetString(%d) returned error %d", name, glGetError()); + } else { + INFO("GL %s: %s", label, str); + } +} + +NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, + std::string default_style_json_) : + default_style_json(default_style_json_) { + VERBOSE("NativeMapView::NativeMapView"); + + ASSERT(env != nullptr); + ASSERT(obj_ != nullptr); + + if (env->GetJavaVM(&vm) < 0) { + env->ExceptionDescribe(); + return; + } + + obj = env->NewGlobalRef(obj_); + if (obj == nullptr) { + env->ExceptionDescribe(); + return; + } + + // TODO replace all printfs in map code with android logging + //freopen("/sdcard/stdout.txt", "w", stdout); // NOTE: can't use till NDK fix the stdout macro bug + //freopen("/sdcard/stderr.txt", "w", stderr); + + view = new MBGLView(this); + map = new mbgl::Map(*view); + + // FIXME need way to load this from Java + map->setAccessToken("pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"); + + // FIXME this asserts because it creates FileSource of different thread from run thread + //map->setStyleJSON(default_style_json); + map->setStyleURL("https://raw.githubusercontent.com/mapbox/mapbox-gl-styles/mb-pages/styles/bright-v5.json"); +} + +NativeMapView::~NativeMapView() { + VERBOSE("NativeMapView::~NativeMapView"); + terminateContext(); + + delete map; + map = nullptr; + + delete view; + view = nullptr; + + ASSERT(vm != nullptr); + ASSERT(obj != nullptr); + + JNIEnv* env = nullptr; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_OK) { + env->DeleteGlobalRef(obj); + } else { + ERROR("GetEnv() failed"); + } + obj = nullptr; + vm = nullptr; +} + +bool NativeMapView::initializeDisplay() { + VERBOSE("NativeMapView::initializeDisplay"); + + ASSERT(display == EGL_NO_DISPLAY); + ASSERT(config == nullptr); + ASSERT(format < 0); + + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) { + ERROR("eglGetDisplay() returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + + EGLint major, minor; + if (!eglInitialize(display, &major, &minor)) { + ERROR("eglInitialize() returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + if ((major <= 1) && (minor < 3)) { + ERROR("EGL version is too low, need 1.3, got %d.%d", major, minor); + terminateDisplay(); + return false; + } + + log_egl_string(display, EGL_VENDOR, "Vendor"); + log_egl_string(display, EGL_VERSION, "Version"); + log_egl_string(display, EGL_CLIENT_APIS, "Client APIs"); + log_egl_string(display, EGL_EXTENSIONS, "Client Extensions"); + + // TODO should try 565, then 8888? + bool use565 = true; + + const EGLint config_attribs[] = { + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_CONFORMANT, EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, + EGL_BUFFER_SIZE, use565 ? 16 : 32, // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode) + EGL_RED_SIZE, use565 ? 5 : 8, + EGL_GREEN_SIZE, use565 ? 6 : 8, + EGL_BLUE_SIZE, use565 ? 5 : 8, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + EGL_NONE + }; + EGLint num_configs; + if (!eglChooseConfig(display, config_attribs, nullptr, 0, &num_configs)) { + ERROR("eglChooseConfig(NULL) returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + if (num_configs < 1) { + ERROR("eglChooseConfig() returned no configs"); + terminateDisplay(); + return false; + } + + // TODO make_unique missing in Android NDK? +// const std::unique_ptr configs = std::make_unique(new EGLConfig[num_configs]); + const std::unique_ptr configs(new EGLConfig[num_configs]); + if (!eglChooseConfig(display, config_attribs, configs.get(), num_configs, + &num_configs)) { + ERROR("eglChooseConfig() returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + + config = chooseConfig(configs.get(), num_configs, use565); + if (config == nullptr) { + ERROR("No config chosen"); + terminateDisplay(); + return false; + } + + if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) { + ERROR("eglGetConfigAttrib() returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + DEBUG("Chosen window format is %d", format); + + return true; +} + +void NativeMapView::terminateDisplay() { + VERBOSE("NativeMapView::terminateDisplay"); + + if (display != EGL_NO_DISPLAY) { + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } + + if (!eglTerminate(display)) { + ERROR("eglTerminate() returned error %d", eglGetError()); + } + } + + display = EGL_NO_DISPLAY; + config = nullptr; + format = -1; +} + +bool NativeMapView::initializeContext() { + VERBOSE("NativeMapView::initializeContext"); + + ASSERT(display != EGL_NO_DISPLAY); + ASSERT(context == EGL_NO_CONTEXT); + ASSERT(config != nullptr); + + const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + context = eglCreateContext(display, config, EGL_NO_CONTEXT, + context_attribs); + if (context == EGL_NO_CONTEXT) { + ERROR("eglCreateContext() returned error %d", eglGetError()); + terminateContext(); + return false; + } + + INFO("Context initialized"); + + return true; +} + +void NativeMapView::terminateContext() { + VERBOSE("NativeMapView::terminateContext"); + + map->terminate(); + + // TODO: there is a bug when you double tap home to go app switcher, as map is black if you immediately switch to the map again + // TODO: this is in the onPause/onResume path + // TODO: the bug causes an GL_INVALID_VALUE with glDelteProgram (I think due to context being deleted first) + // TODO: we need to free resources before we terminate + // TODO: but cause terminate and stop is async they try to do stuff with no context and crash! + + if (display != EGL_NO_DISPLAY) { + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } + + if (context != EGL_NO_CONTEXT) { + if (!eglDestroyContext(display, context)) { + ERROR("eglDestroyContext() returned error %d", eglGetError()); + } + } + } + + context = EGL_NO_CONTEXT; +} + +bool NativeMapView::createSurface(ANativeWindow* window_) { + VERBOSE("NativeMapView::createSurface"); + + ASSERT(window == nullptr); + ASSERT(window_ != nullptr); + window = window_; + + ASSERT(display != EGL_NO_DISPLAY); + ASSERT(surface == EGL_NO_SURFACE); + ASSERT(config != nullptr); + ASSERT(format >= 0); + + ANativeWindow_setBuffersGeometry(window, 0, 0, format); + + const EGLint surface_attribs[] = { + EGL_NONE + }; + surface = eglCreateWindowSurface(display, config, window, surface_attribs); + if (surface == EGL_NO_SURFACE) { + ERROR("eglCreateWindowSurface() returned error %d", eglGetError()); + destroySurface(); + return false; + } + + start(); + + return true; +} + +void NativeMapView::destroySurface() { + VERBOSE("NativeMapView::destroySurface"); + + if (surface != EGL_NO_SURFACE) { + if (!eglDestroySurface(display, surface)) { + ERROR("eglDestroySurface() returned error %d", eglGetError()); + } + } + + surface = EGL_NO_SURFACE; + + if (window != nullptr) { + ANativeWindow_release(window); + window = nullptr; + } +} + +EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], + EGLint num_configs, bool use565) const { + INFO("Found %d configs", num_configs); + + int chosen_config = -1; + for (int i = 0; i < num_configs; i++) { + INFO("Config %d:", i); + + EGLint bits, red, green, blue, alpha, alpha_mask, depth, stencil, + sample_buffers, samples; + + if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) { + ERROR("eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) { + ERROR("eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) { + ERROR("eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) { + ERROR("eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) { + ERROR("eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, + &alpha_mask)) { + ERROR("eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) { + ERROR("eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, + &stencil)) { + ERROR("eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, + &sample_buffers)) { + ERROR("eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) { + ERROR("eglGetConfigAttrib(EGL_SAMPLES) returned error %d", + eglGetError()); + return nullptr; + } + + INFO("Color: %d", bits); + INFO("Red: %d", red); + INFO("Green: %d", green); + INFO("Blue: %d", blue); + INFO("Alpha: %d", alpha); + INFO("Alpha mask: %d", alpha_mask); + INFO("Depth: %d", depth); + INFO("Stencil: %d", stencil); + INFO("Sample buffers: %d", sample_buffers); + INFO("Samples: %d", samples); + + bool config_ok = true; + config_ok &= bits == (use565 ? 16 : 32); + config_ok &= red == (use565 ? 5 : 8); + config_ok &= green == (use565 ? 6 : 8); + config_ok &= blue == (use565 ? 5 : 8); + //config_ok &= (alpha == 0) || (alpha == 8); // Can be either 0 for RGBX or 8 for RGBA but we don't care either way + config_ok &= alpha == 0; // TODO should prefer no alpha over alpha, and 565 over 8888? + //config_ok &= depth == 16; + //config_ok &= stencil == 8; + //config_ok &= sample_buffers == 0; + config_ok &= samples == 0; + + if (config_ok) { // Choose the last matching config, that way we get RGBX if possible (since it is sorted highest to lowest bits) + chosen_config = i; + } + } + + DEBUG("Chosen config is %d", chosen_config); + if (chosen_config >= 0) { + return configs[chosen_config]; + } else { + return nullptr; + } +} + +void NativeMapView::start() { + VERBOSE("NativeMapView::start"); + + if (display == EGL_NO_DISPLAY) { + initializeDisplay(); + } + + if (context == EGL_NO_CONTEXT) { + initializeContext(); + } + + if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) + && (context != EGL_NO_CONTEXT)) { + + if (!eglMakeCurrent(display, surface, surface, context)) { + ERROR("eglMakeCurrent() returned error %d", eglGetError()); + } + + log_gl_string(GL_VENDOR, "Vendor"); + log_gl_string(GL_RENDERER, "Renderer"); + log_gl_string(GL_VERSION, "Version"); + log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); + log_gl_string(GL_EXTENSIONS, "Extensions"); + + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } + + map->start(); + } else { + DEBUG("Not starting because we are not ready"); + } +} + +void NativeMapView::stop() { + VERBOSE("NativeMapView::stop"); + if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) + && (context != EGL_NO_CONTEXT)) { + map->stop(); + } +} + +void NativeMapView::notifyMapChange() { + DEBUG("NativeMapView::notify_map_change()"); + + ASSERT(vm != nullptr); + ASSERT(obj != nullptr); + + JNIEnv* env = nullptr; + // FIXME this returns failure + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + ERROR("GetEnv() failed"); + return; + } + + env->CallVoidMethod(obj, on_map_changed_id); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + return; + } +} + +void MBGLView::make_active() { + VERBOSE("MBGLView::make_active"); + if ((nativeView->display != EGL_NO_DISPLAY) + && (nativeView->surface != EGL_NO_SURFACE) + && (nativeView->context != EGL_NO_CONTEXT)) { + if (!eglMakeCurrent(nativeView->display, nativeView->surface, + nativeView->surface, nativeView->context)) { + ERROR("eglMakeCurrent() returned error %d", eglGetError()); + } + + // TODO still can't fix the black screen problem :-( + EGLint width, height; + if (!(eglQuerySurface(nativeView->display, nativeView->surface, EGL_WIDTH, &width) + & eglQuerySurface(nativeView->display, nativeView->surface, EGL_HEIGHT, &height))) { + ERROR("eglQuerySurface() returned error %d", eglGetError()); + } + map->resize(width, height); + + } else { + DEBUG("Not activating as we are not ready"); + } +} + +void MBGLView::make_inactive() { + VERBOSE("MBGLView::make_inactive"); + if (!eglMakeCurrent(nativeView->display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } +} + +void MBGLView::swap() { + VERBOSE("MBGLView::swap"); + if (map->needsSwap() && (nativeView->display != EGL_NO_DISPLAY) + && (nativeView->surface != EGL_NO_SURFACE)) { + if (!eglSwapBuffers(nativeView->display, nativeView->surface)) { + ERROR("eglSwapBuffers() returned error %d", eglGetError()); + } + map->swapped(); + } else { + DEBUG("Not swapping as we are not ready"); + } +} + +void MBGLView::notify() { + DEBUG("MBGLView::notify()"); + // TODO +} + +void MBGLView::notify_map_change(mbgl::MapChange change, mbgl::timestamp delay) { + DEBUG("MBGLView::notify_map_change()"); + nativeView->notifyMapChange(); + // TODO: use new variables + (void)change; + (void)delay; +} + +} // namespace android +} // namespace mbgl diff --git a/android/NativeMapView.hpp b/android/NativeMapView.hpp new file mode 100644 index 000000000..046cd7c8f --- /dev/null +++ b/android/NativeMapView.hpp @@ -0,0 +1,111 @@ +#ifndef MAP_VIEW_HPP +#define MAP_VIEW_HPP + +#include + +#include + +#include + +#include + +#include + +namespace mbgl { +namespace android { + +extern jmethodID on_map_changed_id; + +extern jclass lon_lat_class; +extern jmethodID lon_lat_constructor_id; +extern jfieldID lon_lat_lon_id; +extern jfieldID lon_lat_lat_id; + +extern jclass lon_lat_zoom_class; +extern jmethodID lon_lat_zoom_constructor_id; +extern jfieldID lon_lat_zoom_lon_id; +extern jfieldID lon_lat_zoom_lat_id; +extern jfieldID lon_lat_zoom_zoom_id; + +extern jclass runtime_exception_class; + +extern jmethodID set_to_array_id; + +extern jclass tree_set_class; +extern jmethodID tree_set_constructor_id; +extern jmethodID tree_set_add_id; + +class MBGLView; + +class NativeMapView { + friend class MBGLView; + +public: + NativeMapView(JNIEnv* env, jobject obj, std::string default_style_json); + ~NativeMapView(); + + mbgl::Map* getMap() const { + return map; + } + + bool initializeDisplay(); + void terminateDisplay(); + + bool initializeContext(); + void terminateContext(); + + bool createSurface(ANativeWindow* window); + void destroySurface(); + + void start(); + void stop(); + + void notifyMapChange(); + +private: + EGLConfig chooseConfig(const EGLConfig configs[], EGLint num_configs, + bool use565) const; + +private: + JavaVM* vm = nullptr; + jobject obj = nullptr; + + ANativeWindow* window = nullptr; + + mbgl::Map* map = nullptr; + MBGLView* view = nullptr; + + EGLDisplay display = EGL_NO_DISPLAY; + EGLSurface surface = EGL_NO_SURFACE; + EGLContext context = EGL_NO_CONTEXT; + + EGLConfig config = nullptr; + EGLint format = -1; + + std::string default_style_json; +}; + +class MBGLView: public mbgl::View { +public: + MBGLView(NativeMapView* nativeView_) : + nativeView(nativeView_) { + } + virtual ~MBGLView() { + } + + void make_active() override; + void make_inactive() override; + + void swap() override; + + void notify() override; + void notify_map_change(mbgl::MapChange change, mbgl::timestamp delay = 0) override; + +private: + NativeMapView* nativeView = nullptr; +}; + +} // namespace android +} // namespace mbgl + +#endif // MAP_VIEW_HPP diff --git a/android/log.h b/android/log.h new file mode 100644 index 000000000..461528a4c --- /dev/null +++ b/android/log.h @@ -0,0 +1,18 @@ +#ifndef LOG_H +#define LOG_H + +#include + +#define S(x) #x +#define S_(x) S(x) +#define S__LINE__ S_(__LINE__) + +#define VERBOSE(fmt, args...) __android_log_print(ANDROID_LOG_VERBOSE, __FILE__ ":" S__LINE__, fmt, ##args) +#define DEBUG(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__ ":" S__LINE__, fmt, ##args) +#define INFO(fmt, args...) __android_log_print(ANDROID_LOG_INFO, __FILE__ ":" S__LINE__, fmt, ##args) +#define WARN(fmt, args...) __android_log_print(ANDROID_LOG_WARN, __FILE__ ":" S__LINE__, fmt, ##args) +#define ERROR(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, __FILE__ ":" S__LINE__, fmt, ##args) +#define ASSERT(cond, ...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, NULL)) +#define ASSERTMSG(cond, fmt, args...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, fmt, ##args)) + +#endif // LOG_H diff --git a/android/mapboxgl-app.gyp b/android/mapboxgl-app.gyp new file mode 100644 index 000000000..203069ace --- /dev/null +++ b/android/mapboxgl-app.gyp @@ -0,0 +1,42 @@ +{ + 'includes': [ + '../gyp/common.gypi', + ], + 'targets': [ + { + 'target_name': 'androidapp', + 'product_name': 'mapbox-gl', + 'type': 'shared_library', + 'sources': [ + './NativeMapView.cpp', + './JNI.cpp', + ], + 'cflags_cc': [ + '-I<(boost_root)/include', + ], + 'variables': { + 'ldflags': [ + '-llog -landroid -lEGL -lGLESv2 ' + '<@(png_ldflags)', + '<@(sqlite3_ldflags)', + '<@(openssl_ldflags)', + '<@(curl_ldflags)', + '<@(zlib_ldflags)', + ], + }, + 'conditions': [ + ['OS == "mac"', { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ '<@(ldflags)' ], + } + }, { + 'libraries': [ '<@(ldflags)' ], + }] + ], + 'dependencies': [ + '../mapboxgl.gyp:mbgl-standalone', + '../mapboxgl.gyp:mbgl-android', + ], + }, + ], +} diff --git a/gyp/mbgl-android.gypi b/gyp/mbgl-android.gypi new file mode 100644 index 000000000..02c475b01 --- /dev/null +++ b/gyp/mbgl-android.gypi @@ -0,0 +1,69 @@ +{ + 'targets': [ + { 'target_name': 'mbgl-android', + 'product_name': 'mbgl-android', + 'type': 'static_library', + 'standalone_static_library': 1, + 'hard_dependency': 1, + 'variables': { + 'cflags_cc': [ + '<@(png_cflags)', + '<@(uv_cflags)', + '<@(curl_cflags)', + '<@(nu_cflags)', + '-I<(boost_root)/include', + ], + 'cflags': [ + '<@(uv_cflags)', + '<@(nu_cflags)', + ], + 'ldflags': [ + '<@(png_ldflags)', + '<@(uv_ldflags)', + '<@(curl_ldflags)', + '<@(nu_ldflags)', + ], + }, + 'sources': [ + '../platform/android/cache_database_data.cpp', + '../platform/default/log_stderr.cpp', + '../platform/default/string_stdlib.cpp', + '../platform/default/http_request_baton_curl.cpp', + '../platform/default/image.cpp', + ], + 'include_dirs': [ + '../include', + ], + 'link_settings': { + 'libraries': [ + '<@(png_static_libs)', + ], + }, + 'conditions': [ + ['OS == "mac"', { + 'xcode_settings': { + 'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ], + 'OTHER_CFLAGS': [ '<@(cflags)' ], + } + }, { + 'cflags_cc': [ '<@(cflags_cc)' ], + 'cflags': [ '<@(cflags)' ], + }] + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '../include', + ], + 'conditions': [ + ['OS == "mac"', { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ '<@(ldflags)' ], + } + }, { + 'ldflags': [ '<@(ldflags)' ], + }] + ], + }, + }, + ], +} diff --git a/gyp/mbgl-platform.gypi b/gyp/mbgl-platform.gypi index 74ac9aecc..08fa5f821 100644 --- a/gyp/mbgl-platform.gypi +++ b/gyp/mbgl-platform.gypi @@ -12,5 +12,9 @@ 'includes': ['mbgl-linux.gypi'], 'variables': { 'platform_library': 'mbgl-linux' }, }], + ['platform == "android"', { + 'includes': ['mbgl-android.gypi'], + 'variables': { 'platform_library': 'mbgl-android' }, + }], ], } From 4dc66ad8b9527b0bb22dff7d5d792ed62a96210b Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 5 Nov 2014 23:18:19 +1100 Subject: [PATCH 038/415] Fix typo --- scripts/travis_before_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 528f571ff..962a6901d 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -18,7 +18,7 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: chmod NDK" chmod a+x ./android-ndk-r10c-linux-x86_64.bin - echo "debug: upacking NDK" + echo "debug: unpacking NDK" #mapbox_time "unpacking NDK" \ $SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > .tmp-ndk-log rm .tmp-ndk-log From 0da778d24475bc4d9a9d9109d7fe6cc2a2a244c1 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 00:02:17 +1100 Subject: [PATCH 039/415] Move cpp files into dir --- android/{ => cpp}/JNI.cpp | 0 android/{ => cpp}/NativeMapView.cpp | 0 android/{ => cpp}/NativeMapView.hpp | 0 android/{ => cpp}/log.h | 0 android/mapboxgl-app.gyp | 4 ++-- 5 files changed, 2 insertions(+), 2 deletions(-) rename android/{ => cpp}/JNI.cpp (100%) rename android/{ => cpp}/NativeMapView.cpp (100%) rename android/{ => cpp}/NativeMapView.hpp (100%) rename android/{ => cpp}/log.h (100%) diff --git a/android/JNI.cpp b/android/cpp/JNI.cpp similarity index 100% rename from android/JNI.cpp rename to android/cpp/JNI.cpp diff --git a/android/NativeMapView.cpp b/android/cpp/NativeMapView.cpp similarity index 100% rename from android/NativeMapView.cpp rename to android/cpp/NativeMapView.cpp diff --git a/android/NativeMapView.hpp b/android/cpp/NativeMapView.hpp similarity index 100% rename from android/NativeMapView.hpp rename to android/cpp/NativeMapView.hpp diff --git a/android/log.h b/android/cpp/log.h similarity index 100% rename from android/log.h rename to android/cpp/log.h diff --git a/android/mapboxgl-app.gyp b/android/mapboxgl-app.gyp index 203069ace..98b6b4ba4 100644 --- a/android/mapboxgl-app.gyp +++ b/android/mapboxgl-app.gyp @@ -8,8 +8,8 @@ 'product_name': 'mapbox-gl', 'type': 'shared_library', 'sources': [ - './NativeMapView.cpp', - './JNI.cpp', + './cpp/NativeMapView.cpp', + './cpp/JNI.cpp', ], 'cflags_cc': [ '-I<(boost_root)/include', From bff63d44c62cea75e2e5162e7c46cd1fbf8ef4c1 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 00:04:48 +1100 Subject: [PATCH 040/415] Add java files --- android/java/.gitignore | 14 + android/java/app/build.gradle | 23 + android/java/app/lint.xml | 4 + android/java/app/src/main/AndroidManifest.xml | 33 + .../mapbox/mapboxgl/app/FragmentActivity.java | 29 + .../com/mapbox/mapboxgl/app/MainActivity.java | 98 ++ .../main/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 7658 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 3777 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 12516 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 24777 bytes .../src/main/res/layout/activity_fragment.xml | 11 + .../app/src/main/res/layout/activity_main.xml | 11 + .../app/src/main/res/values-v11/styles.xml | 11 + .../app/src/main/res/values-v14/styles.xml | 12 + .../java/app/src/main/res/values/strings.xml | 6 + .../java/app/src/main/res/values/styles.xml | 20 + android/java/build.gradle | 15 + .../java/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + android/java/gradlew | 164 +++ android/java/gradlew.bat | 90 ++ android/java/lib/build.gradle | 31 + android/java/lib/lint.xml | 4 + android/java/lib/src/main/AndroidManifest.xml | 12 + .../gesturedetectors/BaseGestureDetector.java | 160 +++ .../gesturedetectors/MoveGestureDetector.java | 188 +++ .../RotateGestureDetector.java | 180 +++ .../ShoveGestureDetector.java | 213 +++ .../TwoFingerGestureDetector.java | 228 ++++ .../java/com/mapbox/mapboxgl/lib/LonLat.java | 91 ++ .../com/mapbox/mapboxgl/lib/LonLatZoom.java | 124 ++ .../com/mapbox/mapboxgl/lib/MapFragment.java | 150 +++ .../java/com/mapbox/mapboxgl/lib/MapView.java | 1178 +++++++++++++++++ .../mapbox/mapboxgl/lib/NativeMapView.java | 501 +++++++ .../java/lib/src/main/res/raw/style.min.js | 1 + .../java/lib/src/main/res/values/attrs.xml | 32 + .../java/lib/src/main/res/values/strings.xml | 3 + android/java/settings.gradle | 2 + 38 files changed, 3645 insertions(+) create mode 100644 android/java/.gitignore create mode 100644 android/java/app/build.gradle create mode 100644 android/java/app/lint.xml create mode 100644 android/java/app/src/main/AndroidManifest.xml create mode 100644 android/java/app/src/main/java/com/mapbox/mapboxgl/app/FragmentActivity.java create mode 100644 android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java create mode 100644 android/java/app/src/main/res/drawable-hdpi/ic_launcher.png create mode 100644 android/java/app/src/main/res/drawable-mdpi/ic_launcher.png create mode 100644 android/java/app/src/main/res/drawable-xhdpi/ic_launcher.png create mode 100644 android/java/app/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 android/java/app/src/main/res/layout/activity_fragment.xml create mode 100644 android/java/app/src/main/res/layout/activity_main.xml create mode 100644 android/java/app/src/main/res/values-v11/styles.xml create mode 100644 android/java/app/src/main/res/values-v14/styles.xml create mode 100644 android/java/app/src/main/res/values/strings.xml create mode 100644 android/java/app/src/main/res/values/styles.xml create mode 100644 android/java/build.gradle create mode 100644 android/java/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/java/gradle/wrapper/gradle-wrapper.properties create mode 100755 android/java/gradlew create mode 100644 android/java/gradlew.bat create mode 100644 android/java/lib/build.gradle create mode 100644 android/java/lib/lint.xml create mode 100644 android/java/lib/src/main/AndroidManifest.xml create mode 100644 android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java create mode 100644 android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java create mode 100644 android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java create mode 100644 android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java create mode 100644 android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java create mode 100644 android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLat.java create mode 100644 android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLatZoom.java create mode 100644 android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapFragment.java create mode 100644 android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java create mode 100644 android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java create mode 100644 android/java/lib/src/main/res/raw/style.min.js create mode 100644 android/java/lib/src/main/res/values/attrs.xml create mode 100644 android/java/lib/src/main/res/values/strings.xml create mode 100644 android/java/settings.gradle diff --git a/android/java/.gitignore b/android/java/.gitignore new file mode 100644 index 000000000..478d78698 --- /dev/null +++ b/android/java/.gitignore @@ -0,0 +1,14 @@ +# Gradle files +.gradle/ + +# IntelliJ files +.idea +*.iml + +# Build files +build/ +*/build/ +*.so + +# Local settings +local.properties diff --git a/android/java/app/build.gradle b/android/java/app/build.gradle new file mode 100644 index 000000000..cb3750b7d --- /dev/null +++ b/android/java/app/build.gradle @@ -0,0 +1,23 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 19 + buildToolsVersion "21.1.0" + + defaultConfig { + applicationId "com.mapbox.mapboxgl.app" + minSdkVersion 19 + targetSdkVersion 20 + } + + buildTypes { + release { + runProguard false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} + +dependencies { + compile project(':lib') +} diff --git a/android/java/app/lint.xml b/android/java/app/lint.xml new file mode 100644 index 000000000..7328a1fd1 --- /dev/null +++ b/android/java/app/lint.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/android/java/app/src/main/AndroidManifest.xml b/android/java/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..83995b8d3 --- /dev/null +++ b/android/java/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/FragmentActivity.java b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/FragmentActivity.java new file mode 100644 index 000000000..1b27ca2fd --- /dev/null +++ b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/FragmentActivity.java @@ -0,0 +1,29 @@ +package com.mapbox.mapboxgl.app; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +public class FragmentActivity extends Activity { + + // + // Static members + // + + // Tag used for logging + private static final String TAG = "FragmentActivity"; + + // + // Lifecycle events + // + + // Called when activity is created + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.v(TAG, "onCreate"); + + // Load the layout + setContentView(R.layout.activity_fragment); + } +} diff --git a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java new file mode 100644 index 000000000..43537f9ac --- /dev/null +++ b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java @@ -0,0 +1,98 @@ +package com.mapbox.mapboxgl.app; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +import com.mapbox.mapboxgl.lib.MapView; + +public class MainActivity extends Activity { + + // + // Static members + // + + // Tag used for logging + private static final String TAG = "MainActivity"; + + // The map + private MapView mMapView; + + // + // Lifecycle events + // + + // Called when the activity is created + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.v(TAG, "onCreate"); + + // Load the layout + setContentView(R.layout.activity_main); + mMapView = (MapView) findViewById(R.id.map); + + // Need to pass on any saved state to the map + mMapView.onCreate(savedInstanceState); + } + + // Called when the activity is destroyed + @Override + protected void onDestroy() { + super.onDestroy(); + Log.v(TAG, "onDestroy"); + + // Need to pass on to view + mMapView.onDestroy(); + } + + // Called when the activity is visible + @Override + protected void onStart() { + super.onStart(); + Log.v(TAG, "onStart"); + + // Need to pass on to view + mMapView.onStart(); + } + + // Called when the activity is invisible + @Override + protected void onStop() { + super.onStop(); + Log.v(TAG, "onStop"); + + // Need to pass on to view + mMapView.onStop(); + } + + // Called when the activity is in the background + @Override + protected void onPause() { + super.onPause(); + Log.v(TAG, "onPause"); + + // Need to pass on to view + mMapView.onPause(); + } + + // Called when the activity is no longer in the background + @Override + protected void onResume() { + super.onResume(); + Log.v(TAG, "onResume"); + + // Need to pass on to view + mMapView.onResume(); + } + + // Called before activity is destroyed + @Override + protected void onSaveInstanceState(Bundle outState) { + Log.v(TAG, "onSaveInstanceState"); + + // Need to retrieve any saved state from the map + mMapView.onSaveInstanceState(outState); + super.onSaveInstanceState(outState); + } +} diff --git a/android/java/app/src/main/res/drawable-hdpi/ic_launcher.png b/android/java/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..288b66551d1efd1f13dd06f20a67534d2df57946 GIT binary patch literal 7658 zcmVMzJ7$s6+2fii#kNjUp($C`b{IGW1?Vk&cRr0`^|9phjbh zJ=WM_FR>&xzW0Bh_Zx_AZnC@g9PVD-lkc48e9SPzeDCl5KXqnMQu_J$`S|(x`S=e# z0Qs-ZzlhUZeaZb3K&emf{_T)opM4psyY0H?f4!q{l=`eaHEQ7cv!1)#kGoXLcMN*( zpF!fk|5rc~_+OlSImLMUb=m3J>n&pCbi21V;NJpBzR2Xm@>OOviw`uPwEdE9(^>Y*HZsM}@^0hM@I5$wKJ_%B?~a>eJ1W;%d>xgoW3TkC!Fdx> z-aIq)+O1aSTMjv%ZR)ff@_ONX(XsEpM2+rCGzPpvP4Wv==f6Nh!3#8|Jx4?6W7L^n zM8O|>0C9MyAp6b!2R=zH2b~@z&CxFiksE$MFV*LrjYD#|{1^F)&17{Q)(_|JwQb*X z%70)Nsz*LY#oX^udi7fr2#=A&9wVE5gLHNS5$rMo$%4ot$>Q!^>`H9;@<3v$y?+)w~$0O6IT$8`^X_c6?lqSq?iKSB`9~1f!kzP5l?`k z2v8`Fvp^hv>4VRndSmwkcWk@qij7yDu=>0`+E3YZ&EIEnvwEw!S6iFXFW)&h7Z5n$ z7P5EVhFrLaFbck@xPVAd(8YBEbrp$Zab(lG&vD}KE+Gcz2+(N)bgU~7ho1Rj-(wke z-SfcK8*bQec{nM^0qrMju<&yWp?aIisj}6^QaO}<@s7ZuIt5eyd_kDe`ZcnY|SI`7NSadR{_ zUU9|hbBF1X>)^AQo`<`2p;EG!CDC>qiQigu~A%&ODn;HU$U2os7?)1md$t z{@DA#7rX9^$MzfE*mT7M>n@JOhI8Zb==l{e)`cg}ZeY>AksudC#abhl*J5}xd9IGt zTN|`_waY8rj@#oy4m^%{vj5Q()^TSPw%l~U@%v>2hw!kMxbV#q?7ZuRJrBlX@571M z_l++;Bir}Tk8b;-^PUfZnLsWz4qLBzVbf)Tb73S_ed&s2CtUE={jCH@z~d+PP`A+? zRhx}bzS)!G%YWuLzPR;q}rXVW$3KX1J@ZrjdV{=(*KwBuJC#jLaOq9@MW zYQ@nTv$6i-XwhA^-55gvIpBf==WEd3F%yMt`lwuQjIvcG$Zs(gX3Ww2D!NEz=vxbD&8bmZ>o0gc*l@{{ zt-0WUH5crmI6GqHmrkOsIp>1)mquXYGsUd6<|tcfK?*Wq36+{JLo-yoc>(!W_FT)3 zjR+?QMfCra1chSSdqYd=0*gwlGH zgK-eSEIeR`1)o`?iQqKsvOryjIjXmqV$Mbi&TEWNvfK~_OAR4!(!=y}brz5?c&AK8 zap*^48rbk#KH>a78?L<`T=5rZ{+;3ldw?nR zY1}jU4+}fTZ)@6Z&l-1|(TS-`TC4t!9+9OvJ!kE~-b0{G|ruUit(`7jb3UY;uaqc_*(=hsoOb z;H~X9_fYIfElkm6VOrJkSApprH@&oa9T5M6w~_nFUF62yVOd|?LgvF$&%D?6V>uR^oX+ z8HXZ**<<;=YB-!)f>X22jQ*CX7@RBC7^8T(5powBBBRL=NtH&Jnq&MbFiCqyaJpg) zvg>lt;3JR2R$USjU)^GP?28xvY{xw}w(?86hg4k7l<%l8Mg}jlQ8RQLS$f!3%TCEw9W}6_f!Wgkd zQbc6xV{(!N6Q^pzBTyaAV^v_~IvC~-DzLQIg6k+N!DFoLC07sqaG6Z$m$jQ5QkI`| ze9?K=PuTw;h;6$*maRDL%;xR25Q>*eA4V2vYV}Gvzo#zi>qB z7tUC6&=n0Gb|`2y=t``RY^bdr^})sq6E+=uFjb(`%&In8BE8lKA?fPy3|5BSn7_fu z{$psH{1;SpKEe=<4>54q@6mtA2l$)v`{+C1ef;&)_g>q8_t01QJ@gy&K0Y1#0hHB# zhap;jfSSRF(6#sorp|wXqjz6;1`U8u>>vcC4?%Rk8k?M~cGiB(-;@()>#Q$bVJOsh zSYlqMEtLjV6xdC9%wjWU>j+-qpZdyrV+znlzPzAg;^&|#%)p2hu zKjMY9&%Ll@pC=Y|dZKx!2O77#p>C@SYPUFI?j}1jTU2l6W=)yP3N>4;=xjl*W{#>& z_tHHw@)5kvfP9fw&}6Lc73W~4Uo4)ADQ!YLB$#KPgMCLF~hxIahpwN`Dz0;dxMk$ zsxj}hDTbI(EHOo6ry_8Z#Km~SQ#a34JdH0rKRVZX49o&hxsb zdW%_i{4t`!P>SMZh6F|*SOh+U_|OrD1`6)oDGVs z#nn>2no>J0vZ$TcK0LkiP2wqD4+#|pEMSx<8 zBy3c`KtG=8dIiv;?IFWU+D%#h5?z+RRF7Os54lu(WHjoCfPCYI9E(bH@0UH#c6a_# z9ag-|Kx`mqt&`HO8_`0I#imkWCT|$8X-veLX6^<@Y(AWiwFgpBxyG7YM~YdrD84Yh z$g-7+v@XJF<;|%gy=PE?m{P5W_;P(js z07Ke6sR&77q=eO^F51n8O{1~zbR)ibdQ%*QFviex@Noveee1zFY&len%9X=Km&&KO z#}h;D;vV-RK=pb^o~w)LJ%AzyU2^u)R+%%BVYVfOtq>a5mh%-0s(u!!8OWQ74qTM>`A#E+a^O)m7jd+~yZ z;h!BnkLBlYpFP6HJ+nv!64AYQ{1E}oc@0PwG3cD@IK!dg91tmpyA@CGyz=I4Nk*eC z0n%b4Cib`Mm9Ypf+ZT6EH7ssZ0LosdLl>t*8B9k6G`&rAAu;X!?*Feo@wgv+C^Er#EUb?t)YjP=M7J(=rdT7jEpv z$@6=WRw_fRd;~V{Y{jET55JE$KL$n+1l+lM6RoRi5EN&FnFYQ$`Q<_EKHMUL$!OH0 zP3i$;foXZh%sYJ0Id_?XN^rXB$+QL?#)Epvaw9Q=r9OH9o*VqW3OXJtNCn$+T4Osl7O*lIxP=>|ll0S7xZPs$v}^*8M`KrMS> zwMyEoUJ)FYIlmiF0|A<=jhJFBHZf+<4H*H-sNsM#38D_|mcq?=O~mDW`pV|-124Dl zoE3UTND6?)98ZWmNMmjl{NfTovfiR$4> zN|>dC#4;;HWg8NpfhWdIG9StTQ5nK`+$kbB1+7xbeg>jTMrBI{TaSZ<8A)@`YEA|NpiDOAKS&=99DZ~pOg z-tAi7Jh_GBGDqajoj}>@aktWh|M7Sc(C%igTqIZV3`T$|$OuqmwgFd=2q+{&^{do6 z3FC2xtA{tVISchg)kS7X1-nswwR#QE@`Fi3id$`-WX#hR0i|(3wc6zST9{g>L4XF| z9u*)_PpeV@qBKm|P63D)jlBN&DL_4tczl|(C=AQC%(57AUF|9~bgu?@L zLWQmfC?ZQAUZKh-JSG_q4Ne>O6g>gbn0IKR7!x_ z{+ECfDkWm0noweeFu5+gLIxf833Z?>ruH=l!~=7|Ql7yK#o{t~t{%cOj9yxf`qRK( zd3#8F$Kpud$0eQvS!%tOsGg*`n%#iZ*o0}yw;c%(WwE6MNT3WS2I?H{W-WSR zf%dNf3P#(ee-3En<~iK8ML_Yh2vWHL;)^X2mZ8I4?2vDStqLjV`b?_Rcw3UUL@Lg& zdGD57rN=_kjK0$`|NUQk4bYCGjfXs?|Vbt|}i=bpx_p_!^J| zyz0)Q#x}o&1KRYHapikJ4Z;6ZL9ZyQiGWB!3A4C*$jvH@kXT}e2ss7mVC6#rQ=Nv9 zg06D~iGW%N&>};z`cA6SXW?lkPi;r_`%CWuI#u<10z_$3OCSJLAZk*8)}#(v0}(mH zx&cuPsACP*0ln|iS9|6=;Af7q*1u~1nFfKw>%wlonS|XtMGAWYETo9A( z2#?7shaDXa{}`04Q=U*^$Z{4~cjYd$60#PU3#m0mT?r+2U12lr=h@0_f7P3BQTLf% zVC5T;CAl#z&!{UhTTci{*AfB~C4yI|`UB^Q1E+D2bJ~OEXE)gpARSRbye(0vNYT3m zXDDOB)WN83PQ|&4$HjQkQ}2j?>fZ#UxbM}gmpFFn01D?!!>CCEFg8jV!D(t#2X!l` zOq&21i-6*CN5W&0+UFMTYJUisVfe?$T$AM72HAUhXn5_SC zufKWd7q9a>-#87Az&L3~V4V3?zp2L8Wuf}JNBXMzyZWen$Xk-y6%jRs3r7m+1c)n$ z#~}_#F#(W>Zx6@M(m;q@1rcecShlVP-#of6)<6RFSR4>{G5#~Z@AloV(bQ6iNwJ3T zPZ)~uEOkl>nuyNVKx_#CD$}Bhx*Jfb0+74E+D;W$mEZC{D?mQzz2r&<&CGgFr;J*U z;nPd)bY-!hzOVFu^x=j+J?848^r3CQM<4NlQ}<5v;ee`FMCBJYj}jF`0Ud9uh`Sqy zme#;QNvKSaAXq*KA<2eVwXqSm?p#M%lOjl8`}z_VwwGgkq!#=V29a8*DK66%6*HZn za4_7x_Ad@%qVq5Z1xB?(tp@$rS3tP_{&#$ivCj|e`+BKzap;)5 zrqNw9YjnhFJA*@-uYi;(&X6P`B;M#!AQ$IJJQD_`sv0+}zGFa2(HAXwt|8r)Dy*7P`CKEOrm60c zl4}_tDp!v>b2Anxw-JJpjqdowsCkEky#C77Tl!H~r9Sc5!v}|D7|aVwRlggNrHdJ5 z_DGxUinOZXNUgF%${Z`CR9Rr=Tyt8uIZ|sZ$SjdoV~zAWTcp?8Bct8{@_Hv^);lAk z)&=P`F7#YyB+nX7Z3QVN-qF9XOqAZCuSN9<&$s+jbhb_Sqy)*U zDJdF=$umPr`3T78cq6k)26>ei(&vmudeul|%pJ+()$T%O?P!)&=gG3`y;)AfSmZWL zKwhH{@)~^UXJ6#h`5?1q98zbyBd*B)<+MEWO_6z0znEOZzrAzc+2CId$f*i6_nW5o zo#WWQGUo|>d6kQa(^L_ZqK~L73&a&TQFIztR6uVN<}89$!1gW=|@3`g&Qu=g0K z0Q9l2me?(XD0@S(N)$AW`*U%2`Vpf+PLMo%6>M$ez3#>P%DzhX4}9S2mqDnuf$ zvwSeUOr1?DQbS~(DuUC8!7qLoWD!F#!gmn4*dW2`2Y|w6xH!8_RK8CxDU9&%53kU{ z7#BTEj9(L`s$fEtCYu;$@t*-GtI6s?{48}wkf>$Qpk7LynoV{1P9KIb!9(FXVTfSm z`U3@}jBp+uFy#IS|9*mZm@<4~hhb8pIw??t&T8%KH} zd_5oL3r;E?>o_S+_wK}4WnuEnVbsMF)Ld->tAkLv7D7_>Sm1Q43l>fv^m}&!@vnm~ z9P_t~T8qwvN=<<)D85`%98bg)NvPkFkh^Gfc?JzTHhlOezxz8t`R*zqDf*vJnW@@E za9DJ{4n0So%mC4OdMq?uuPY!QT1^c$sj_MZw6pxRo)U&j?vrCXr?D0!9*ODHqx znqFeu6<=a4#1`1I$uo@Zjtw6=NlBrCKCgQ^+h0ZH8u-Q*m_ALL<5O4m2Hnvj*wc>ks?G z2G34C@E_ZLZKgG#r9IGlue=QUde;x0&cEf}-^JJmm+rK2tvlk-Rei)^T;Dlp|J|h@ z^m+gPwY2+#Jp|DwyKZ{cr#D)vv<3?RYtkmp$A#M_scJjzTH4xNh5}{7UZcq4B{H*L zAbrC#D0V!CY|}%?3-2R&-YsP7u%Pze_gYZ0)AsviO2LRhr%`gXg-+>Ct8*6~gsguK z#ocE}dD{&cL$1D$Nahk|Gw0#KoMAkBPxj3Dsk7U=vbNLla&#_VG^p9{qm1aoZlq4X z^Cs&0V~Cl@NMI=QP@|Gq(}0&K#8vp=Jm%rVTTdK%=7x@k&di334&4O8uk5(sqd^Q_ zOZx$2J`a)j^giNo8q0Qj^e(~C7xVD# zBMhtX326_pU@IUwD0ag^9a#=8X??`t?%Wdb?IN60 zFU5H#fIN^s2jJ9e3jX;b?0@8i_S^2*cGVr<-K)a(^8r}3&jQ&SjUUMBc)a)44{XT~ zKlMpG`6^Vs@6j}zdy)c%>BZfb9XR-87LGiduL=0*b8mD$U4(;==VR}EFYLPIfh||3 zVcW$8=vDXN$A_m-+HT1LV5F7&O24xA)DN34P8+uS&iwD&Z+kG?ZaSg;#ynhn(2U*J z7o+`-JNDe0iH`fTu;=b9%D4zSZ+fEb+6-*E=!SLQIb-b!2XtJJqGfL^@-~|yYrPpH zDuess3cWG!rE%Avcd_4iaawO{mmOL!+JO`WtzC|EbjIc@u9RgrY^6+ZxiSr#F1cYt z*Hq9wXgcGBHOK8yeb|PmV}+va76iZo87&rwFVj~C$j1Kv_X7KXwa2GL)gQN2uQ_Rr zhSN5hW^6noMAKP&G@o-s%Q+|V$!I=1Src^QDMwA@bUv3^5H)(M zP^vZL9y0!PAE}eBBhux_lLe!GGl+Xg{I`3M7LZhacjB4%!EKjCzGcV#KFV+LG)%3v zI;ND*|Lq0kdwSp$Z!~oArwQR+?+d_E@5L2TL6@q&1o#akT(Z zndVr!!swDu+<3e83k&~x@|pkk9~tTM6EElYY`)@nyKIl?+yR4VyX-Qw{l?suT{mZ{ z*(PoMemYaUV^Z&mLId~RH+*9o4=!dxr6P}$-AS-}?D(>a?2OwLAAWHcK?T+U+_$Bd0H+2K@Gd&HJ$ zK4(wOI~lDPoJn0=(R6Yu>N}^R`de32>~TT)&M7Evb3nlsA@Vj^BY(3Mxh3+qS|V?Y zS)T$?52Vx^VMU=nGe2_lzTo7E%M}fVOhKEu=9<*CMoe%rr^|hz&R+)vpssD+Kj*gE zoK!aQ8D*m(lI!@IMw`3ro0N>T_T||djOZc8DBfj?(%oh#?=Xj|!-6<&si6w1GO7+M zjn0?06YqERQKxVl%Y9R1t~VwM89`oa2yq!7%hL^**`d070_B|8lzKkH2G1srz%hZc z-vXeM1z>wp(O|$R*76~zMvKYkFkT_Lsj1cWuKetqCS~lPR({m>_c<1>YplWIm~qkCnxIgemozS;3?C_$TSDl-C+CIqbEq zCX_fsCQz<-ZtC0-|1ux|s}J~mn%!!9EOm_mLm5F*l>q_Z!7FrBUUG$9S?XE?CUcz; zh+W9jM#wgUl9lC@J$q4J1?cr4@Xv zC^A5pf=2*!cl(LQ$CD~ia@YZ9!d8g!mvYVuy$9F>H9HpkDSMspa7w*ClU!#&57I|; z9v8DiM&-*^2@8_z^qKT#L$FoI-DHfStwL-&CZ*-U8CoX1!GAY~{=Iqo3RiC*M)Te% zDA$;id>WDp5j6|+5t7PNb`n)s9@BLZQJyhcxS9^XxCB1Yr?ld~MQ3=p1X3Q^FgE&dXYoUL}|I+_tJ#&iuIehCwfO`ksO(*Xf!Iu!m%R*UUE zc`c7&Ga^~VLu3w@nG>d4k)*QEOswKDiUvL{1B{Tj(Hi$2Uqb!HG^7>#;p~OOc=P79 zChz@$_w>BNkrTU-kg)*u>vM4V`UyzNjY!!H2ml`u>60+if7F(xV!hNvs&+;T+b6~v ziUmjwjtWJ?|2$v-+3W20CROujrO0C?6cJe*mZud`l`u20g3C~UKtrST7I@TsLlfxT zpRS;)H68_3;ka<=6nb9w;KbH)cE1ndBo=P5nsyo76C}JWC3gl zkZ|)7R`ck4S(P$IA`2jqf51ZY)h7cRWNo2CvsxUsCspx?K0I1%ar*#-=vF6`StTc| z=4yI_)CgJY`T+DB+q^gKoI_DyrXeZ{?DVDlFg` zpxfl1z|Bn{y0R*zXb@;9=AXddKX3q-^M_`)IJ8MCx%4Gm#FcQdJVTF}6FhpYq}WnU z=NLKl1}m|d&F23FK!4Ld>HZ1%bv}6cs{5C~`|aAz?+}rx&kisGV2EW3a}9tc@#FJY zx+*F7)U^UyW)lFh;cgcft)T+~P9Ff+?0|L;|Asv#_;@FTt0OHFyh%M6701yR@XhM%MVnb5s%)PZNAfoq&3TkEL;fEkdEzkO3~En?B5L zajKC}rsGQW5L2Ls5XCsgGjK$Mq}V!vK&jd0me&$r8_e+V>GgNy^9cdSsd^Ux+r$@t z1`w6QLrks(A{4f8U!q&(C*hQd2|!A{fWF%Z@g;^#fY_p$Li%998y97*V@6yFM;%+J zhsf-S2uK>I_E@4@oT74;usf*43Le-QL9+r%l?Z3fANpB7e+8iO*8mv2eobTfXUy;&SFz++{h$;;GQAtS4_Vq$VFUj<3*L;5#51F~?1#Tb8y zL3@D2^r?3g|EcHFi5)Jpzy7AMc}bkC!m1Ad^#NOjxKa*H%yGz41rWbaX%y#4_d^YU zS3S>YfW0Mvhat=L#{BRx@Jk$z@C-IFEC35cE8XDk!zmX^g+n5;Y^E!#yej1?&-!I4 z*7HL{U55VF4Vm_IpYfm03*qrZemoxgSt^x&P}~w2oLp(ECMgBm2W%DCBg%Eu?1ZCOGtXt46rC#51~p{aY#0c z`aGt9nm3n+@Jucu(`^teae7Gt>w0my z;V&Ln!n%O8UVy%9OsOf&5Lax5m^@RWkO0AQV|HnW#zhVNjC5 z&QD@+Hdsah<~Spv&<&Cz7l@0TATD-5La7szST==8DxV7Zs{bJOfUMjT(h?8E<+;)F z!Txoy+^oY-%AX;UzQ^EytyJMjQ?gy>e-5`rU&AY8B9^Q)L6}^K6uFI2=56;OsRLQ@nJU84FF?X+ZtcpuQhaRJtXG zD#wv5j=}th(WK?Nm^^zVY^Qx)p{04H1a1Eam)SPchpC-reSv9~ID z7K0BUr^!7&reGoynL827)AeXt>rs#sFm=wD@_tQ^l)HO)28`%k7(JSHS2WS3T=>T6 zVPTl%AOoP_((0%B#uRYWQ3QY;PNAthc*p87(-(~w_s@@*G!OGdG2?n@Z;MccKEjmj zCWXg%N1Cl3@FMN^ww3wT!Rl3Nx>pqNmCoPihHyw&svtvB z==>~sl?Ri&TGShnW^;H!%-D(l5HGuff;fK;5F6Qtr8xR6OL27eUe15#ClL1K1%cH| ruJZJht7Zygm5zVW`osRPe+>Ii)X|D+8y7?e00000NkvXXu0mjf#L-I; literal 0 HcmV?d00001 diff --git a/android/java/app/src/main/res/drawable-xhdpi/ic_launcher.png b/android/java/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d4fb7cd9d868f1d7d9964f1686dcbc018ef9495a GIT binary patch literal 12516 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 literal 0 HcmV?d00001 diff --git a/android/java/app/src/main/res/layout/activity_fragment.xml b/android/java/app/src/main/res/layout/activity_fragment.xml new file mode 100644 index 000000000..6310106fc --- /dev/null +++ b/android/java/app/src/main/res/layout/activity_fragment.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/android/java/app/src/main/res/layout/activity_main.xml b/android/java/app/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..e4b11e3ad --- /dev/null +++ b/android/java/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/android/java/app/src/main/res/values-v11/styles.xml b/android/java/app/src/main/res/values-v11/styles.xml new file mode 100644 index 000000000..3c02242ad --- /dev/null +++ b/android/java/app/src/main/res/values-v11/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/android/java/app/src/main/res/values-v14/styles.xml b/android/java/app/src/main/res/values-v14/styles.xml new file mode 100644 index 000000000..a91fd0372 --- /dev/null +++ b/android/java/app/src/main/res/values-v14/styles.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/android/java/app/src/main/res/values/strings.xml b/android/java/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..f6d4e76fa --- /dev/null +++ b/android/java/app/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + Mapbox GL + + diff --git a/android/java/app/src/main/res/values/styles.xml b/android/java/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..6ce89c7ba --- /dev/null +++ b/android/java/app/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/android/java/build.gradle b/android/java/build.gradle new file mode 100644 index 000000000..e1e165302 --- /dev/null +++ b/android/java/build.gradle @@ -0,0 +1,15 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:0.13.2' + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/android/java/gradle/wrapper/gradle-wrapper.jar b/android/java/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/java/gradlew.bat b/android/java/gradlew.bat new file mode 100644 index 000000000..8a0b282aa --- /dev/null +++ b/android/java/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/java/lib/build.gradle b/android/java/lib/build.gradle new file mode 100644 index 000000000..d6e4f141c --- /dev/null +++ b/android/java/lib/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 19 + buildToolsVersion "21.1.0" + + defaultConfig { + applicationId "com.mapbox.mapboxgl.lib" + minSdkVersion 19 + targetSdkVersion 20 + + ndk { + moduleName "NativeMapView" + } + } + + buildTypes { + debug { + jniDebugBuild true + } + + release { + runProguard false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} + +dependencies { + compile "commons-io:commons-io:2.4" +} diff --git a/android/java/lib/lint.xml b/android/java/lib/lint.xml new file mode 100644 index 000000000..84598b8d4 --- /dev/null +++ b/android/java/lib/lint.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/android/java/lib/src/main/AndroidManifest.xml b/android/java/lib/src/main/AndroidManifest.xml new file mode 100644 index 000000000..b947bc7d0 --- /dev/null +++ b/android/java/lib/src/main/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java new file mode 100644 index 000000000..86d1d50f6 --- /dev/null +++ b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java @@ -0,0 +1,160 @@ +package com.almeros.android.multitouch.gesturedetectors; + +import android.content.Context; +import android.view.MotionEvent; + +/** + * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie + * (code.almeros.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +public abstract class BaseGestureDetector { + protected final Context mContext; + protected boolean mGestureInProgress; + + protected MotionEvent mPrevEvent; + protected MotionEvent mCurrEvent; + + protected float mCurrPressure; + protected float mPrevPressure; + protected long mTimeDelta; + + /** + * This value is the threshold ratio between the previous combined pressure + * and the current combined pressure. When pressure decreases rapidly + * between events the position values can often be imprecise, as it usually + * indicates that the user is in the process of lifting a pointer off of the + * device. This value was tuned experimentally. + */ + protected static final float PRESSURE_THRESHOLD = 0.67f; + + public BaseGestureDetector(Context context) { + mContext = context; + } + + /** + * All gesture detectors need to be called through this method to be able to + * detect gestures. This method delegates work to handler methods + * (handleStartProgressEvent, handleInProgressEvent) implemented in + * extending classes. + * + * @param event + * @return + */ + public boolean onTouchEvent(MotionEvent event) { + final int actionCode = event.getAction() & MotionEvent.ACTION_MASK; + if (!mGestureInProgress) { + handleStartProgressEvent(actionCode, event); + } else { + handleInProgressEvent(actionCode, event); + } + return true; + } + + /** + * Called when the current event occurred when NO gesture is in progress + * yet. The handling in this implementation may set the gesture in progress + * (via mGestureInProgress) or out of progress + * + * @param actionCode + * @param event + */ + protected abstract void handleStartProgressEvent(int actionCode, + MotionEvent event); + + /** + * Called when the current event occurred when a gesture IS in progress. The + * handling in this implementation may set the gesture out of progress (via + * mGestureInProgress). + * + * @param action + * @param event + */ + protected abstract void handleInProgressEvent(int actionCode, + MotionEvent event); + + protected void updateStateByEvent(MotionEvent curr) { + final MotionEvent prev = mPrevEvent; + + // Reset mCurrEvent + if (mCurrEvent != null) { + mCurrEvent.recycle(); + mCurrEvent = null; + } + mCurrEvent = MotionEvent.obtain(curr); + + // Delta time + mTimeDelta = curr.getEventTime() - prev.getEventTime(); + + // Pressure + mCurrPressure = curr.getPressure(curr.getActionIndex()); + mPrevPressure = prev.getPressure(prev.getActionIndex()); + } + + protected void resetState() { + if (mPrevEvent != null) { + mPrevEvent.recycle(); + mPrevEvent = null; + } + if (mCurrEvent != null) { + mCurrEvent.recycle(); + mCurrEvent = null; + } + mGestureInProgress = false; + } + + /** + * Returns {@code true} if a gesture is currently in progress. + * + * @return {@code true} if a gesture is currently in progress, {@code false} + * otherwise. + */ + public boolean isInProgress() { + return mGestureInProgress; + } + + /** + * Return the time difference in milliseconds between the previous accepted + * GestureDetector event and the current GestureDetector event. + * + * @return Time difference since the last move event in milliseconds. + */ + public long getTimeDelta() { + return mTimeDelta; + } + + /** + * Return the event time of the current GestureDetector event being + * processed. + * + * @return Current GestureDetector event time in milliseconds. + */ + public long getEventTime() { + return mCurrEvent.getEventTime(); + } + +} diff --git a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java new file mode 100644 index 000000000..0136183ed --- /dev/null +++ b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java @@ -0,0 +1,188 @@ +package com.almeros.android.multitouch.gesturedetectors; + +import android.content.Context; +import android.graphics.PointF; +import android.view.MotionEvent; + +/** + * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie + * (code.almeros.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +public class MoveGestureDetector extends BaseGestureDetector { + + /** + * Listener which must be implemented which is used by MoveGestureDetector + * to perform callbacks to any implementing class which is registered to a + * MoveGestureDetector via the constructor. + * + * @see MoveGestureDetector.SimpleOnMoveGestureListener + */ + public interface OnMoveGestureListener { + public boolean onMove(MoveGestureDetector detector); + + public boolean onMoveBegin(MoveGestureDetector detector); + + public void onMoveEnd(MoveGestureDetector detector); + } + + /** + * Helper class which may be extended and where the methods may be + * implemented. This way it is not necessary to implement all methods of + * OnMoveGestureListener. + */ + public static class SimpleOnMoveGestureListener implements + OnMoveGestureListener { + public boolean onMove(MoveGestureDetector detector) { + return false; + } + + public boolean onMoveBegin(MoveGestureDetector detector) { + return true; + } + + public void onMoveEnd(MoveGestureDetector detector) { + // Do nothing, overridden implementation may be used + } + } + + private static final PointF FOCUS_DELTA_ZERO = new PointF(); + + private final OnMoveGestureListener mListener; + + private PointF mCurrFocusInternal; + private PointF mPrevFocusInternal; + private PointF mFocusExternal = new PointF(); + private PointF mFocusDeltaExternal = new PointF(); + + public MoveGestureDetector(Context context, OnMoveGestureListener listener) { + super(context); + mListener = listener; + } + + @Override + protected void handleStartProgressEvent(int actionCode, MotionEvent event) { + switch (actionCode) { + case MotionEvent.ACTION_DOWN: + resetState(); // In case we missed an UP/CANCEL event + + mPrevEvent = MotionEvent.obtain(event); + mTimeDelta = 0; + + updateStateByEvent(event); + break; + + case MotionEvent.ACTION_MOVE: + mGestureInProgress = mListener.onMoveBegin(this); + break; + } + } + + @Override + protected void handleInProgressEvent(int actionCode, MotionEvent event) { + switch (actionCode) { + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mListener.onMoveEnd(this); + resetState(); + break; + + case MotionEvent.ACTION_MOVE: + updateStateByEvent(event); + + // Only accept the event if our relative pressure is within + // a certain limit. This can help filter shaky data as a + // finger is lifted. + if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) { + final boolean updatePrevious = mListener.onMove(this); + if (updatePrevious) { + mPrevEvent.recycle(); + mPrevEvent = MotionEvent.obtain(event); + } + } + break; + } + } + + protected void updateStateByEvent(MotionEvent curr) { + super.updateStateByEvent(curr); + + final MotionEvent prev = mPrevEvent; + + // Focus intenal + mCurrFocusInternal = determineFocalPoint(curr); + mPrevFocusInternal = determineFocalPoint(prev); + + // Focus external + // - Prevent skipping of focus delta when a finger is added or removed + boolean mSkipNextMoveEvent = prev.getPointerCount() != curr + .getPointerCount(); + mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO + : new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x, + mCurrFocusInternal.y - mPrevFocusInternal.y); + + // - Don't directly use mFocusInternal (or skipping will occur). Add + // unskipped delta values to mFocusExternal instead. + mFocusExternal.x += mFocusDeltaExternal.x; + mFocusExternal.y += mFocusDeltaExternal.y; + } + + /** + * Determine (multi)finger focal point (a.k.a. center point between all + * fingers) + * + * @param MotionEvent + * e + * @return PointF focal point + */ + private PointF determineFocalPoint(MotionEvent e) { + // Number of fingers on screen + final int pCount = e.getPointerCount(); + float x = 0.0f; + float y = 0.0f; + + for (int i = 0; i < pCount; i++) { + x += e.getX(i); + y += e.getY(i); + } + + return new PointF(x / pCount, y / pCount); + } + + public float getFocusX() { + return mFocusExternal.x; + } + + public float getFocusY() { + return mFocusExternal.y; + } + + public PointF getFocusDelta() { + return mFocusDeltaExternal; + } + +} diff --git a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java new file mode 100644 index 000000000..124fe8509 --- /dev/null +++ b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java @@ -0,0 +1,180 @@ +package com.almeros.android.multitouch.gesturedetectors; + +import android.content.Context; +import android.view.MotionEvent; + +/** + * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie + * (code.almeros.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +public class RotateGestureDetector extends TwoFingerGestureDetector { + + /** + * Listener which must be implemented which is used by RotateGestureDetector + * to perform callbacks to any implementing class which is registered to a + * RotateGestureDetector via the constructor. + * + * @see RotateGestureDetector.SimpleOnRotateGestureListener + */ + public interface OnRotateGestureListener { + public boolean onRotate(RotateGestureDetector detector); + + public boolean onRotateBegin(RotateGestureDetector detector); + + public void onRotateEnd(RotateGestureDetector detector); + } + + /** + * Helper class which may be extended and where the methods may be + * implemented. This way it is not necessary to implement all methods of + * OnRotateGestureListener. + */ + public static class SimpleOnRotateGestureListener implements + OnRotateGestureListener { + public boolean onRotate(RotateGestureDetector detector) { + return false; + } + + public boolean onRotateBegin(RotateGestureDetector detector) { + return true; + } + + public void onRotateEnd(RotateGestureDetector detector) { + // Do nothing, overridden implementation may be used + } + } + + private final OnRotateGestureListener mListener; + private boolean mSloppyGesture; + + public RotateGestureDetector(Context context, + OnRotateGestureListener listener) { + super(context); + mListener = listener; + } + + @Override + protected void handleStartProgressEvent(int actionCode, MotionEvent event) { + switch (actionCode) { + case MotionEvent.ACTION_POINTER_DOWN: + // At least the second finger is on screen now + + resetState(); // In case we missed an UP/CANCEL event + mPrevEvent = MotionEvent.obtain(event); + mTimeDelta = 0; + + updateStateByEvent(event); + + // See if we have a sloppy gesture + mSloppyGesture = isSloppyGesture(event); + if (!mSloppyGesture) { + // No, start gesture now + mGestureInProgress = mListener.onRotateBegin(this); + } + break; + + case MotionEvent.ACTION_MOVE: + if (!mSloppyGesture) { + break; + } + + // See if we still have a sloppy gesture + mSloppyGesture = isSloppyGesture(event); + if (!mSloppyGesture) { + // No, start normal gesture now + mGestureInProgress = mListener.onRotateBegin(this); + } + + break; + + case MotionEvent.ACTION_POINTER_UP: + if (!mSloppyGesture) { + break; + } + + break; + } + } + + @Override + protected void handleInProgressEvent(int actionCode, MotionEvent event) { + switch (actionCode) { + case MotionEvent.ACTION_POINTER_UP: + // Gesture ended but + updateStateByEvent(event); + + if (!mSloppyGesture) { + mListener.onRotateEnd(this); + } + + resetState(); + break; + + case MotionEvent.ACTION_CANCEL: + if (!mSloppyGesture) { + mListener.onRotateEnd(this); + } + + resetState(); + break; + + case MotionEvent.ACTION_MOVE: + updateStateByEvent(event); + + // Only accept the event if our relative pressure is within + // a certain limit. This can help filter shaky data as a + // finger is lifted. + if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) { + final boolean updatePrevious = mListener.onRotate(this); + if (updatePrevious) { + mPrevEvent.recycle(); + mPrevEvent = MotionEvent.obtain(event); + } + } + break; + } + } + + @Override + protected void resetState() { + super.resetState(); + mSloppyGesture = false; + } + + /** + * Return the rotation difference from the previous rotate event to the + * current event. + * + * @return The current rotation //difference in degrees. + */ + public float getRotationDegreesDelta() { + double diffRadians = Math.atan2(mPrevFingerDiffY, mPrevFingerDiffX) + - Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX); + return (float) (diffRadians * 180.0 / Math.PI); + } +} diff --git a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java new file mode 100644 index 000000000..254597105 --- /dev/null +++ b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java @@ -0,0 +1,213 @@ +package com.almeros.android.multitouch.gesturedetectors; + +import android.content.Context; +import android.view.MotionEvent; + +/** + * @author Robert Nordan (robert.nordan@norkart.no) + * + * Copyright (c) 2013, Norkart AS + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +public class ShoveGestureDetector extends TwoFingerGestureDetector { + + /** + * Listener which must be implemented which is used by ShoveGestureDetector + * to perform callbacks to any implementing class which is registered to a + * ShoveGestureDetector via the constructor. + * + * @see ShoveGestureDetector.SimpleOnShoveGestureListener + */ + public interface OnShoveGestureListener { + public boolean onShove(ShoveGestureDetector detector); + + public boolean onShoveBegin(ShoveGestureDetector detector); + + public void onShoveEnd(ShoveGestureDetector detector); + } + + /** + * Helper class which may be extended and where the methods may be + * implemented. This way it is not necessary to implement all methods of + * OnShoveGestureListener. + */ + public static class SimpleOnShoveGestureListener implements + OnShoveGestureListener { + public boolean onShove(ShoveGestureDetector detector) { + return false; + } + + public boolean onShoveBegin(ShoveGestureDetector detector) { + return true; + } + + public void onShoveEnd(ShoveGestureDetector detector) { + // Do nothing, overridden implementation may be used + } + } + + private float mPrevAverageY; + private float mCurrAverageY; + + private final OnShoveGestureListener mListener; + private boolean mSloppyGesture; + + public ShoveGestureDetector(Context context, OnShoveGestureListener listener) { + super(context); + mListener = listener; + } + + @Override + protected void handleStartProgressEvent(int actionCode, MotionEvent event) { + switch (actionCode) { + case MotionEvent.ACTION_POINTER_DOWN: + // At least the second finger is on screen now + + resetState(); // In case we missed an UP/CANCEL event + mPrevEvent = MotionEvent.obtain(event); + mTimeDelta = 0; + + updateStateByEvent(event); + + // See if we have a sloppy gesture + mSloppyGesture = isSloppyGesture(event); + if (!mSloppyGesture) { + // No, start gesture now + mGestureInProgress = mListener.onShoveBegin(this); + } + break; + + case MotionEvent.ACTION_MOVE: + if (!mSloppyGesture) { + break; + } + + // See if we still have a sloppy gesture + mSloppyGesture = isSloppyGesture(event); + if (!mSloppyGesture) { + // No, start normal gesture now + mGestureInProgress = mListener.onShoveBegin(this); + } + + break; + + case MotionEvent.ACTION_POINTER_UP: + if (!mSloppyGesture) { + break; + } + + break; + } + } + + @Override + protected void handleInProgressEvent(int actionCode, MotionEvent event) { + switch (actionCode) { + case MotionEvent.ACTION_POINTER_UP: + // Gesture ended but + updateStateByEvent(event); + + if (!mSloppyGesture) { + mListener.onShoveEnd(this); + } + + resetState(); + break; + + case MotionEvent.ACTION_CANCEL: + if (!mSloppyGesture) { + mListener.onShoveEnd(this); + } + + resetState(); + break; + + case MotionEvent.ACTION_MOVE: + updateStateByEvent(event); + + // Only accept the event if our relative pressure is within + // a certain limit. This can help filter shaky data as a + // finger is lifted. Also check that shove is meaningful. + if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD + && Math.abs(getShovePixelsDelta()) > 0.5f) { + final boolean updatePrevious = mListener.onShove(this); + if (updatePrevious) { + mPrevEvent.recycle(); + mPrevEvent = MotionEvent.obtain(event); + } + } + break; + } + } + + @Override + protected void resetState() { + super.resetState(); + mSloppyGesture = false; + mPrevAverageY = 0.0f; + mCurrAverageY = 0.0f; + } + + @Override + protected void updateStateByEvent(MotionEvent curr) { + super.updateStateByEvent(curr); + + final MotionEvent prev = mPrevEvent; + float py0 = prev.getY(0); + float py1 = prev.getY(1); + mPrevAverageY = (py0 + py1) / 2.0f; + + float cy0 = curr.getY(0); + float cy1 = curr.getY(1); + mCurrAverageY = (cy0 + cy1) / 2.0f; + } + + @Override + protected boolean isSloppyGesture(MotionEvent event) { + boolean sloppy = super.isSloppyGesture(event); + if (sloppy) + return true; + + // If it's not traditionally sloppy, we check if the angle between + // fingers + // is acceptable. + double angle = Math.abs(Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX)); + // about 20 degrees, left or right + return !((0.0f < angle && angle < 0.35f) || 2.79f < angle + && angle < Math.PI); + } + + /** + * Return the distance in pixels from the previous shove event to the + * current event. + * + * @return The current distance in pixels. + */ + public float getShovePixelsDelta() { + return mCurrAverageY - mPrevAverageY; + } +} diff --git a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java new file mode 100644 index 000000000..5215a91bb --- /dev/null +++ b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java @@ -0,0 +1,228 @@ +package com.almeros.android.multitouch.gesturedetectors; + +import android.content.Context; +import android.graphics.PointF; +import android.util.DisplayMetrics; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +/** + * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie + * (code.almeros.com) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +public abstract class TwoFingerGestureDetector extends BaseGestureDetector { + + private final float mEdgeSlop; + private float mRightSlopEdge; + private float mBottomSlopEdge; + + protected float mPrevFingerDiffX; + protected float mPrevFingerDiffY; + protected float mCurrFingerDiffX; + protected float mCurrFingerDiffY; + + private float mCurrLen; + private float mPrevLen; + + private PointF mFocus; + + public TwoFingerGestureDetector(Context context) { + super(context); + + ViewConfiguration config = ViewConfiguration.get(context); + mEdgeSlop = config.getScaledEdgeSlop(); + } + + @Override + protected abstract void handleStartProgressEvent(int actionCode, + MotionEvent event); + + @Override + protected abstract void handleInProgressEvent(int actionCode, + MotionEvent event); + + protected void updateStateByEvent(MotionEvent curr) { + super.updateStateByEvent(curr); + + final MotionEvent prev = mPrevEvent; + + mCurrLen = -1; + mPrevLen = -1; + + // Previous + final float px0 = prev.getX(0); + final float py0 = prev.getY(0); + final float px1 = prev.getX(1); + final float py1 = prev.getY(1); + final float pvx = px1 - px0; + final float pvy = py1 - py0; + mPrevFingerDiffX = pvx; + mPrevFingerDiffY = pvy; + + // Current + final float cx0 = curr.getX(0); + final float cy0 = curr.getY(0); + final float cx1 = curr.getX(1); + final float cy1 = curr.getY(1); + final float cvx = cx1 - cx0; + final float cvy = cy1 - cy0; + mCurrFingerDiffX = cvx; + mCurrFingerDiffY = cvy; + mFocus = determineFocalPoint(curr); + } + + /** + * Return the current distance between the two pointers forming the gesture + * in progress. + * + * @return Distance between pointers in pixels. + */ + public float getCurrentSpan() { + if (mCurrLen == -1) { + final float cvx = mCurrFingerDiffX; + final float cvy = mCurrFingerDiffY; + mCurrLen = (float) Math.sqrt(cvx * cvx + cvy * cvy); + } + return mCurrLen; + } + + /** + * Return the previous distance between the two pointers forming the gesture + * in progress. + * + * @return Previous distance between pointers in pixels. + */ + public float getPreviousSpan() { + if (mPrevLen == -1) { + final float pvx = mPrevFingerDiffX; + final float pvy = mPrevFingerDiffY; + mPrevLen = (float) Math.sqrt(pvx * pvx + pvy * pvy); + } + return mPrevLen; + } + + /** + * MotionEvent has no getRawX(int) method; simulate it pending future API + * approval. + * + * @param event + * @param pointerIndex + * @return + */ + protected static float getRawX(MotionEvent event, int pointerIndex) { + float offset = event.getX() - event.getRawX(); + if (pointerIndex < event.getPointerCount()) { + return event.getX(pointerIndex) + offset; + } + return 0.0f; + } + + /** + * MotionEvent has no getRawY(int) method; simulate it pending future API + * approval. + * + * @param event + * @param pointerIndex + * @return + */ + protected static float getRawY(MotionEvent event, int pointerIndex) { + float offset = event.getY() - event.getRawY(); + if (pointerIndex < event.getPointerCount()) { + return event.getY(pointerIndex) + offset; + } + return 0.0f; + } + + /** + * Check if we have a sloppy gesture. Sloppy gestures can happen if the edge + * of the user's hand is touching the screen, for example. + * + * @param event + * @return + */ + protected boolean isSloppyGesture(MotionEvent event) { + // As orientation can change, query the metrics in touch down + DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); + mRightSlopEdge = metrics.widthPixels - mEdgeSlop; + mBottomSlopEdge = metrics.heightPixels - mEdgeSlop; + + final float edgeSlop = mEdgeSlop; + final float rightSlop = mRightSlopEdge; + final float bottomSlop = mBottomSlopEdge; + + final float x0 = event.getRawX(); + final float y0 = event.getRawY(); + final float x1 = getRawX(event, 1); + final float y1 = getRawY(event, 1); + + boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlop + || y0 > bottomSlop; + boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlop + || y1 > bottomSlop; + + if (p0sloppy && p1sloppy) { + return true; + } else if (p0sloppy) { + return true; + } else if (p1sloppy) { + return true; + } + return false; + } + + /** + * Determine (multi)finger focal point (a.k.a. center point between all + * fingers) + * + * @param MotionEvent + * e + * @return PointF focal point + */ + public static PointF determineFocalPoint(MotionEvent e) { + // Number of fingers on screen + final int pCount = e.getPointerCount(); + float x = 0.0f; + float y = 0.0f; + + for (int i = 0; i < pCount; i++) { + x += e.getX(i); + y += e.getY(i); + } + + return new PointF(x / pCount, y / pCount); + } + + public float getFocusX() { + return mFocus.x; + } + + public float getFocusY() { + return mFocus.y; + } + +} diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLat.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLat.java new file mode 100644 index 000000000..87260c2a9 --- /dev/null +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLat.java @@ -0,0 +1,91 @@ +package com.mapbox.mapboxgl.lib; + +import android.os.Parcel; +import android.os.Parcelable; + +public class LonLat implements Parcelable { + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public LonLat createFromParcel(Parcel in) { + return new LonLat(in); + } + + public LonLat[] newArray(int size) { + return new LonLat[size]; + } + }; + + private double lon; + private double lat; + + public LonLat(double lon, double lat) { + this.lon = lon; + this.lat = lat; + } + + private LonLat(Parcel in) { + lon = in.readDouble(); + lat = in.readDouble(); + } + + public double getLon() { + return lon; + } + + public void setLon(double lon) { + this.lon = lon; + } + + public double getLat() { + return lat; + } + + public void setLat(double lat) { + this.lat = lat; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(lat); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(lon); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LonLat other = (LonLat) obj; + if (Double.doubleToLongBits(lat) != Double.doubleToLongBits(other.lat)) + return false; + if (Double.doubleToLongBits(lon) != Double.doubleToLongBits(other.lon)) + return false; + return true; + } + + @Override + public String toString() { + return "LonLat [lon=" + lon + ", lat=" + lat + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeDouble(lon); + out.writeDouble(lat); + } + +} diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLatZoom.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLatZoom.java new file mode 100644 index 000000000..d20cccd47 --- /dev/null +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLatZoom.java @@ -0,0 +1,124 @@ +package com.mapbox.mapboxgl.lib; + +import android.os.Parcel; +import android.os.Parcelable; + +public class LonLatZoom implements Parcelable { + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public LonLatZoom createFromParcel(Parcel in) { + return new LonLatZoom(in); + } + + public LonLatZoom[] newArray(int size) { + return new LonLatZoom[size]; + } + }; + + private double lon; + private double lat; + private double zoom; + + public LonLatZoom(double lon, double lat, double zoom) { + this.lon = lon; + this.lat = lat; + this.zoom = zoom; + } + + public LonLatZoom(LonLat lonLat, double zoom) { + this.lon = lonLat.getLon(); + this.lat = lonLat.getLat(); + this.zoom = zoom; + } + + private LonLatZoom(Parcel in) { + lon = in.readDouble(); + lat = in.readDouble(); + zoom = in.readDouble(); + } + + public double getLon() { + return lon; + } + + public void setLon(double lon) { + this.lon = lon; + } + + public double getLat() { + return lat; + } + + public void setLat(double lat) { + this.lat = lat; + } + + public double getZoom() { + return zoom; + } + + public void setZoom(double zoom) { + this.zoom = zoom; + } + + public LonLat getLonLat() { + return new LonLat(lon, lat); + } + + public void setLonLat(LonLat lonLat) { + this.lon = lonLat.getLon(); + this.lat = lonLat.getLat(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(lat); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(lon); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(zoom); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LonLatZoom other = (LonLatZoom) obj; + if (Double.doubleToLongBits(lat) != Double.doubleToLongBits(other.lat)) + return false; + if (Double.doubleToLongBits(lon) != Double.doubleToLongBits(other.lon)) + return false; + if (Double.doubleToLongBits(zoom) != Double + .doubleToLongBits(other.zoom)) + return false; + return true; + } + + @Override + public String toString() { + return "LonLatZoom [lon=" + lon + ", lat=" + lat + ", zoom=" + zoom + + "]"; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeDouble(lon); + out.writeDouble(lat); + out.writeDouble(zoom); + } + +} diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapFragment.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapFragment.java new file mode 100644 index 000000000..1263ac56e --- /dev/null +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapFragment.java @@ -0,0 +1,150 @@ +package com.mapbox.mapboxgl.lib; + +import android.app.Activity; +import android.app.Fragment; +import android.content.res.TypedArray; +import android.os.Bundle; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +public class MapFragment extends Fragment { + + // + // Static members + // + + // Tag used for logging + private static final String TAG = "MapFragment"; + + // The map + private MapView mMapView; + + // The style attrs to load into the map + AttributeSet mAttrs; + + // + // Lifecycle events + // + + // Called when the fragment is created + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + Log.v(TAG, "onCreateView"); + + // Create the map + mMapView = new MapView(getActivity()); + + // Load the attributes + TypedArray typedArray = getActivity().obtainStyledAttributes(mAttrs, + R.styleable.MapFragment, 0, 0); + try { + double centerLongitude = typedArray.getFloat( + R.styleable.MapFragment_centerLongitude, 0.0f); + double centerLatitude = typedArray.getFloat( + R.styleable.MapFragment_centerLatitude, 0.0f); + LonLat centerCoordinate = new LonLat(centerLongitude, + centerLatitude); + mMapView.setCenterCoordinate(centerCoordinate); + mMapView.setDirection(typedArray.getFloat( + R.styleable.MapFragment_direction, 0.0f)); + mMapView.setZoomLevel(typedArray.getFloat( + R.styleable.MapFragment_zoomLevel, 0.0f)); + mMapView.setZoomEnabled(typedArray.getBoolean( + R.styleable.MapFragment_zoomEnabled, true)); + mMapView.setScrollEnabled(typedArray.getBoolean( + R.styleable.MapFragment_scrollEnabled, true)); + mMapView.setRotateEnabled(typedArray.getBoolean( + R.styleable.MapFragment_rotateEnabled, true)); + mMapView.setDebugActive(typedArray.getBoolean( + R.styleable.MapFragment_debugActive, false)); + } finally { + typedArray.recycle(); + } + + // Need to pass on any saved state to the map + mMapView.onCreate(savedInstanceState); + + // Return the map as the root view + return mMapView; + } + + // Called when the fragment is destroyed + @Override + public void onDestroyView() { + super.onDestroyView(); + Log.v(TAG, "onDestroyView"); + + // Need to pass on to view + mMapView.onDestroy(); + mMapView = null; + } + + // Called to load any fragment attributes set in the activity's layout + @Override + public void onInflate(Activity activity, AttributeSet attrs, + Bundle savedInstanceState) { + super.onInflate(activity, attrs, savedInstanceState); + Log.v(TAG, "onInflate"); + + // TODO seems this is called before onCreateView + // Kinda strange, is it supposed to be like this? + // Need to keep a copy of these and pass on to MapView later + + mAttrs = attrs; + } + + // Called when the fragment is visible + @Override + public void onStart() { + super.onStart(); + Log.v(TAG, "onStart"); + + // Need to pass on to view + mMapView.onStart(); + } + + // Called when the fragment is invisible + @Override + public void onStop() { + super.onStop(); + Log.v(TAG, "onStop"); + + // Need to pass on to view + mMapView.onStop(); + } + + // Called when the fragment is in the background + @Override + public void onPause() { + super.onPause(); + Log.v(TAG, "onPause"); + + // Need to pass on to view + mMapView.onPause(); + } + + // Called when the fragment is no longer in the background + @Override + public void onResume() { + super.onResume(); + Log.v(TAG, "onResume"); + + // Need to pass on to view + mMapView.onResume(); + } + + // Called before fragment is destroyed + @Override + public void onSaveInstanceState(Bundle outState) { + Log.v(TAG, "onSaveInstanceState"); + + // Need to retrieve any saved state from the map + mMapView.onSaveInstanceState(outState); + super.onSaveInstanceState(outState); + } +} diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java new file mode 100644 index 000000000..0f1961d24 --- /dev/null +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -0,0 +1,1178 @@ +package com.mapbox.mapboxgl.lib; + +import java.io.IOException; + +import org.apache.commons.io.Charsets; +import org.apache.commons.io.IOUtils; +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.TypedArray; +import android.graphics.PointF; +import android.os.Bundle; +import android.util.AttributeSet; +import android.util.Log; +import android.view.GestureDetector; +import android.view.InputDevice; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.ZoomButtonsController; + +import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; +import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector; + +// Custom view that shows a Map +// Based on SurfaceView as we use OpenGL ES to render +public class MapView extends SurfaceView { + // TODO try TextureView + + // + // Static members + // + + // Tag used for logging + private static final String TAG = "MapView"; + + // Used for saving instance state + private static final String STATE_CENTER_COORDINATE = "centerCoordinate"; + private static final String STATE_CENTER_DIRECTION = "centerDirection"; + private static final String STATE_ZOOM_LEVEL = "zoomLevel"; + private static final String STATE_ZOOM_ENABLED = "zoomEnabled"; + private static final String STATE_SCROLL_ENABLED = "scrollEnabled"; + private static final String STATE_ROTATE_ENABLED = "rotateEnabled"; + private static final String STATE_DEBUG_ACTIVE = "debugActive"; + + // + // Instance members + // + + // Used to call JNI NativeMapView + private NativeMapView mNativeMapView; + + // Used to style the map + private String mDefaultStyleJSON; + + // Touch gesture detectors + private GestureDetector mGestureDetector; + private ScaleGestureDetector mScaleGestureDetector; + private RotateGestureDetector mRotateGestureDetector; + private boolean mTwoTap = false; + + // Shows zoom buttons + private ZoomButtonsController mZoomButtonsController; + + // Used to track trackball long presses + private TrackballLongPressTimeOut mCurrentTrackballLongPressTimeOut; + + // + // Properties + // + + private boolean mZoomEnabled = true; + private boolean mScrollEnabled = true; + private boolean mRotateEnabled = true; + + // + // Constructors + // + + // Called when no properties are being set from XML + public MapView(Context context) { + super(context); + initialize(context, null, 0); + } + + // Called when properties are being set from XML + public MapView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context, attrs, 0); + } + + // Called when properties are being set from XML + public MapView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialize(context, attrs, defStyle); + } + + // + // Initialization + // + + // Common initialization code goes here + private void initialize(Context context, AttributeSet attrs, int defStyle) { + Log.v(TAG, "initialize"); + + // Check if we are in Eclipse UI editor + if (isInEditMode()) { + // TODO editor does not load properly because + return; + } + + // Load the map style + try { + mDefaultStyleJSON = IOUtils.toString(context.getResources() + // .openRawResource(R.raw.style_leith), Charsets.UTF_8); + .openRawResource(R.raw.style), Charsets.UTF_8); + } catch (IOException e) { + throw new RuntimeException( + "Couldn't load default style JSON resource", e); + } + + // Create the NativeMapView + mNativeMapView = new NativeMapView(this, mDefaultStyleJSON); + mNativeMapView.addDefaultSource(); + // mNativeMapView.addSource("countries", + // "http://192.168.20.103/%d/%d/%d.pbf"); + + // Load the attributes + TypedArray typedArray = context.obtainStyledAttributes(attrs, + R.styleable.MapView, 0, 0); + try { + double centerLongitude = typedArray.getFloat( + R.styleable.MapView_centerLongitude, 0.0f); + double centerLatitude = typedArray.getFloat( + R.styleable.MapView_centerLatitude, 0.0f); + LonLat centerCoordinate = new LonLat(centerLongitude, + centerLatitude); + setCenterCoordinate(centerCoordinate); + setDirection(typedArray.getFloat(R.styleable.MapView_direction, + 0.0f)); + setZoomLevel(typedArray.getFloat(R.styleable.MapView_zoomLevel, + 0.0f)); + setZoomEnabled(typedArray.getBoolean( + R.styleable.MapView_zoomEnabled, true)); + setScrollEnabled(typedArray.getBoolean( + R.styleable.MapView_scrollEnabled, true)); + setRotateEnabled(typedArray.getBoolean( + R.styleable.MapView_rotateEnabled, true)); + setDebugActive(typedArray.getBoolean( + R.styleable.MapView_debugActive, false)); + } finally { + typedArray.recycle(); + } + + // Ensure this view is interactable + setClickable(true); + setLongClickable(true); + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); + + // Register the SurfaceHolder callbacks + getHolder().addCallback(new Callbacks()); + + // Touch gesture detectors + mGestureDetector = new GestureDetector(context, new GestureListener()); + mGestureDetector.setIsLongpressEnabled(true); + mScaleGestureDetector = new ScaleGestureDetector(context, + new ScaleGestureListener()); + mRotateGestureDetector = new RotateGestureDetector(context, + new RotateGestureListener()); + + // Shows the zoom controls + // But not when in Eclipse UI editor + if (!isInEditMode()) { + if (!context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)) { + mZoomButtonsController = new ZoomButtonsController(this); + mZoomButtonsController.setZoomSpeed(300); + mZoomButtonsController.setOnZoomListener(new OnZoomListener()); + } + } + } + + // + // Property methods + // + + public LonLat getCenterCoordinate() { + return mNativeMapView.getLonLat(); + } + + public void setCenterCoordinate(LonLat centerCoordinate) { + setCenterCoordinate(centerCoordinate, false); + } + + public void setCenterCoordinate(LonLat centerCoordinate, boolean animated) { + double duration = animated ? 0.3 : 0.0; + mNativeMapView.setLonLat(centerCoordinate, duration); + } + + public void setCenterCoordinate(LonLatZoom centerCoordinate) { + setCenterCoordinate(centerCoordinate, false); + } + + public void setCenterCoordinate(LonLatZoom centerCoordinate, + boolean animated) { + double duration = animated ? 0.3 : 0.0; + mNativeMapView.setLonLatZoom(centerCoordinate, duration); + } + + public double getDirection() { + double direction = mNativeMapView.getAngle(); + + direction *= 180 / Math.PI; + + while (direction > 360) + direction -= 360; + while (direction < 0) + direction += 360; + + return direction; + } + + public void setDirection(double direction) { + setDirection(direction, false); + } + + public void setDirection(double direction, boolean animated) { + double duration = animated ? 0.3 : 0.0; + + direction *= Math.PI / 180; + + mNativeMapView.setAngle(direction, duration); + } + + public void resetPosition() { + mNativeMapView.resetPosition(); + } + + public void resetNorth() { + mNativeMapView.resetNorth(); + } + + public double getZoomLevel() { + return mNativeMapView.getZoom(); + } + + public void setZoomLevel(double zoomLevel) { + setZoomLevel(zoomLevel, false); + } + + public void setZoomLevel(double zoomLevel, boolean animated) { + double duration = animated ? 0.3 : 0.0; + mNativeMapView.setZoom(zoomLevel, duration); + } + + public boolean isZoomEnabled() { + return mZoomEnabled; + } + + public void setZoomEnabled(boolean zoomEnabled) { + this.mZoomEnabled = zoomEnabled; + + if ((mZoomButtonsController != null) + && (getVisibility() == View.VISIBLE) && mZoomEnabled) { + mZoomButtonsController.setVisible(true); + } + } + + public boolean isScrollEnabled() { + return mScrollEnabled; + } + + public void setScrollEnabled(boolean scrollEnabled) { + this.mScrollEnabled = scrollEnabled; + } + + public boolean isRotateEnabled() { + return mRotateEnabled; + } + + public void setRotateEnabled(boolean rotateEnabled) { + this.mRotateEnabled = rotateEnabled; + } + + public boolean isDebugActive() { + return mNativeMapView.getDebug(); + } + + public void setDebugActive(boolean debugActive) { + mNativeMapView.setDebug(debugActive); + } + + public void toggleDebug() { + mNativeMapView.toggleDebug(); + } + + // + // Style methods + // + + public void toggleStyle() { + // TODO + } + + // TODO seems like JSON simple may be better since it implements Map + // interface + // Other candidates: fastjson, json-smart, fossnova json, + public JSONObject getRawStyle() { + try { + return new JSONObject(mNativeMapView.getStyleJSON()); + } catch (JSONException e) { + e.printStackTrace(); + return null; + } + } + + public void setRawStyle(JSONObject style) { + mNativeMapView.setStyleJSON(style.toString()); + } + + public void getStyleOrderedLayerNames() { + // TODO + } + + public void setStyleOrderedLayerNames() { + // TODO + } + + public void getAllStyleClasses() { + // TODO + } + + public void getAppliedStyleClasses() { + // TODO + } + + public void setAppliedStyleClasses() { + // TODO + } + + public void getStyleDescriptionForLayer() { + // TODO + } + + public void setStyleDescription() { + // TODO + } + + // + // Lifecycle events + // + + // Called when we need to restore instance state + // Must be called from Activity onCreate + public void onCreate(Bundle savedInstanceState) { + Log.v(TAG, "onCreate"); + if (savedInstanceState != null) { + lonlat = (LonLat) savedInstanceState + .getParcelable(STATE_CENTER_COORDINATE); + angle = savedInstanceState.getDouble(STATE_CENTER_DIRECTION); + zoom = savedInstanceState.getDouble(STATE_ZOOM_LEVEL); + setCenterCoordinate((LonLat) savedInstanceState + .getParcelable(STATE_CENTER_COORDINATE)); + setDirection(savedInstanceState.getDouble(STATE_CENTER_DIRECTION)); + setZoomLevel(savedInstanceState.getDouble(STATE_ZOOM_LEVEL)); + setZoomEnabled(savedInstanceState.getBoolean(STATE_ZOOM_ENABLED)); + setScrollEnabled(savedInstanceState + .getBoolean(STATE_SCROLL_ENABLED)); + setRotateEnabled(savedInstanceState + .getBoolean(STATE_ROTATE_ENABLED)); + setDebugActive(savedInstanceState.getBoolean(STATE_DEBUG_ACTIVE)); + } + + mNativeMapView.initializeDisplay(); + } + + // Called when we need to save instance state + // Must be called from Activity onSaveInstanceState + public void onSaveInstanceState(Bundle outState) { + Log.v(TAG, "onSaveInstanceState"); + outState.putParcelable(STATE_CENTER_COORDINATE, getCenterCoordinate()); + outState.putDouble(STATE_CENTER_DIRECTION, getDirection()); + outState.putDouble(STATE_ZOOM_LEVEL, getZoomLevel()); + outState.putBoolean(STATE_ZOOM_ENABLED, isZoomEnabled()); + outState.putBoolean(STATE_SCROLL_ENABLED, isScrollEnabled()); + outState.putBoolean(STATE_ROTATE_ENABLED, isRotateEnabled()); + outState.putBoolean(STATE_DEBUG_ACTIVE, isDebugActive()); + } + + // Called when we need to clean up + // Must be called from Activity onDestroy + public void onDestroy() { + Log.v(TAG, "onDestroy"); + mNativeMapView.terminateDisplay(); + } + + // Called when we need to create the GL context + // Must be called from Activity onStart + public void onStart() { + Log.v(TAG, "onStart"); + mNativeMapView.initializeContext(); + } + + // Called when we need to terminate the GL context + // Must be called from Activity onPause + public void onStop() { + Log.v(TAG, "onStop"); + mNativeMapView.terminateContext(); + } + + // Called when we need to stop the render thread + // Must be called from Activity onPause + public void onPause() { + Log.v(TAG, "onPause"); + lonlat = mNativeMapView.getLonLat(); + angle = mNativeMapView.getAngle(); + zoom = mNativeMapView.getZoom(); + mNativeMapView.stop(); + } + + // Called when we need to start the render thread + // Must be called from Activity onResume + + // TODO need to fix this in Map C++ code + // Seems map state gets reset when we start() + private LonLat lonlat; + private double angle, zoom; + + public void onResume() { + Log.v(TAG, "onResume"); + mNativeMapView.start(); + if (lonlat != null) { + mNativeMapView.setLonLat(lonlat); + mNativeMapView.setAngle(angle); + mNativeMapView.setZoom(zoom); + } + } + + // This class handles SurfaceHolder callbacks + private class Callbacks implements SurfaceHolder.Callback2 { + + // Called when we need to redraw the view + // This is called before our view is first visible to prevent an initial + // flicker (see Android SDK documentation) + @Override + public void surfaceRedrawNeeded(SurfaceHolder holder) { + Log.v(TAG, "surfaceRedrawNeeded"); + mNativeMapView.update(); + } + + // Called when the native surface buffer has been created + // Must do all EGL/GL ES initialization here + @Override + public void surfaceCreated(SurfaceHolder holder) { + Log.v(TAG, "surfaceCreated"); + mNativeMapView.createSurface(holder.getSurface()); + if (lonlat != null) { + mNativeMapView.setLonLat(lonlat); + mNativeMapView.setAngle(angle); + mNativeMapView.setZoom(zoom); + } + } + + // Called when the native surface buffer has been destroyed + // Must do all EGL/GL ES destruction here + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.v(TAG, "surfaceDestroyed"); + mNativeMapView.destroySurface(); + } + + // Called when the format or size of the native surface buffer has been + // changed + // Must handle window resizing here. + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, + int height) { + Log.v(TAG, "surfaceChanged"); + Log.i(TAG, "resize " + format + " " + width + " " + height); + mNativeMapView.resize(width, height); + // TODO fix bug when rotating - sometimes 1/2 map black + } + } + + // Called when view is no longer connected + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + // Required by ZoomButtonController (from Android SDK documentation) + if (mZoomButtonsController != null) { + mZoomButtonsController.setVisible(false); + } + } + + // Called when view is hidden and shown + @Override + protected void onVisibilityChanged(View changedView, int visibility) { + // Required by ZoomButtonController (from Android SDK documentation) + if ((mZoomButtonsController != null) && (visibility != View.VISIBLE)) { + mZoomButtonsController.setVisible(false); + } + if ((mZoomButtonsController != null) && (visibility == View.VISIBLE) + && mZoomEnabled) { + mZoomButtonsController.setVisible(true); + } + } + + // + // Draw events + // + + // TODO: onDraw for editor? + // By default it just shows a gray screen with "MapView" + // Not too important but perhaps we could put a static demo map image there + + // + // Input events + // + + // Zoom in or out + private void zoom(boolean zoomIn) { + zoom(zoomIn, -1.0f, -1.0f); + } + + private void zoom(boolean zoomIn, float x, float y) { + // Cancel any animation + mNativeMapView.cancelTransitions(); + + if (zoomIn) { + mNativeMapView.scaleBy(2.0, x, y, 0.3); + } else { + // TODO two finger tap zoom out + mNativeMapView.scaleBy(0.5, x, y, 0.3); + } + } + + // Called when user touches the screen, all positions are absolute + @Override + public boolean onTouchEvent(MotionEvent event) { + // Check and ignore non touch or left clicks + if ((event.getButtonState() != 0) + && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { + return false; + } + + // Check two finger gestures first + boolean rotateRetVal = false; + mRotateGestureDetector.onTouchEvent(event); + boolean scaleRetVal = false; + mScaleGestureDetector.onTouchEvent(event); + + // Handle two finger tap + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // First pointer down + break; + + case MotionEvent.ACTION_POINTER_DOWN: + // Second pointer down + if (event.getPointerCount() == 2) { + mTwoTap = true; + } else { + mTwoTap = false; + } + break; + + case MotionEvent.ACTION_POINTER_UP: + // Second pointer up + break; + + case MotionEvent.ACTION_UP: + // First pointer up + long tapInterval = event.getEventTime() - event.getDownTime(); + boolean isTap = tapInterval <= ViewConfiguration.getTapTimeout(); + boolean inProgress = mRotateGestureDetector.isInProgress() + || mScaleGestureDetector.isInProgress(); + + if (mTwoTap && isTap && !inProgress) { + PointF focalPoint = TwoFingerGestureDetector + .determineFocalPoint(event); + zoom(false, focalPoint.x, focalPoint.y); + mTwoTap = false; + return true; + } + + mTwoTap = false; + break; + + case MotionEvent.ACTION_CANCEL: + mTwoTap = false; + break; + } + + // Do not change this code! It will break very easily. + boolean retVal = rotateRetVal || scaleRetVal; + retVal = mGestureDetector.onTouchEvent(event) || retVal; + return retVal || super.onTouchEvent(event); + } + + // This class handles one finger gestures + private class GestureListener extends + GestureDetector.SimpleOnGestureListener { + + // Must always return true otherwise all events are ignored + @Override + public boolean onDown(MotionEvent e) { + // Show the zoom controls + if ((mZoomButtonsController != null) && mZoomEnabled) { + mZoomButtonsController.setVisible(true); + } + + return true; + } + + // Called for double taps + @Override + public boolean onDoubleTap(MotionEvent e) { + if (!mZoomEnabled) { + return false; + } + + // Single finger double tap + // Zoom in + zoom(true, e.getX(), e.getY()); + return true; + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + // Cancel any animation + mNativeMapView.cancelTransitions(); + + return true; + } + + // Called for single taps after a delay + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + return false; + } + + // Called for a long press + @Override + public void onLongPress(MotionEvent e) { + // TODO + } + + // Called for flings + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { + if (!mScrollEnabled) { + return false; + } + + // Fling the map + // TODO Google Maps also has a rotate and zoom fling + // TODO does not work + /* + * float ease = 0.25f; + * + * velocityX = velocityX * ease; velocityY = velocityY * ease; + * + * double speed = Math.sqrt(velocityX * velocityX + velocityY * + * velocityY); double deceleration = 2500; double duration = speed / + * (deceleration * ease); + * + * + * // Cancel any animation mNativeMapView.cancelTransitions(); + * + * mNativeMapView.moveBy(velocityX * duration / 2.0, velocityY * + * duration / 2.0, duration); + * + * return true; + */ + return false; + } + + // Called for drags + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, + float distanceX, float distanceY) { + if (!mScrollEnabled) { + return false; + } + + // Cancel any animation + mNativeMapView.cancelTransitions(); // TODO need to test canceling + // transitions with touch + + // Scroll the map + mNativeMapView.moveBy(-distanceX, -distanceY); + return true; + } + } + + // This class handles two finger gestures + private class ScaleGestureListener extends + ScaleGestureDetector.SimpleOnScaleGestureListener { + + long mBeginTime = 0; + float mScaleFactor = 1.0f; + boolean mStarted = false; + + // Called when two fingers first touch the screen + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + if (!mZoomEnabled) { + return false; + } + + mBeginTime = detector.getEventTime(); + + return true; + } + + // Called when fingers leave screen + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + mBeginTime = 0; + mScaleFactor = 1.0f; + mStarted = false; + } + + // Called each time one of the two fingers moves + // Called for pinch zooms + @Override + public boolean onScale(ScaleGestureDetector detector) { + if (!mZoomEnabled) { + return false; + } + + // If scale is large enough ignore a tap + // TODO: Google Maps seem to use a velocity rather than absolute + // value? + mScaleFactor *= detector.getScaleFactor(); + if ((mScaleFactor > 1.05f) || (mScaleFactor < 0.95f)) { + mStarted = true; + } + + // Ignore short touches in case it is a tap + // Also ignore small scales + long time = detector.getEventTime(); + long interval = time - mBeginTime; + if (!mStarted && (interval <= ViewConfiguration.getTapTimeout())) { + return false; + } + + // TODO complex decision between roate or scale or both (see Google + // Maps app) + + // Cancel any animation + mNativeMapView.cancelTransitions(); + + // Scale the map + mNativeMapView.scaleBy(detector.getScaleFactor(), + detector.getFocusX(), detector.getFocusY()); + + return true; + } + } + + // This class handles two rotate gestures + // TODO need way to single finger rotate - need to research how google maps + // does this - for phones with single touch, or when using mouse etc + private class RotateGestureListener extends + RotateGestureDetector.SimpleOnRotateGestureListener { + + long mBeginTime = 0; + float mTotalAngle = 0.0f; + boolean mStarted = false; + + // Called when two fingers first touch the screen + @Override + public boolean onRotateBegin(RotateGestureDetector detector) { + if (!mRotateEnabled) { + return false; + } + + mBeginTime = detector.getEventTime(); + Log.d("rotate", "rotate begin"); + return true; + } + + // Called when the fingers leave the screen + @Override + public void onRotateEnd(RotateGestureDetector detector) { + mBeginTime = 0; + mTotalAngle = 0.0f; + mStarted = false; + Log.d("rotate", "rotate end"); + } + + // Called each time one of the two fingers moves + // Called for rotation + @Override + public boolean onRotate(RotateGestureDetector detector) { + if (!mRotateEnabled) { + return false; + } + + Log.d("rotate", "rotate evt"); + + // If rotate is large enough ignore a tap + // TODO: Google Maps seem to use a velocity rather than absolute + // value, up to a point then they always rotate + mTotalAngle += detector.getRotationDegreesDelta(); + Log.d("rotate", "ttl angle " + mTotalAngle); + if ((mTotalAngle > 5.0f) || (mTotalAngle < -5.0f)) { + mStarted = true; + Log.d("rotate", "rotate started"); + } + + // Ignore short touches in case it is a tap + // Also ignore small rotate + long time = detector.getEventTime(); + long interval = time - mBeginTime; + if (!mStarted && (interval <= ViewConfiguration.getTapTimeout())) { + Log.d("rotate", "rotate ignored"); + return false; + } + + // TODO complex decision between rotate or scale or both (see Google + // Maps app). It seems if you start one or the other it takes more + // to start the other too. Haven't figured out what it uses to + // decide when to transition to both at the same time. + + // Cancel any animation + mNativeMapView.cancelTransitions(); + + // Rotate the map + double angle = mNativeMapView.getAngle(); + angle -= detector.getRotationDegreesDelta() * Math.PI / 180.0; + Log.d("rotate", "rotate to " + angle); + mNativeMapView.setAngle(angle, detector.getFocusX(), + detector.getFocusY()); + + return true; + } + } + + // This class handles input events from the zoom control buttons + // Zoom controls allow single touch only devices to zoom in and out + private class OnZoomListener implements + ZoomButtonsController.OnZoomListener { + + // Not used + @Override + public void onVisibilityChanged(boolean visible) { + // Ignore + } + + // Called when user pushes a zoom button + @Override + public void onZoom(boolean zoomIn) { + if (!mZoomEnabled) { + return; + } + + // Zoom in or out + zoom(zoomIn); + } + } + + // Called when the user presses a key, also called for repeating keys held + // down + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + // If the user has held the scroll key down for a while then accelerate + // the scroll speed + double scrollDist = event.getRepeatCount() >= 5 ? 50.0 : 10.0; + + // Check which key was pressed via hardware/real key code + switch (keyCode) { + // Tell the system to track these keys for long presses on + // onKeyLongPress is fired + case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_DPAD_CENTER: + event.startTracking(); + return true; + + case KeyEvent.KEYCODE_DPAD_LEFT: + if (!mScrollEnabled) { + return false; + } + + // Cancel any animation + mNativeMapView.cancelTransitions(); + + // Move left + mNativeMapView.moveBy(scrollDist, 0.0); + return true; + + case KeyEvent.KEYCODE_DPAD_RIGHT: + if (!mScrollEnabled) { + return false; + } + + // Cancel any animation + mNativeMapView.cancelTransitions(); + + // Move right + mNativeMapView.moveBy(-scrollDist, 0.0); + return true; + + case KeyEvent.KEYCODE_DPAD_UP: + if (!mScrollEnabled) { + return false; + } + + // Cancel any animation + mNativeMapView.cancelTransitions(); + + // Move up + mNativeMapView.moveBy(0.0, scrollDist); + return true; + + case KeyEvent.KEYCODE_DPAD_DOWN: + if (!mScrollEnabled) { + return false; + } + + // Cancel any animation + mNativeMapView.cancelTransitions(); + + // Move down + mNativeMapView.moveBy(0.0, -scrollDist); + return true; + + default: + // We are not interested in this key + return super.onKeyUp(keyCode, event); + } + } + + // Called when the user long presses a key that is being tracked + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + // Check which key was pressed via hardware/real key code + switch (keyCode) { + // Tell the system to track these keys for long presses on + // onKeyLongPress is fired + case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_DPAD_CENTER: + if (!mZoomEnabled) { + return false; + } + + // Zoom out + zoom(false); + return true; + + default: + // We are not interested in this key + return super.onKeyUp(keyCode, event); + } + } + + // Called when the user releases a key + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + // Check if the key action was canceled (used for virtual keyboards) + if (event.isCanceled()) { + return super.onKeyUp(keyCode, event); + } + + // Check which key was pressed via hardware/real key code + // Note if keyboard does not have physical key (ie primary non-shifted + // key) then it will not appear here + // Must use the key character map as physical to character is not + // fixed/guaranteed + switch (keyCode) { + case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_DPAD_CENTER: + if (!mZoomEnabled) { + return false; + } + + // Zoom in + zoom(true); + return true; + } + + // We are not interested in this key + return super.onKeyUp(keyCode, event); + } + + // Called for trackball events, all motions are relative in device specific + // units + // TODO: test trackball click and long click + @Override + public boolean onTrackballEvent(MotionEvent event) { + // Choose the action + switch (event.getActionMasked()) { + // The trackball was rotated + case MotionEvent.ACTION_MOVE: + if (!mScrollEnabled) { + return false; + } + + // Cancel any animation + mNativeMapView.cancelTransitions(); + + // Scroll the map + mNativeMapView.moveBy(-10.0 * event.getX(), -10.0 * event.getY()); + return true; + + // Trackball was pushed in so start tracking and tell system we are + // interested + // We will then get the up action + case MotionEvent.ACTION_DOWN: + // Set up a delayed callback to check if trackball is still + // After waiting the system long press time out + if (mCurrentTrackballLongPressTimeOut != null) { + mCurrentTrackballLongPressTimeOut.cancel(); + mCurrentTrackballLongPressTimeOut = null; + } + mCurrentTrackballLongPressTimeOut = new TrackballLongPressTimeOut(); + postDelayed(mCurrentTrackballLongPressTimeOut, + ViewConfiguration.getLongPressTimeout()); + return true; + + // Trackball was released + case MotionEvent.ACTION_UP: + if (!mZoomEnabled) { + return false; + } + + // Only handle if we have not already long pressed + if (mCurrentTrackballLongPressTimeOut != null) { + // Zoom in + zoom(true); + } + return true; + + // Trackball was cancelled + case MotionEvent.ACTION_CANCEL: + if (mCurrentTrackballLongPressTimeOut != null) { + mCurrentTrackballLongPressTimeOut.cancel(); + mCurrentTrackballLongPressTimeOut = null; + } + return true; + + default: + // We are not interested in this event + return super.onTrackballEvent(event); + } + } + + // This class implements the trackball long press time out callback + private class TrackballLongPressTimeOut implements Runnable { + + // Track if we have been cancelled + private boolean cancelled; + + public TrackballLongPressTimeOut() { + cancelled = false; + } + + // Cancel the timeoht + public void cancel() { + cancelled = true; + } + + // Called when long press time out expires + @Override + public void run() { + // Check if the trackball is still pressed + if (!cancelled) { + // Zoom out + zoom(false); + + // Ensure the up action is not run + mCurrentTrackballLongPressTimeOut = null; + } + } + } + + // Called for events that don't fit the other handlers + // such as mouse scroll events, mouse moves, joystick, trackpad + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + // Mouse events + // TODO: SOURCE_TOUCH_NAVIGATION? + // TODO: source device resolution? + if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { + // Choose the action + switch (event.getActionMasked()) { + // Mouse scrolls + case MotionEvent.ACTION_SCROLL: + if (!mZoomEnabled) { + return false; + } + + // Cancel any animation + mNativeMapView.cancelTransitions(); + + // Get the vertical scroll amount, one click = 1 + float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL); + + // Scale the map by the appropriate power of two factor + mNativeMapView.scaleBy(Math.pow(2.0, scrollDist), event.getX(), + event.getY()); + + return true; + + default: + // We are not interested in this event + return super.onGenericMotionEvent(event); + } + } + + // We are not interested in this event + return super.onGenericMotionEvent(event); + } + + // Called when the mouse pointer enters or exits the view + // or when it fades in or out due to movement + @Override + public boolean onHoverEvent(MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_HOVER_ENTER: + case MotionEvent.ACTION_HOVER_MOVE: + // Show the zoom controls + if ((mZoomButtonsController != null) && mZoomEnabled) { + mZoomButtonsController.setVisible(true); + } + return true; + + case MotionEvent.ACTION_HOVER_EXIT: + // Hide the zoom controls + if (mZoomButtonsController != null) { + mZoomButtonsController.setVisible(false); + } + + default: + // We are not interested in this event + return super.onHoverEvent(event); + } + } + + // + // Accessibility events + // + + // + // Map events + // + + public interface OnMapChangedListener { + void onMapChanged(); + } + + private OnMapChangedListener mOnMapChangedListener; + + // Adds a listener for onMapChanged + public void setOnMapChangedListener(OnMapChangedListener listener) { + mOnMapChangedListener = listener; + } + + // Called when the map view transformation has changed + // Called via JNI from NativeMapView + // Need to update anything that relies on map state + protected void onMapChanged() { + Log.v(TAG, "onMapChanged"); + if (mOnMapChangedListener != null) { + mOnMapChangedListener.onMapChanged(); + } + } +} diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java new file mode 100644 index 000000000..468d4713d --- /dev/null +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -0,0 +1,501 @@ +package com.mapbox.mapboxgl.lib; + +import java.util.Set; + +import android.util.Log; +import android.view.Surface; + +// Class that wraps the native methods for convenience +class NativeMapView { + + // + // Static members + // + + // Tag used for logging + private static final String TAG = "NativeMapView"; + + // + // Instance members + // + + // Holds the pointer to JNI NativeMapView + private long mNativeMapViewPtr = 0; + + // Used for callbacks + private MapView mMapView; + + // + // Static methods + // + + static { + System.loadLibrary("mapbox-gl"); + } + + // + // Constructors + // + + public NativeMapView(MapView mapView, String defaultStyleJSON) { + mMapView = mapView; + + // Create the NativeMapView + mNativeMapViewPtr = nativeCreate(defaultStyleJSON); + } + + // + // Methods + // + + public void initializeDisplay() { + nativeInitializeDisplay(mNativeMapViewPtr); + } + + public void terminateDisplay() { + nativeTerminateDisplay(mNativeMapViewPtr); + } + + public void initializeContext() { + nativeInitializeContext(mNativeMapViewPtr); + } + + public void terminateContext() { + nativeTerminateContext(mNativeMapViewPtr); + } + + public void createSurface(Surface surface) { + nativeCreateSurface(mNativeMapViewPtr, surface); + } + + public void destroySurface() { + nativeDestroySurface(mNativeMapViewPtr); + } + + public void start() { + nativeStart(mNativeMapViewPtr); + } + + public void stop() { + nativeStop(mNativeMapViewPtr); + } + + public void rerender() { + nativeRerender(mNativeMapViewPtr); + } + + public void update() { + nativeUpdate(mNativeMapViewPtr); + } + + public void cleanup() { + nativeCleanup(mNativeMapViewPtr); + } + + public void addDefaultSource() { + nativeAddDefaultSource(mNativeMapViewPtr); + } + + public void removeDefaultSource() { + nativeRemoveDefaultSource(mNativeMapViewPtr); + } + + public void addSource(String name, String url) { + nativeAddSource(mNativeMapViewPtr, name, url); + } + + public void removeSource(String name) { + nativeRemoveSource(mNativeMapViewPtr, name); + } + + public void resize(int width, int height) { + resize(width, height, 1.0f); + } + + public void resize(int width, int height, float ratio) { + if (width < 0) { + throw new IllegalArgumentException("width cannot be negative."); + } + + if (height < 0) { + throw new IllegalArgumentException("height cannot be negative."); + } + + if (width > 65535) { + throw new IllegalArgumentException( + "width cannot be greater than 65535."); + } + + if (height > 65535) { + throw new IllegalArgumentException( + "height cannot be greater than 65535."); + } + + nativeResize(mNativeMapViewPtr, width, height, ratio); + } + + public void resize(int width, int height, float ratio, int fbWidth, + int fbHeight) { + if (width < 0) { + throw new IllegalArgumentException("width cannot be negative."); + } + + if (height < 0) { + throw new IllegalArgumentException("height cannot be negative."); + } + + if (fbWidth < 0) { + throw new IllegalArgumentException("fbWidth cannot be negative."); + } + + if (fbHeight < 0) { + throw new IllegalArgumentException("fbHeight cannot be negative."); + } + + if (fbWidth > 65535) { + throw new IllegalArgumentException( + "fbWidth cannot be greater than 65535."); + } + + if (fbHeight > 65535) { + throw new IllegalArgumentException( + "fbHeight cannot be greater than 65535."); + } + nativeResize(mNativeMapViewPtr, width, height, ratio, fbWidth, fbHeight); + } + + public void setAppliedClasses(Set appliedClasses) { + nativeSetAppliedClasses(mNativeMapViewPtr, appliedClasses); + } + + public Set getAppliedClasses() { + return nativeGetAppliedClasses(mNativeMapViewPtr); + } + + public void setDefaultTransitionDuration() { + setDefaultTransitionDuration(0); + } + + public void setDefaultTransitionDuration(long durationMilliseconds) { + if (durationMilliseconds < 0) { + throw new IllegalArgumentException( + "durationMilliseconds cannot be negative."); + } + + nativeSetDefaultTransitionDuration(mNativeMapViewPtr, + durationMilliseconds); + } + + public void setStyleJSON(String newStyleJSON) { + nativeSetStyleJSON(mNativeMapViewPtr, newStyleJSON); + } + + public String getStyleJSON() { + return nativeGetStyleJSON(mNativeMapViewPtr); + } + + public void cancelTransitions() { + nativeCancelTransitions(mNativeMapViewPtr); + } + + public void moveBy(double dx, double dy) { + moveBy(dx, dy, 0.0); + } + + public void moveBy(double dx, double dy, double duration) { + nativeMoveBy(mNativeMapViewPtr, dx, dy, duration); + } + + public void setLonLat(LonLat lonLat) { + setLonLat(lonLat, 0.0); + } + + public void setLonLat(LonLat lonLat, double duration) { + nativeSetLonLat(mNativeMapViewPtr, lonLat, duration); + } + + public LonLat getLonLat() { + return nativeGetLonLat(mNativeMapViewPtr); + } + + public void startPanning() { + nativeStartPanning(mNativeMapViewPtr); + } + + public void stopPanning() { + nativeStopPanning(mNativeMapViewPtr); + } + + public void resetPosition() { + nativeResetPosition(mNativeMapViewPtr); + } + + public void scaleBy(double ds) { + scaleBy(ds, -1.0, -1.0); + } + + public void scaleBy(double ds, double cx, double cy) { + scaleBy(ds, cx, cy, 0.0); + } + + public void scaleBy(double ds, double cx, double cy, double duration) { + nativeScaleBy(mNativeMapViewPtr, ds, cx, cy, duration); + } + + public void setScale(double scale) { + setScale(scale, -1.0, -1.0); + } + + public void setScale(double scale, double cx, double cy) { + setScale(scale, cx, cy, 0.0); + } + + public void setScale(double scale, double cx, double cy, double duration) { + nativeSetScale(mNativeMapViewPtr, scale, cx, cy, duration); + } + + public double getScale() { + return nativeGetScale(mNativeMapViewPtr); + } + + public void setZoom(double zoom) { + setZoom(zoom, 0.0); + } + + public void setZoom(double zoom, double duration) { + nativeSetZoom(mNativeMapViewPtr, zoom, duration); + } + + public double getZoom() { + return nativeGetZoom(mNativeMapViewPtr); + } + + public void setLonLatZoom(LonLatZoom lonLatZoom) { + setLonLatZoom(lonLatZoom, 0.0); + } + + public void setLonLatZoom(LonLatZoom lonLatZoom, double duration) { + nativeSetLonLatZoom(mNativeMapViewPtr, lonLatZoom, duration); + } + + public LonLatZoom getLonLatZoom() { + return nativeGetLonLatZoom(mNativeMapViewPtr); + } + + public void resetZoom() { + nativeResetZoom(mNativeMapViewPtr); + } + + public void startScaling() { + nativeStartScaling(mNativeMapViewPtr); + } + + public void stopScaling() { + nativeStopScaling(mNativeMapViewPtr); + } + + public double getMinZoom() { + return nativeGetMinZoom(mNativeMapViewPtr); + } + + public double getMaxZoom() { + return nativeGetMaxZoom(mNativeMapViewPtr); + } + + public void rotateBy(double sx, double sy, double ex, double ey) { + rotateBy(sx, sy, ex, ey, 0.0); + } + + public void rotateBy(double sx, double sy, double ex, double ey, + double duration) { + nativeRotateBy(mNativeMapViewPtr, sx, sy, ex, ey, duration); + } + + public void setAngle(double angle) { + setAngle(angle, 0.0); + } + + public void setAngle(double angle, double duration) { + nativeSetAngle(mNativeMapViewPtr, angle, duration); + } + + public void setAngle(double angle, double cx, double cy) { + nativeSetAngle(mNativeMapViewPtr, angle, cx, cy); + } + + public double getAngle() { + return nativeGetAngle(mNativeMapViewPtr); + } + + public void resetNorth() { + nativeResetNorth(mNativeMapViewPtr); + } + + public void startRotating() { + nativeStartRotating(mNativeMapViewPtr); + } + + public void stopRotating() { + nativeStopRotating(mNativeMapViewPtr); + } + + public boolean canRotate() { + return nativeCanRotate(mNativeMapViewPtr); + } + + public void setDebug(boolean debug) { + nativeSetDebug(mNativeMapViewPtr, debug); + } + + public void toggleDebug() { + nativeToggleDebug(mNativeMapViewPtr); + } + + public boolean getDebug() { + return nativeGetDebug(mNativeMapViewPtr); + } + + // + // Callbacks + // + + protected void onMapChanged() { + Log.v(TAG, "onMapChanged"); + mMapView.onMapChanged(); + } + + // + // JNI methods + // + + @Override + protected void finalize() throws Throwable { + nativeDestroy(mNativeMapViewPtr); + mNativeMapViewPtr = 0; + super.finalize(); + } + + private native long nativeCreate(String defaultStyleJSON); + + private native void nativeDestroy(long nativeMapViewPtr); + + private native void nativeInitializeDisplay(long nativeMapViewPtr); + + private native void nativeTerminateDisplay(long nativeMapViewPtr); + + private native void nativeInitializeContext(long nativeMapViewPtr); + + private native void nativeTerminateContext(long nativeMapViewPtr); + + private native void nativeCreateSurface(long nativeMapViewPtr, + Surface surface); + + private native void nativeDestroySurface(long nativeMapViewPtr); + + private native void nativeStart(long nativeMapViewPtr); + + private native void nativeStop(long nativeMapViewPtr); + + private native void nativeRerender(long nativeMapViewPtr); + + private native void nativeUpdate(long nativeMapViewPtr); + + private native void nativeCleanup(long nativeMapViewPtr); + + private native void nativeAddDefaultSource(long nativeMapViewPtr); + + private native void nativeRemoveDefaultSource(long nativeMapViewPtr); + + private native void nativeAddSource(long nativeMapViewPtr, String name, + String url); + + private native void nativeRemoveSource(long nativeMapViewPtr, String name); + + private native void nativeResize(long nativeMapViewPtr, int width, + int height, float ratio); + + private native void nativeResize(long nativeMapViewPtr, int width, + int height, float ratio, int fbWidth, int fbHeight); + + private native void nativeSetAppliedClasses(long nativeMapViewPtr, + Set appliedClasses); + + private native Set nativeGetAppliedClasses(long nativeMapViewPtr); + + private native void nativeSetDefaultTransitionDuration( + long nativeMapViewPtr, long durationMilliseconds); + + private native void nativeSetStyleJSON(long nativeMapViewPtr, + String newStyleJSON); + + private native String nativeGetStyleJSON(long nativeMapViewPtr); + + private native void nativeCancelTransitions(long nativeMapViewPtr); + + private native void nativeMoveBy(long nativeMapViewPtr, double dx, + double dy, double duration); + + private native void nativeSetLonLat(long nativeMapViewPtr, LonLat lonLat, + double duration); + + private native LonLat nativeGetLonLat(long nativeMapViewPtr); + + private native void nativeStartPanning(long nativeMapViewPtr); + + private native void nativeStopPanning(long nativeMapViewPtr); + + private native void nativeResetPosition(long nativeMapViewPtr); + + private native void nativeScaleBy(long nativeMapViewPtr, double ds, + double cx, double cy, double duration); + + private native void nativeSetScale(long nativeMapViewPtr, double scale, + double cx, double cy, double duration); + + private native double nativeGetScale(long nativeMapViewPtr); + + private native void nativeSetZoom(long nativeMapViewPtr, double zoom, + double duration); + + private native double nativeGetZoom(long nativeMapViewPtr); + + private native void nativeSetLonLatZoom(long nativeMapViewPtr, + LonLatZoom lonLatZoom, double duration); + + private native LonLatZoom nativeGetLonLatZoom(long nativeMapViewPtr); + + private native void nativeResetZoom(long nativeMapViewPtr); + + private native void nativeStartScaling(long nativeMapViewPtr); + + private native void nativeStopScaling(long nativeMapViewPtr); + + private native double nativeGetMinZoom(long nativeMapViewPtr); + + private native double nativeGetMaxZoom(long nativeMapViewPtr); + + private native void nativeRotateBy(long nativeMapViewPtr, double sx, + double sy, double ex, double ey, double duration); + + private native void nativeSetAngle(long nativeMapViewPtr, double angle, + double duration); + + private native void nativeSetAngle(long nativeMapViewPtr, double angle, + double cx, double cy); + + private native double nativeGetAngle(long nativeMapViewPtr); + + private native void nativeResetNorth(long nativeMapViewPtr); + + private native void nativeStartRotating(long nativeMapViewPtr); + + private native void nativeStopRotating(long nativeMapViewPtr); + + private native boolean nativeCanRotate(long nativeMapViewPtr); + + private native void nativeSetDebug(long nativeMapViewPtr, boolean debug); + + private native void nativeToggleDebug(long nativeMapViewPtr); + + private native boolean nativeGetDebug(long nativeMapViewPtr); +} diff --git a/android/java/lib/src/main/res/raw/style.min.js b/android/java/lib/src/main/res/raw/style.min.js new file mode 100644 index 000000000..906bec14f --- /dev/null +++ b/android/java/lib/src/main/res/raw/style.min.js @@ -0,0 +1 @@ +{"buckets":{"admin_maritime":{"source":"outdoors","layer":"admin","field":"maritime","value":1,"join":"round","cap":"round","type":"line"},"admin_l2":{"source":"outdoors","layer":"admin","filter":["and",{"field":"admin_level","value":2},{"field":"maritime","operator":"not","value":1}],"join":"round","cap":"round","type":"line"},"admin_l3":{"source":"outdoors","layer":"admin","filter":["and",{"field":"admin_level","value":[3,4,5]},{"field":"maritime","operator":"not","value":1}],"join":"round","type":"line"},"landcover_wood":{"source":"outdoors","layer":"landcover","field":"class","value":"wood","type":"fill"},"landcover_scrub":{"source":"outdoors","layer":"landcover","field":"class","value":"scrub","type":"fill"},"landcover_grass":{"source":"outdoors","layer":"landcover","field":"class","value":"grass","type":"fill"},"landcover_crop":{"source":"outdoors","layer":"landcover","field":"class","value":"crop","type":"fill"},"landcover_snow":{"source":"outdoors","layer":"landcover","field":"class","value":"snow","type":"fill"},"water":{"source":"outdoors","layer":"water","type":"fill"},"waterway_other":{"source":"outdoors","layer":"waterway","field":"type","value":["ditch","drain"],"cap":"round","type":"line"},"waterway_river_canal":{"source":"outdoors","layer":"waterway","field":"type","value":["river","canal"],"cap":"round","type":"line"},"waterway_stream":{"source":"outdoors","layer":"waterway","field":"type","value":"stream","cap":"round","type":"line"},"landuse_park":{"source":"outdoors","layer":"landuse","field":"class","value":"park","type":"fill"},"landuse_pitch":{"source":"outdoors","layer":"landuse","field":"class","value":"pitch","type":"fill"},"landuse_cemetery":{"source":"outdoors","layer":"landuse","field":"class","value":"cemetery","type":"fill"},"landuse_hospital":{"source":"outdoors","layer":"landuse","field":"class","value":"hospital","type":"fill"},"landuse_industrial":{"source":"outdoors","layer":"landuse","type":"fill","field":"class","value":"industrial"},"landuse_school":{"source":"outdoors","layer":"landuse","field":"class","value":"school","type":"fill"},"landuse_wood":{"source":"outdoors","layer":"landuse","field":"class","value":"wood","type":"fill"},"landuse_scrub":{"source":"outdoors","layer":"landuse","field":"class","value":"scrub","type":"fill"},"landuse_grass":{"source":"outdoors","layer":"landuse","field":"class","value":"grass","type":"fill"},"landuse_crop":{"source":"outdoors","layer":"landuse","field":"class","value":"crop","type":"fill"},"landuse_sand":{"source":"outdoors","layer":"landuse","field":"class","value":"sand","type":"fill"},"landuse_rock":{"source":"outdoors","layer":"landuse","field":"class","value":"rock","type":"fill"},"landuse_snow":{"source":"outdoors","layer":"landuse","field":"class","value":"snow","type":"fill"},"overlay_wetland":{"source":"outdoors","layer":"landuse_overlay","field":"class","value":["wetland","wetland_noveg"],"type":"fill"},"overlay_breakwater_pier":{"source":"outdoors","layer":"landuse_overlay","field":"class","value":["breakwater","pier"],"type":"fill"},"hillshade_full_shadow":{"source":"outdoors","layer":"hillshade","field":"class","value":"full_shadow","type":"fill"},"hillshade_medium_shadow":{"source":"outdoors","layer":"hillshade","field":"class","value":"medium_shadow","type":"fill"},"hillshade_medium_highlight":{"source":"outdoors","layer":"hillshade","field":"class","value":"medium_highlight","type":"fill"},"hillshade_full_highlight":{"source":"outdoors","layer":"hillshade","field":"class","value":"full_highlight","type":"fill"},"contour_line_5":{"source":"outdoors","layer":"contour","field":"index","join":"round","value":5,"type":"line"},"contour_line_10":{"source":"outdoors","layer":"contour","field":"index","join":"round","value":10,"type":"line"},"contour_line_other":{"source":"outdoors","layer":"contour","join":"round","type":"line"},"contour_label":{"source":"outdoors","layer":"contour","filter":["and",{"field":"index","value":[5,10]},{"field":"ele","operator":"not","value":0}],"path":"curve","text_field":"{{ele}} m","font":"Open Sans Regular, Arial Unicode MS Regular","fontSize":10,"feature_type":"line","type":"text","maxAngleDelta":0.5},"building":{"source":"outdoors","layer":"building","type":"fill"},"barrier_line_gate":{"source":"outdoors","layer":"barrier_line","field":"class","value":"gate","type":"line"},"barrier_line_fence":{"source":"outdoors","layer":"barrier_line","field":"class","value":"fence","type":"line"},"barrier_line_hedge":{"source":"outdoors","layer":"barrier_line","field":"class","value":"hedge","type":"line"},"barrier_line_land":{"source":"outdoors","layer":"barrier_line","field":"class","value":"land","type":"line"},"barrier_line_land_fill":{"source":"outdoors","layer":"barrier_line","field":"class","value":"land","type":"fill"},"barrier_line_cliff":{"source":"outdoors","layer":"barrier_line","field":"class","value":"cliff","type":"line"},"aeroway_fill":{"source":"outdoors","layer":"aeroway","type":"fill","enabled":12},"aeroway_runway":{"source":"outdoors","layer":"aeroway","field":"type","value":"runway","type":"line","enabled":12},"aeroway_taxiway":{"source":"outdoors","layer":"aeroway","field":"type","value":"taxiway","type":"line","enabled":12},"motorway":{"source":"outdoors","layer":"road","field":"class","value":"motorway","join":"round","cap":"round","type":"line"},"motorway_link":{"source":"outdoors","layer":"road","field":"class","value":"motorway_link","join":"round","cap":"round","type":"line"},"main":{"source":"outdoors","layer":"road","field":"class","value":"main","join":"round","cap":"round","type":"line"},"street":{"source":"outdoors","layer":"road","field":"class","value":["street","street_limited"],"join":"round","cap":"round","type":"line","enabled":12},"service":{"source":"outdoors","layer":"road","field":"class","value":"service","join":"round","cap":"round","type":"line","enabled":15},"path":{"source":"outdoors","layer":"road","field":"class","value":"path","type":"line"},"path_footway":{"source":"outdoors","layer":"road","field":"type","value":"footway","type":"line"},"path_path":{"source":"outdoors","layer":"road","field":"type","value":"path","type":"line"},"path_cycleway":{"source":"outdoors","layer":"road","field":"type","value":"cycleway","type":"line"},"path_mtb":{"source":"outdoors","layer":"road","field":"type","value":"mtb","type":"line"},"path_steps":{"source":"outdoors","layer":"road","field":"type","value":"steps","type":"line"},"path_piste":{"source":"outdoors","layer":"road","field":"type","value":"piste","type":"line"},"major_rail":{"source":"outdoors","layer":"road","field":"class","value":"major_rail","type":"line"},"tunnel_motorway":{"source":"outdoors","layer":"tunnel","field":"class","value":"motorway","type":"line"},"tunnel_motorway_link":{"source":"outdoors","layer":"tunnel","field":"class","value":"motorway_link","type":"line"},"tunnel_main":{"source":"outdoors","layer":"tunnel","field":"class","value":"main","type":"line"},"tunnel_street":{"source":"outdoors","layer":"tunnel","field":"class","value":["street","street_limited"],"type":"line","enabled":12},"tunnel_service":{"source":"outdoors","layer":"tunnel","field":"class","value":"service","type":"line","enabled":15},"tunnel_path":{"source":"outdoors","layer":"tunnel","field":"class","value":"path","type":"line"},"tunnel_path_footway":{"source":"outdoors","layer":"tunnel","field":"type","value":"footway","type":"line"},"tunnel_path_path":{"source":"outdoors","layer":"tunnel","field":"type","value":"path","type":"line"},"tunnel_path_cycleway":{"source":"outdoors","layer":"tunnel","field":"type","value":"cycleway","type":"line"},"tunnel_path_mtb":{"source":"outdoors","layer":"tunnel","field":"type","value":"mtb","type":"line"},"tunnel_path_steps":{"source":"outdoors","layer":"tunnel","field":"type","value":"steps","type":"line"},"tunnel_path_piste":{"source":"outdoors","layer":"tunnel","field":"type","value":"piste","type":"line"},"tunnel_major_rail":{"source":"outdoors","layer":"tunnel","field":"class","value":"major_rail","type":"line"},"bridge_motorway":{"source":"outdoors","layer":"bridge","field":"class","value":"motorway","type":"line"},"bridge_motorway_link":{"source":"outdoors","layer":"bridge","field":"class","value":"motorway_link","type":"line"},"bridge_main":{"source":"outdoors","layer":"bridge","field":"class","value":"main","type":"line"},"bridge_street":{"source":"outdoors","layer":"bridge","field":"class","value":["street","street_limited"],"type":"line","enabled":12},"bridge_service":{"source":"outdoors","layer":"bridge","field":"class","value":"service","type":"line","enabled":15},"bridge_path":{"source":"outdoors","layer":"bridge","type":"line"},"bridge_path_footway":{"source":"outdoors","layer":"bridge","field":"type","value":"footway","type":"line"},"bridge_path_path":{"source":"outdoors","layer":"bridge","field":"type","value":"path","type":"line"},"bridge_path_cycleway":{"source":"outdoors","layer":"bridge","field":"type","value":"cycleway","type":"line"},"bridge_path_mtb":{"source":"outdoors","layer":"bridge","field":"type","value":"mtb","type":"line"},"bridge_path_steps":{"source":"outdoors","layer":"bridge","field":"type","value":"steps","type":"line"},"bridge_path_piste":{"source":"outdoors","layer":"bridge","field":"type","value":"piste","type":"line"},"bridge_major_rail":{"source":"outdoors","layer":"bridge","field":"class","value":"major_rail","type":"line"},"bridge_aerialway":{"source":"outdoors","layer":"bridge","field":"class","value":"aerialway","type":"line"},"country_label":{"source":"outdoors","layer":"country_label","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":24,"feature_type":"point","type":"text","maxWidth":5},"country_label_line":{"source":"outdoors","layer":"country_label_line","type":"line"},"marine_label_line_1":{"source":"outdoors","layer":"marine_label","feature_type":"line","type":"text","field":"labelrank","value":1,"text_field":"{{name_en}}","path":"curve","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":30,"letterSpacing":0.4,"maxAngleDelta":0.5},"marine_label_line_2":{"source":"outdoors","layer":"marine_label","feature_type":"line","field":"labelrank","value":2,"type":"text","text_field":"{{name_en}}","path":"curve","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":24,"letterSpacing":0.2,"maxAngleDelta":0.5},"marine_label_line_3":{"source":"outdoors","layer":"marine_label","feature_type":"line","type":"text","field":"labelrank","value":3,"text_field":"{{name_en}}","path":"curve","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":18,"letterSpacing":0.1,"maxAngleDelta":0.5},"marine_label_line_other":{"source":"outdoors","layer":"marine_label","feature_type":"line","type":"text","field":"labelrank","value":[4,5,6],"text_field":"{{name_en}}","path":"curve","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":16,"letterSpacing":0.1,"maxAngleDelta":0.5},"marine_label_point_1":{"source":"outdoors","layer":"marine_label","feature_type":"point","type":"text","text_field":"{{name_en}}","path":"horizontal","field":"labelrank","value":1,"font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":30,"maxWidth":8,"letterSpacing":0.4,"lineHeight":2},"marine_label_point_2":{"source":"outdoors","layer":"marine_label","feature_type":"point","type":"text","text_field":"{{name_en}}","path":"horizontal","field":"labelrank","value":2,"font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":24,"maxWidth":8,"letterSpacing":0.2,"lineHeight":1.5},"marine_label_point_3":{"source":"outdoors","layer":"marine_label","feature_type":"point","type":"text","text_field":"{{name_en}}","path":"horizontal","field":"labelrank","value":3,"font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":18,"maxWidth":8,"letterSpacing":0.1,"lineHeight":1.3},"marine_label_point_other":{"source":"outdoors","layer":"marine_label","feature_type":"point","type":"text","text_field":"{{name_en}}","path":"horizontal","field":"labelrank","value":[4,5,6],"font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":16,"maxWidth":8,"letterSpacing":0.1,"lineHeight":1.2},"state_label":{"source":"outdoors","layer":"state_label","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Regular, Arial Unicode MS Regular","fontSize":16,"feature_type":"point","type":"text","enabled":4,"maxWidth":8},"place_label_city":{"source":"outdoors","layer":"place_label","field":"type","value":"city","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":20,"feature_type":"point","type":"text","maxWidth":8},"place_label_town":{"source":"outdoors","layer":"place_label","field":"type","value":"town","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":24,"feature_type":"point","type":"text","maxWidth":8},"place_label_village":{"source":"outdoors","layer":"place_label","field":"type","value":"village","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":22,"feature_type":"point","type":"text","maxWidth":8},"place_label_other":{"source":"outdoors","layer":"place_label","field":"type","value":["hamlet","suburb","neighbourhood"],"text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":18,"maxWidth":6,"feature_type":"point","type":"text"},"road_label_1":{"source":"outdoors","layer":"road_label","field":"class","value":["motorway","main"],"text_field":"{{name_en}}","path":"curve","padding":2,"font":"Open Sans Regular, Arial Unicode MS Regular","fontSize":18,"feature_type":"line","type":"text","maxAngleDelta":0.5},"road_label_2":{"source":"outdoors","layer":"road_label","field":"class","value":["street","street_limited"],"text_field":"{{name_en}}","path":"curve","padding":2,"font":"Open Sans Regular, Arial Unicode MS Regular","fontSize":16,"feature_type":"line","type":"text","maxAngleDelta":0.5},"road_label_3":{"source":"outdoors","layer":"road_label","field":"class","value":["service","driveway","path"],"text_field":"{{name_en}}","path":"curve","padding":2,"font":"Open Sans Regular, Arial Unicode MS Regular","fontSize":14,"feature_type":"line","type":"text","maxAngleDelta":0.5},"water_label":{"source":"outdoors","layer":"water_label","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":12,"feature_type":"point","type":"text","maxWidth":8},"waterway_label":{"source":"outdoors","layer":"waterway_label","text_field":"{{name_en}}","path":"curve","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":12,"feature_type":"line","type":"text","maxAngleDelta":0.5},"poi":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","field":"scalerank","value":[1,2],"size":12,"type":"point"},"poi_3":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","field":"scalerank","value":3,"size":12,"type":"point"},"poi_4_4":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","filter":["and",{"field":"scalerank","value":4},{"field":"localrank","operator":"<=","value":4}],"size":12,"type":"point"},"poi_4_16":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","filter":["and",{"field":"scalerank","value":4},{"field":"localrank","operator":"<=","value":16},{"field":"localrank","operator":">","value":4}],"size":12,"type":"point"},"poi_4_all":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","field":"scalerank","value":4,"size":12,"type":"point"},"poi_aerodrome":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","field":"maki","value":"airport","size":24,"type":"point"},"poi_label_1-2":{"source":"outdoors","layer":"poi_label","field":"scalerank","value":[1,2],"text_field":"{{name_en}}","path":"horizontal","padding":2,"maxWidth":10,"verticalAlignment":"top","translate":[0,-1],"font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":12,"feature_type":"point","type":"text"},"poi_label_3":{"source":"outdoors","layer":"poi_label","field":"scalerank","value":3,"text_field":"{{name_en}}","path":"horizontal","padding":2,"maxWidth":8,"verticalAlignment":"top","translate":[0,-1],"font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":11,"feature_type":"point","type":"text"},"poi_label_4_4":{"source":"outdoors","layer":"poi_label","filter":["and",{"field":"scalerank","value":4},{"field":"localrank","operator":"<=","value":4}],"text_field":"{{name_en}}","path":"horizontal","padding":2,"maxWidth":8,"verticalAlignment":"top","translate":[0,-1],"font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":10,"feature_type":"point","type":"text"},"poi_label_4_16":{"source":"outdoors","layer":"poi_label","filter":["and",{"field":"scalerank","value":4},{"field":"localrank","operator":"<=","value":16}],"text_field":"{{name_en}}","path":"horizontal","padding":2,"maxWidth":8,"verticalAlignment":"top","translate":[0,-1],"font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":10,"feature_type":"point","type":"text"},"poi_label_4_all":{"source":"outdoors","layer":"poi_label","field":"scalerank","value":4,"text_field":"{{name_en}}","path":"horizontal","padding":2,"maxWidth":8,"verticalAlignment":"top","translate":[0,-1],"font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":10,"feature_type":"point","type":"text"}},"structure":[{"name":"background","bucket":"background"},{"name":"landcover_snow","bucket":"landcover_snow"},{"name":"landcover_crop","bucket":"landcover_crop"},{"name":"landcover_grass","bucket":"landcover_grass"},{"name":"landcover_scrub","bucket":"landcover_scrub"},{"name":"landcover_wood","bucket":"landcover_wood"},{"name":"landuse_wood","bucket":"landuse_wood"},{"name":"landuse_school","bucket":"landuse_school"},{"name":"landuse_sand","bucket":"landuse_sand"},{"name":"landuse_pitch","bucket":"landuse_pitch"},{"name":"landuse_park","bucket":"landuse_park"},{"name":"landuse_industrial","bucket":"landuse_industrial"},{"name":"landuse_scrub","bucket":"landuse_scrub"},{"name":"landuse_grass","bucket":"landuse_grass"},{"name":"landuse_crop","bucket":"landuse_crop"},{"name":"landuse_rock","bucket":"landuse_rock"},{"name":"landuse_snow","bucket":"landuse_snow"},{"name":"landuse_hospital","bucket":"landuse_hospital"},{"name":"landuse_cemetery","bucket":"landuse_cemetery"},{"name":"overlay_wetland","bucket":"overlay_wetland"},{"name":"overlay_breakwater_pier","bucket":"overlay_breakwater_pier"},{"name":"waterway_river_canal","bucket":"waterway_river_canal"},{"name":"waterway_stream","bucket":"waterway_stream"},{"name":"building_shadow","bucket":"building"},{"name":"building","bucket":"building"},{"name":"building_wall","bucket":"building"},{"name":"hillshade_full_highlight","bucket":"hillshade_full_highlight"},{"name":"hillshade_medium_highlight","bucket":"hillshade_medium_highlight"},{"name":"hillshade_medium_shadow","bucket":"hillshade_medium_shadow"},{"name":"hillshade_full_shadow","bucket":"hillshade_full_shadow"},{"name":"contour_line_loud","bucket":"contour_line_10"},{"name":"contour_line_loud","bucket":"contour_line_5"},{"name":"contour_line_regular","bucket":"contour_line_other"},{"name":"barrier_line_gate","bucket":"barrier_line_gate"},{"name":"barrier_line_fence","bucket":"barrier_line_fence"},{"name":"barrier_line_hedge","bucket":"barrier_line_hedge"},{"name":"barrier_line_land","bucket":"barrier_line_land"},{"name":"barrier_line_land_fill","bucket":"barrier_line_land_fill"},{"name":"barrier_line_cliff","bucket":"barrier_line_cliff"},{"name":"water","bucket":"water"},{"name":"aeroway_fill","bucket":"aeroway_fill"},{"name":"aeroway_runway","bucket":"aeroway_runway"},{"name":"aeroway_taxiway","bucket":"aeroway_taxiway"},{"name":"tunnel_motorway_link_casing","bucket":"tunnel_motorway_link"},{"name":"tunnel_service_casing","bucket":"tunnel_service"},{"name":"tunnel_main_casing","bucket":"tunnel_main"},{"name":"tunnel_street_casing","bucket":"tunnel_street"},{"name":"tunnel_motorway_link","bucket":"tunnel_motorway_link"},{"name":"tunnel_service","bucket":"tunnel_service"},{"name":"tunnel_street","bucket":"tunnel_street"},{"name":"tunnel_main","bucket":"tunnel_main"},{"name":"tunnel_motorway_casing","bucket":"tunnel_motorway"},{"name":"tunnel_motorway","bucket":"tunnel_motorway"},{"name":"road_path_case","bucket":"tunnel_path"},{"name":"road_path_footway","bucket":"tunnel_path_footway"},{"name":"road_path_path","bucket":"tunnel_path_path"},{"name":"road_path_cycleway","bucket":"tunnel_path_cycleway"},{"name":"road_path_mtb","bucket":"tunnel_path_mtb"},{"name":"road_path_piste","bucket":"tunnel_path_piste"},{"name":"road_path_steps","bucket":"tunnel_path_steps"},{"name":"road_major_rail","bucket":"tunnel_major_rail"},{"name":"road_major_rail_hatching","bucket":"tunnel_major_rail"},{"name":"road_motorway_link_casing","bucket":"motorway_link"},{"name":"road_service_casing","bucket":"service"},{"name":"road_main_casing","bucket":"main"},{"name":"road_street_casing","bucket":"street"},{"name":"road_motorway_casing","bucket":"motorway"},{"name":"road_motorway_link","bucket":"motorway_link"},{"name":"road_service","bucket":"service"},{"name":"road_street","bucket":"street"},{"name":"road_main","bucket":"main"},{"name":"road_motorway","bucket":"motorway"},{"name":"road_path_case","bucket":"path"},{"name":"road_path_footway","bucket":"path_footway"},{"name":"road_path_path","bucket":"path_path"},{"name":"road_path_cycleway","bucket":"path_cycleway"},{"name":"road_path_mtb","bucket":"path_mtb"},{"name":"road_path_piste","bucket":"path_piste"},{"name":"road_path_steps","bucket":"path_steps"},{"name":"road_major_rail","bucket":"major_rail"},{"name":"road_major_rail_hatching","bucket":"major_rail"},{"name":"bridge_motorway_link_casing","bucket":"bridge_motorway_link"},{"name":"bridge_service_casing","bucket":"bridge_service"},{"name":"bridge_main_casing","bucket":"bridge_main"},{"name":"bridge_street_casing","bucket":"bridge_street"},{"name":"bridge_motorway_link","bucket":"bridge_motorway_link"},{"name":"bridge_service","bucket":"bridge_service"},{"name":"bridge_street","bucket":"bridge_street"},{"name":"bridge_main","bucket":"bridge_main"},{"name":"bridge_motorway_casing","bucket":"bridge_motorway"},{"name":"bridge_motorway","bucket":"bridge_motorway"},{"name":"road_path_footway","bucket":"bridge_path_footway"},{"name":"road_path_path","bucket":"bridge_path_path"},{"name":"road_path_cycleway","bucket":"bridge_path_cycleway"},{"name":"road_path_mtb","bucket":"bridge_path_mtb"},{"name":"road_path_piste","bucket":"bridge_path_piste"},{"name":"road_path_steps","bucket":"bridge_path_steps"},{"name":"bridge_aerialway_casing","bucket":"bridge_aerialway"},{"name":"bridge_aerialway","bucket":"bridge_aerialway"},{"name":"road_major_rail","bucket":"bridge_major_rail"},{"name":"road_major_rail_hatching","bucket":"bridge_major_rail"},{"name":"admin_l3","bucket":"admin_l3"},{"name":"admin_l2","bucket":"admin_l2"},{"name":"admin_maritime_cover","bucket":"admin_maritime"},{"name":"admin_maritime","bucket":"admin_maritime"},{"name":"country_label_line","bucket":"country_label_line"},{"name":"country_label","bucket":"country_label"},{"name":"marine_label_line_1","bucket":"marine_label_line_1"},{"name":"marine_label_line_2","bucket":"marine_label_line_2"},{"name":"marine_label_line_3","bucket":"marine_label_line_3"},{"name":"marine_label_line_other","bucket":"marine_label_line_other"},{"name":"marine_label_point_1","bucket":"marine_label_point_1"},{"name":"marine_label_point_2","bucket":"marine_label_point_2"},{"name":"marine_label_point_3","bucket":"marine_label_point_3"},{"name":"marine_label_point_other","bucket":"marine_label_point_other"},{"name":"state_label","bucket":"state_label"},{"name":"place_label_city","bucket":"place_label_city"},{"name":"place_label_town","bucket":"place_label_town"},{"name":"place_label_village","bucket":"place_label_village"},{"name":"place_label_other","bucket":"place_label_other"},{"name":"water_label","bucket":"water_label"},{"name":"poi_aerodrome","bucket":"poi_aerodrome"},{"name":"poi","bucket":"poi"},{"name":"poi_label_1-2","bucket":"poi_label_1-2"},{"name":"road_label_1","bucket":"road_label_1"},{"name":"poi_3","bucket":"poi_3"},{"name":"poi_label_3","bucket":"poi_label_3"},{"name":"road_label_2","bucket":"road_label_2"},{"name":"road_label_3","bucket":"road_label_3"},{"name":"contour_label","bucket":"contour_label"},{"name":"waterway_label","bucket":"waterway_label"},{"name":"poi_4_4","bucket":"poi_4_4"},{"name":"poi_label_4_4","bucket":"poi_label_4_4"},{"name":"poi_4_16","bucket":"poi_4_16"},{"name":"poi_label_4_16","bucket":"poi_label_4_16"},{"name":"poi_4_all","bucket":"poi_4_all"},{"name":"poi_label_4_all","bucket":"poi_label_4_all"}],"constants":{"land":"rgb(244,239,225)","water":"#cdd","water_dark":"#185869","crop":"#eeeed4","grass":"#e6e6cc","scrub":"#dfe5c8","wood":"#cee2bd","snow":"#f4f8ff","rock":"#ddd","sand":"#ffd","cemetery":"#edf4ed","pitch":"#fff","park":"#d4e4bc","piste":"blue","school":"#e8dfe0","hospital":"#f8eee0","builtup":"#f6faff","case":"#fff","motorway":"#cda0a0","main":"#ddc0b9","street":"#fff","text":"#666","text_stroke":"rgba(255,255,255,0.8)","country_text":"#222","marine_text":"#a0bdc0","water_text":"#185869","land_night":"#017293","water_night":"#103","water_dark_night":"#003366","crop_night":"#178d96","grass_night":"#23948a","scrub_night":"#31a186","wood_night":"#45b581","park_night":"#51bd8b","snow_night":"#5ad9fe","rock_night":"#999","sand_night":"#437162","cemetery_night":"#218c96","pitch_night":"rgba(255,255,255,0.2)","school_night":"#01536a","hospital_night":"#015e7a","builtup_night":"#014b60","admin_night":"#ffb680","text_night":"#fff","text_water_night":"#2a5b8a","text_stroke_night":"#103","text2_stroke_night":"rgba(1,69,89,0.8)","case_night":"#015e7a","street_case_night":"#015b76","motorway_night":"#bbdde7","main_night":"#64b2c9","street_night":"#0186ac","contour_night":"#ffff80","river_canal_width":["stops",{"z":11,"val":0.5},{"z":12,"val":1},{"z":14,"val":2},{"z":16,"val":3}],"stream_width":["stops",{"z":13,"val":0.25},{"z":14,"val":0.5},{"z":16,"val":1.5},{"z":18,"val":2}],"motorway_width":["stops",{"z":5,"val":0},{"z":6,"val":0.5},{"z":8,"val":0.8},{"z":10,"val":1},{"z":11,"val":1.2},{"z":12,"val":2},{"z":13,"val":3},{"z":14,"val":4},{"z":15,"val":6},{"z":16,"val":9},{"z":17,"val":12},{"z":18,"val":14}],"motorway_casing_width":["stops",{"z":7.5,"val":0.6},{"z":8,"val":0.8},{"z":10,"val":2.8},{"z":11,"val":3},{"z":12,"val":4},{"z":13,"val":5},{"z":14,"val":6.5},{"z":15,"val":9},{"z":16,"val":12},{"z":17,"val":15},{"z":18,"val":17}],"motorway_link_width":["stops",{"z":12,"val":1.2},{"z":14,"val":2},{"z":16,"val":3},{"z":18,"val":4}],"motorway_link_casing_width":["stops",{"z":12,"val":2.8},{"z":14,"val":3.5},{"z":16,"val":5},{"z":18,"val":6}],"main_width":["stops",{"z":5,"val":1},{"z":12,"val":1},{"z":13,"val":1.5},{"z":14,"val":2},{"z":15,"val":3},{"z":16,"val":6},{"z":17,"val":10},{"z":18,"val":12}],"main_casing_width":["stops",{"z":9,"val":2.9},{"z":12,"val":2.9},{"z":13,"val":3.5},{"z":14,"val":4},{"z":15,"val":5.5},{"z":16,"val":9},{"z":17,"val":12},{"z":18,"val":14}],"street_width":["stops",{"z":14.5,"val":0},{"z":15,"val":1.5},{"z":16,"val":3},{"z":17,"val":8}],"street_casing_width":["stops",{"z":13,"val":0.4},{"z":14,"val":1},{"z":15,"val":2.5},{"z":16,"val":4},{"z":17,"val":10}],"street_casing_opacity":["stops",{"z":14,"val":0},{"z":14.5,"val":1}],"service_casing_width":["stops",{"z":14,"val":0.5},{"z":15,"val":3},{"z":16,"val":3.5},{"z":17,"val":4},{"z":18,"val":5},{"z":19,"val":6}],"runway_width":["stops",{"z":10,"val":1},{"z":11,"val":2},{"z":12,"val":3},{"z":13,"val":5},{"z":14,"val":7},{"z":15,"val":11},{"z":16,"val":15},{"z":17,"val":19},{"z":18,"val":23}],"taxiway_width":["stops",{"z":10,"val":0.2},{"z":12,"val":0.2},{"z":13,"val":1},{"z":14,"val":1.5},{"z":15,"val":2},{"z":16,"val":3},{"z":17,"val":4},{"z":18,"val":5}],"aerialway_width":["stops",{"z":13.5,"val":0.8},{"z":14,"val":1.4},{"z":15,"val":1.6},{"z":16,"val":2},{"z":17,"val":2.4},{"z":18,"val":3}],"aerialway_casing_width":["stops",{"z":13.5,"val":2},{"z":14,"val":2.5},{"z":15,"val":3},{"z":16,"val":3.5},{"z":17,"val":4},{"z":22,"val":5}],"path_width":["stops",{"z":14,"val":1.2},{"z":15,"val":1.5},{"z":16,"val":1.8}],"admin_l2_width":["stops",{"z":2,"val":0.5},{"z":3,"val":0.7},{"z":4,"val":0.7},{"z":5,"val":0.8},{"z":6,"val":1},{"z":8,"val":2},{"z":10,"val":3}],"admin_l3_width":["stops",{"z":6,"val":0.6},{"z":8,"val":1},{"z":12,"val":2}],"road_label_1_size":["stops",{"z":13,"val":11},{"z":14,"val":12},{"z":15,"val":13},{"z":16,"val":14},{"z":17,"val":16},{"z":18,"val":18}],"road_label_2_size":["stops",{"z":13,"val":11},{"z":14,"val":12},{"z":16,"val":14},{"z":18,"val":16}],"road_label_3_size":["stops",{"z":15,"val":10},{"z":16,"val":12},{"z":18,"val":14}],"fence_width":["stops",{"z":17,"val":0.6},{"z":19,"val":1}],"hedge_width":["stops",{"z":16,"val":0.6},{"z":17,"val":1.2},{"z":19,"val":1.6}],"barrier_line_land_width":["stops",{"z":14,"val":0.4},{"z":15,"val":0.75},{"z":16,"val":1.5},{"z":17,"val":3},{"z":18,"val":6},{"z":19,"val":12},{"z":20,"val":24},{"z":21,"val":48}],"country_label_size":["stops",{"z":1,"val":14},{"z":12,"val":24}],"poi_label_1-2_size":["stops",{"z":15,"val":10},{"z":16,"val":11},{"z":17,"val":12}],"poi_label_3_size":["stops",{"z":16,"val":10},{"z":17,"val":11}],"hillshade_prerender":["stops",{"z":11,"val":0},{"z":12,"val":1}],"hillshade_prerender_size":["stops",{"z":11,"val":1056},{"z":12,"val":512},{"z":13,"val":256}]},"classes":[{"name":"default","layers":{"background":{"color":"land"},"admin_maritime_cover":{"color":"water","width":5},"admin_maritime":{"color":"#c0d6d6","width":["stops",{"z":6,"val":1},{"z":8,"val":2},{"z":12,"val":3}]},"admin_l2":{"color":"#88a","width":"admin_l2_width"},"admin_l3":{"color":"#88a","dasharray":[60,20],"opacity":["stops",{"z":4,"val":0},{"z":6,"val":1}],"width":"admin_l3_width"},"waterway_river_canal":{"color":"#87abaf","width":"river_canal_width"},"waterway_stream":{"color":"#87abaf","width":"stream_width"},"barrier_line_gate":{"width":2.5,"color":"#aab"},"barrier_line_fence":{"color":"#aeada3","width":"fence_width"},"barrier_line_hedge":{"color":"#8de99b","width":"hedge_width"},"barrier_line_land":{"color":"land","width":"barrier_line_land_width"},"barrier_line_land_fill":{"color":"land"},"barrier_line_cliff":{"color":"#987","width":4},"landcover_wood":{"color":"wood","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_scrub":{"color":"scrub","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_grass":{"color":"grass","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_crop":{"color":"crop"},"landcover_snow":{"color":"snow"},"landuse_wood":{"color":"wood"},"landuse_scrub":{"color":"scrub"},"landuse_grass":{"color":"grass"},"landuse_crop":{"color":"crop"},"landuse_snow":{"color":"snow"},"landuse_rock":{"color":"rock"},"landuse_sand":{"color":"sand"},"landuse_park":{"color":"park"},"landuse_cemetery":{"color":"cemetery"},"landuse_hospital":{"color":"hospital"},"landuse_school":{"color":"school"},"landuse_pitch":{"color":"rgba(255,255,255,0.5)","stroke":"pitch"},"landuse_industrial":{"color":"rgba(246,250,255,0.5)"},"overlay_wetland":{"color":"rgba(210,225,225,0.2)","image":"wetland_noveg_64"},"overlay_breakwater_pier":{"color":"land"},"hillshade_full_shadow":{"color":"#103","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":15,"val":0.08},{"z":16,"val":0.075},{"z":17,"val":0.05},{"z":18,"val":0.05},{"z":19,"val":0.025}]},"hillshade_medium_shadow":{"color":"#206","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":15,"val":0.08},{"z":16,"val":0.075},{"z":17,"val":0.05},{"z":18,"val":0.05},{"z":19,"val":0.025}]},"hillshade_full_highlight":{"color":"#fffff3","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":15,"val":0.3},{"z":16,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.2},{"z":19,"val":0.1}]},"hillshade_medium_highlight":{"color":"#ffd","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":15,"val":0.3},{"z":16,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.2},{"z":19,"val":0.1}]},"contour_line_loud":{"color":"#008","width":0.9,"opacity":["stops",{"z":12,"val":0.05},{"z":13,"val":0.11}]},"contour_line_regular":{"color":"#008","width":0.5,"opacity":["stops",{"z":12,"val":0.05},{"z":13,"val":0.11}]},"contour_label":{"color":"text","stroke":"land","strokeWidth":0.3,"strokeBlur":3,"size":10},"water":{"color":"water","stroke":"#a2bdc0"},"aeroway_fill":{"color":"#ddd"},"aeroway_runway":{"color":"#ddd","width":"runway_width"},"aeroway_taxiway":{"color":"#ddd","width":"taxiway_width"},"building":{"color":"#ebe7db"},"building_wall":{"color":"#ebe7db","stroke":"#d5d1c6","opacity":["stops",{"z":16.5,"val":0},{"z":17,"val":0.7}]},"building_shadow":{"color":"#d5d1c6","stroke":"#d5d1c6","translate":[1,1],"opacity":["stops",{"z":16.5,"val":0},{"z":17,"val":1}]},"tunnel_motorway_casing":{"color":"case","dasharray":[6,6],"width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"tunnel_motorway":{"color":"#e6cec7","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"tunnel_main_casing":{"color":"case","dasharray":[6,6],"width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"tunnel_main":{"color":"#e6cec7","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"tunnel_motorway_link_casing":{"color":"case","dasharray":[6,6],"width":"motorway_link_casing_width"},"tunnel_motorway_link":{"color":"#e6cec7","width":"motorway_link_width"},"tunnel_street_casing":{"color":"#d9d5c6","width":"street_casing_width","opacity":"street_casing_opacity"},"tunnel_street":{"color":"#d9d5c6","width":"street_width"},"tunnel_service_casing":{"color":"#000","opacity":0.04,"dasharray":[6,6],"width":"service_casing_width"},"tunnel_service":{"color":"#e6cec7","width":2},"road_motorway_casing":{"color":"case","width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"road_motorway":{"color":"motorway","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"road_main_casing":{"color":"case","width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"road_main":{"color":"main","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"road_motorway_link_casing":{"color":"case","width":"motorway_link_casing_width"},"road_motorway_link":{"color":"motorway","width":"motorway_link_width"},"road_street_casing":{"color":"#d9d5c6","width":"street_casing_width","opacity":"street_casing_opacity"},"road_street":{"color":"street","width":"street_width"},"road_service_casing":{"color":"#000","opacity":0.04,"width":"service_casing_width"},"road_service":{"color":"street","width":2},"road_path_case":{"color":"#ffd","opacity":0.4,"width":["stops",{"z":15,"val":3},{"z":16,"val":4}]},"road_path_footway":{"color":"#bba","dasharray":[10,4],"width":"path_width"},"road_path_path":{"color":"#987","dasharray":[10,4],"opacity":0.8,"width":["stops",{"z":14,"val":0.8},{"z":15,"val":0.9},{"z":16,"val":1.2}]},"road_path_cycleway":{"color":"#488","dasharray":[10,4],"width":"path_width"},"road_path_mtb":{"color":"#488","dasharray":[12,4],"width":"path_width"},"road_path_piste":{"color":"#87b","dasharray":[8,4],"width":"path_width"},"road_path_steps":{"color":"#bba","dasharray":[10,4],"width":4},"road_major_rail":{"color":"#c8c4c0","width":0.8},"road_major_rail_hatching":{"color":"#c8c4c0","dasharray":[2,31],"width":5},"bridge_motorway_casing":{"color":"case","width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"bridge_motorway":{"color":"motorway","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"bridge_main_casing":{"color":"case","width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"bridge_main":{"color":"main","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"bridge_motorway_link_casing":{"color":"case","width":"motorway_link_casing_width"},"bridge_motorway_link":{"color":"motorway","width":"motorway_link_width"},"bridge_street_casing":{"color":"#d9d5c6","width":"street_casing_width","opacity":"street_casing_opacity"},"bridge_street":{"color":"street","width":"street_width"},"bridge_service_casing":{"color":"#000","opacity":0.04,"width":"service_casing_width"},"bridge_service":{"color":"street","width":2},"bridge_aerialway_casing":{"color":"white","opacity":0.5,"width":"aerialway_casing_width"},"bridge_aerialway":{"color":"#876","opacity":0.5,"width":"aerialway_width"},"country_label":{"color":"country_text","stroke":"rgba(255,255,255,0.5)","strokeWidth":0.5,"size":"country_label_size"},"country_label_line":{"color":"country_text","width":0.5,"opacity":0.5},"marine_label_line_1":{"color":"marine_text","size":["stops",{"z":3,"val":20},{"z":4,"val":25},{"z":5,"val":30},{"z":22,"val":30}],"stroke":"water"},"marine_label_line_2":{"color":"marine_text","size":["stops",{"z":3,"val":13},{"z":4,"val":14},{"z":5,"val":20},{"z":6,"val":24},{"z":22,"val":24}],"stroke":"water"},"marine_label_line_3":{"color":"marine_text","size":["stops",{"z":3,"val":12},{"z":4,"val":13},{"z":5,"val":15},{"z":6,"val":18},{"z":22,"val":18}],"stroke":"water"},"marine_label_line_other":{"color":"marine_text","size":["stops",{"z":4,"val":12},{"z":5,"val":14},{"z":6,"val":16},{"z":22,"val":16}],"stroke":"water"},"marine_label_point_1":{"color":"marine_text","size":["stops",{"z":3,"val":20},{"z":4,"val":25},{"z":5,"val":30},{"z":22,"val":30}],"stroke":"water"},"marine_label_point_2":{"color":"marine_text","size":["stops",{"z":3,"val":13},{"z":4,"val":14},{"z":5,"val":20},{"z":6,"val":24},{"z":22,"val":24}],"stroke":"water"},"marine_label_point_3":{"color":"marine_text","size":["stops",{"z":3,"val":12},{"z":4,"val":13},{"z":5,"val":15},{"z":6,"val":18},{"z":22,"val":18}],"stroke":"water"},"marine_label_point_other":{"color":"marine_text","size":["stops",{"z":4,"val":12},{"z":5,"val":14},{"z":6,"val":16},{"z":22,"val":16}],"stroke":"water"},"state_label":{"color":"#333","strokeWidth":0.4,"strokeBlur":1,"stroke":"rgba(244,239,225,0.8)","size":["stops",{"z":3.99,"val":0},{"z":4,"val":10},{"z":9.99,"val":16},{"z":10,"val":0}]},"place_label_city":{"color":"#444","strokeWidth":0.4,"stroke":"text_stroke","size":["stops",{"z":3.99,"val":0},{"z":4,"val":10},{"z":7,"val":14},{"z":14.99,"val":20},{"z":15,"val":0}]},"place_label_town":{"color":"#716656","strokeWidth":0.3,"strokeBlur":2,"stroke":"text_stroke","size":["stops",{"z":9,"val":10},{"z":12,"val":13},{"z":14,"val":17},{"z":16,"val":22}]},"place_label_village":{"color":"#635644","strokeWidth":0.3,"strokeBlur":2,"stroke":"text_stroke","size":["stops",{"z":9,"val":8},{"z":12,"val":10},{"z":14,"val":14},{"z":16,"val":16},{"z":17,"val":20}]},"place_label_other":{"color":"#7d6c55","stroke":"text_stroke","size":["stops",{"z":13,"val":11},{"z":14,"val":12},{"z":16,"val":14},{"z":18,"val":18}]},"road_label_1":{"color":"#585042","stroke":"land","strokeWidth":0.6,"strokeBlur":2,"size":"road_label_1_size"},"road_label_2":{"color":"#585042","stroke":"land","strokeWidth":0.6,"strokeBlur":2,"size":"road_label_2_size"},"road_label_3":{"color":"#585042","stroke":"land","strokeWidth":0.6,"strokeBlur":2,"size":"road_label_3_size"},"water_label":{"color":"water_dark","stroke":"rgba(255,255,255,0.75)"},"waterway_label":{"color":"water_dark","strokeWidth":0.4,"strokeBlur":2,"stroke":"text_stroke"},"poi":{"antialias":false},"poi_3":{"antialias":false,"opacity":["stops",{"z":17.5,"val":0},{"z":17.75,"val":1}]},"poi_4_4":{"antialias":false,"opacity":["stops",{"z":18,"val":0},{"z":18.25,"val":1}]},"poi_4_16":{"antialias":false,"opacity":["stops",{"z":18.5,"val":0},{"z":18.75,"val":1}]},"poi_4_all":{"antialias":false,"opacity":["stops",{"z":18.75,"val":0},{"z":19,"val":1}]},"poi_label_1-2":{"color":"#444","size":"poi_label_1-2_size","stroke":"land","strokeWidth":0.3,"strokeBlur":1},"poi_label_3":{"color":"#444","size":"poi_label_3_size","stroke":"land","strokeWidth":0.3,"strokeBlur":1,"opacity":["stops",{"z":17.5,"val":0},{"z":17.75,"val":1}]},"poi_label_4_4":{"color":"#444","size":10,"opacity":["stops",{"z":18,"val":0},{"z":17.25,"val":1}],"stroke":"land","strokeWidth":0.3,"strokeBlur":1},"poi_label_4_16":{"color":"#444","size":10,"opacity":["stops",{"z":18.25,"val":0},{"z":18.75,"val":1}],"stroke":"land","strokeWidth":0.3,"strokeBlur":1},"poi_label_4_all":{"color":"#444","size":10,"opacity":["stops",{"z":18.75,"val":0},{"z":19,"val":1}],"stroke":"land","strokeWidth":0.3,"strokeBlur":1},"poi_aerodrome":{"opacity":["stops",{"z":13,"val":0},{"z":13.25,"val":1}],"antialias":false}}},{"name":"night","layers":{"background":{"color":"land_night"},"admin_maritime_cover":{"color":"water_night","width":5},"admin_maritime":{"color":"#0a1347","width":["stops",{"z":6,"val":1},{"z":8,"val":2},{"z":12,"val":3}]},"admin_l2":{"color":"admin_night","width":"admin_l2_width"},"admin_l3":{"color":"admin_night","dasharray":[60,20],"opacity":["stops",{"z":4,"val":0},{"z":6,"val":1}],"width":"admin_l3_width"},"waterway_river_canal":{"color":"rgb(10,20,71)","width":"river_canal_width"},"waterway_stream":{"color":"rgb(10,20,71)","width":"stream_width"},"barrier_line_gate":{"width":2.5,"color":"#59596f"},"barrier_line_fence":{"color":"#014b61","width":"fence_width"},"barrier_line_hedge":{"color":"#2e7a57","width":"hedge_width"},"barrier_line_land":{"color":"land_night","width":"barrier_line_land_width"},"barrier_line_land_fill":{"color":"land_night"},"barrier_line_cliff":{"color":"#63574b","width":4},"landcover_wood":{"color":"wood_night","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_scrub":{"color":"scrub_night","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_grass":{"color":"grass_night","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_crop":{"color":"crop_night"},"landcover_snow":{"color":"snow_night"},"landuse_wood":{"color":"wood_night","opacity":0.8},"landuse_scrub":{"color":"scrub_night","opacity":0.8},"landuse_grass":{"color":"grass_night","opacity":0.8},"landuse_crop":{"color":"crop_night","opacity":0.8},"landuse_snow":{"color":"snow_night","opacity":0.8},"landuse_rock":{"color":"rock_night","opacity":0.8},"landuse_sand":{"color":"sand_night","opacity":0.8},"landuse_park":{"color":"park_night"},"landuse_cemetery":{"color":"cemetery_night"},"landuse_hospital":{"color":"hospital_night"},"landuse_school":{"color":"school_night"},"landuse_pitch":{"color":"pitch_night","stroke":"pitch"},"landuse_industrial":{"color":"builtup_night"},"overlay_wetland":{"color":"rgba(210,225,225,0.2)","image":"wetland_noveg_64"},"overlay_breakwater_pier":{"color":"land_night"},"hillshade_full_shadow":{"color":"#103","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":16,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.1},{"z":19,"val":0.05}]},"hillshade_medium_shadow":{"color":"#206","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":16,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.1},{"z":19,"val":0.05}]},"hillshade_full_highlight":{"color":"#fdfdad","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":14,"val":0.4},{"z":15,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.1},{"z":19,"val":0.05}]},"hillshade_medium_highlight":{"color":"#ffe1b7","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":15,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.15},{"z":19,"val":0.05}]},"contour_line_loud":{"color":"contour_night","width":0.9,"opacity":["stops",{"z":12,"val":0.1},{"z":13,"val":0.2}]},"contour_line_regular":{"color":"contour_night","width":0.5,"opacity":["stops",{"z":12,"val":0.1},{"z":13,"val":0.4}]},"contour_label":{"color":"contour_night","stroke":"land_night","strokeWidth":0.3,"strokeBlur":3,"size":10},"water":{"color":"water_night","stroke":"water_dark_night"},"aeroway_fill":{"color":"#367"},"aeroway_runway":{"color":"#367","width":"runway_width"},"aeroway_taxiway":{"color":"#367","width":"taxiway_width"},"building":{"color":"#027797"},"building_wall":{"color":"#027797","stroke":"#026688","opacity":["stops",{"z":16.5,"val":0},{"z":17,"val":0.7}]},"building_shadow":{"color":"#026688","stroke":"#026688","translate":[1,1],"opacity":["stops",{"z":16.5,"val":0},{"z":17,"val":1}]},"tunnel_motorway_casing":{"color":"case_night","dasharray":[6,6],"width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"tunnel_motorway":{"color":"#78b0c1","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"tunnel_main_casing":{"color":"case_night","dasharray":[6,6],"width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"tunnel_main":{"color":"#78b0c1","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"tunnel_motorway_link_casing":{"color":"case_night","dasharray":[6,6],"width":"motorway_link_casing_width"},"tunnel_motorway_link":{"color":"#78b0c1","width":"motorway_link_width"},"tunnel_street_casing":{"color":"street_case_night","width":"street_casing_width","opacity":"street_casing_opacity"},"tunnel_street":{"color":"street_night","width":"street_width"},"tunnel_service_casing":{"color":"#000","opacity":0.04,"dasharray":[6,6],"width":"service_casing_width"},"tunnel_service":{"color":"#017ca0","width":2},"road_motorway_casing":{"color":"case_night","width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"road_motorway":{"color":"motorway_night","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"road_main_casing":{"color":"case_night","width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"road_main":{"color":"main_night","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"road_motorway_link_casing":{"color":"case_night","width":"motorway_link_casing_width"},"road_motorway_link":{"color":"motorway_night","width":"motorway_link_width"},"road_street_casing":{"color":"street_case_night","width":"street_casing_width","opacity":"street_casing_opacity"},"road_street":{"color":"street_night","width":"street_width"},"road_service_casing":{"color":"#000","opacity":0.04,"width":"service_casing_width"},"road_service":{"color":"street_night","width":2},"road_path_case":{"color":"land_night","opacity":0.2},"road_path_footway":{"color":"#fff","dasharray":[10,4],"width":"path_width"},"road_path_path":{"color":"#fff","dasharray":[10,4],"opacity":0.8,"width":["stops",{"z":14,"val":0.8},{"z":15,"val":0.9},{"z":16,"val":1.2}]},"road_path_cycleway":{"color":"#94e6ff","dasharray":[10,4],"width":"path_width"},"road_path_mtb":{"color":"#94e6ff","dasharray":[12,4],"width":"path_width"},"road_path_piste":{"color":"#715dae","dasharray":[8,4],"width":"path_width"},"road_path_steps":{"color":"#016684","dasharray":[10,4],"opacity":0.3,"width":6},"road_major_rail":{"color":"#c8c4c0","width":0.8},"road_major_rail_hatching":{"color":"#c8c4c0","dasharray":[2,31],"width":5},"bridge_motorway_casing":{"color":"case_night","width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"bridge_motorway":{"color":"motorway_night","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"bridge_main_casing":{"color":"case_night","width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"bridge_main":{"color":"main_night","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"bridge_motorway_link_casing":{"color":"case_night","width":"motorway_link_casing_width"},"bridge_motorway_link":{"color":"motorway_night","width":"motorway_link_width"},"bridge_street_casing":{"color":"street_case_night","width":"street_casing_width","opacity":"street_casing_opacity"},"bridge_street":{"color":"street_night","width":"street_width"},"bridge_service_casing":{"color":"#000","opacity":0.04,"width":"service_casing_width"},"bridge_service":{"color":"street_night","width":2},"bridge_aerialway_casing":{"color":"white","opacity":0.5,"width":"aerialway_casing_width"},"bridge_aerialway":{"color":"#876","opacity":0.5,"width":"aerialway_width"},"country_label":{"color":"text_night","stroke":"text2_stroke_night","strokeWidth":0.4,"strokeBlur":2,"size":"country_label_size"},"country_label_line":{"color":"text_night","width":0.5,"opacity":0.5},"marine_label_line_1":{"color":"water_dark_night","size":["stops",{"z":3,"val":20},{"z":4,"val":25},{"z":5,"val":30},{"z":22,"val":30}],"stroke":"water_night"},"marine_label_line_2":{"color":"water_dark_night","size":["stops",{"z":3,"val":13},{"z":4,"val":14},{"z":5,"val":20},{"z":6,"val":24},{"z":22,"val":24}],"stroke":"water_night"},"marine_label_line_3":{"color":"water_dark_night","size":["stops",{"z":3,"val":12},{"z":4,"val":13},{"z":5,"val":15},{"z":6,"val":18},{"z":22,"val":18}],"stroke":"water_night"},"marine_label_line_other":{"color":"water_dark_night","size":["stops",{"z":4,"val":12},{"z":5,"val":14},{"z":6,"val":16},{"z":22,"val":16}],"stroke":"water_night"},"marine_label_point_1":{"color":"water_dark_night","size":["stops",{"z":3,"val":20},{"z":4,"val":25},{"z":5,"val":30},{"z":22,"val":30}],"stroke":"water_night"},"marine_label_point_2":{"color":"water_dark_night","size":["stops",{"z":3,"val":13},{"z":4,"val":14},{"z":5,"val":20},{"z":6,"val":24},{"z":22,"val":24}],"stroke":"water_night"},"marine_label_point_3":{"color":"water_dark_night","size":["stops",{"z":3,"val":12},{"z":4,"val":13},{"z":5,"val":15},{"z":6,"val":18},{"z":22,"val":18}],"stroke":"water_night"},"marine_label_point_other":{"color":"water_dark_night","size":["stops",{"z":4,"val":12},{"z":5,"val":14},{"z":6,"val":16},{"z":22,"val":16}],"stroke":"water_night"},"state_label":{"color":"#fff","strokeWidth":0.4,"strokeBlur":1,"stroke":"land_night","size":["stops",{"z":3.99,"val":0},{"z":4,"val":10},{"z":9.99,"val":16},{"z":10,"val":0}]},"place_label_city":{"color":"#fff","strokeWidth":0.4,"stroke":"text2_stroke_night","size":["stops",{"z":3.99,"val":0},{"z":4,"val":10},{"z":7,"val":14},{"z":14.99,"val":20},{"z":15,"val":0}]},"place_label_town":{"color":"text_night","strokeWidth":0.3,"strokeBlur":2,"stroke":"text2_stroke_night","size":["stops",{"z":9,"val":10},{"z":12,"val":13},{"z":14,"val":17},{"z":16,"val":22}]},"place_label_village":{"color":"text_night","strokeWidth":0.3,"strokeBlur":2,"stroke":"text2_stroke_night","size":["stops",{"z":9,"val":8},{"z":12,"val":10},{"z":14,"val":14},{"z":16,"val":16},{"z":17,"val":20}]},"place_label_other":{"color":"text_night","stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":2,"size":["stops",{"z":13,"val":11},{"z":14,"val":12},{"z":16,"val":14},{"z":18,"val":18}]},"road_label_1":{"color":"text_night","stroke":"text2_stroke_night","strokeWidth":0.5,"strokeBlur":3,"size":"road_label_1_size"},"road_label_2":{"color":"text_night","stroke":"text2_stroke_night","strokeWidth":0.5,"strokeBlur":3,"size":"road_label_2_size"},"road_label_3":{"color":"text_night","stroke":"text2_stroke_night","strokeWidth":0.5,"strokeBlur":3,"size":"road_label_3_size"},"water_label":{"color":"text_water_night","stroke":"water_night"},"waterway_label":{"color":"text_water_night","stroke":"water_night"},"poi":{"color":"white","antialias":false},"poi_3":{"antialias":false,"opacity":["stops",{"z":17.5,"val":0},{"z":17.75,"val":1}]},"poi_4_4":{"antialias":false,"opacity":["stops",{"z":18,"val":0},{"z":17.25,"val":1}]},"poi_4_16":{"antialias":false,"opacity":["stops",{"z":18.5,"val":0},{"z":18.75,"val":1}]},"poi_4_all":{"antialias":false,"opacity":["stops",{"z":18.75,"val":0},{"z":19,"val":1}]},"poi_label_1-2":{"color":"#fff","size":"poi_label_1-2_size","stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":1},"poi_label_3":{"color":"#fff","size":"poi_label_3_size","stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":1,"opacity":["stops",{"z":17.5,"val":0},{"z":17.75,"val":1}]},"poi_label_4_4":{"color":"#fff","size":10,"opacity":["stops",{"z":18,"val":0},{"z":18.25,"val":1}],"stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":1},"poi_label_4_16":{"color":"#fff","size":10,"opacity":["stops",{"z":18.5,"val":0},{"z":18.75,"val":1}],"stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":1},"poi_label_4_all":{"color":"#fff","size":10,"opacity":["stops",{"z":18.75,"val":0},{"z":19,"val":1}],"stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":1},"poi_aerodrome":{"opacity":["stops",{"z":13,"val":0},{"z":13.25,"val":1}],"antialias":false}}}],"sprite":"/img/maki-sprite"} \ No newline at end of file diff --git a/android/java/lib/src/main/res/values/attrs.xml b/android/java/lib/src/main/res/values/attrs.xml new file mode 100644 index 000000000..b42f729c9 --- /dev/null +++ b/android/java/lib/src/main/res/values/attrs.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/java/lib/src/main/res/values/strings.xml b/android/java/lib/src/main/res/values/strings.xml new file mode 100644 index 000000000..f11f7450a --- /dev/null +++ b/android/java/lib/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + + diff --git a/android/java/settings.gradle b/android/java/settings.gradle new file mode 100644 index 000000000..0959cbb33 --- /dev/null +++ b/android/java/settings.gradle @@ -0,0 +1,2 @@ +include ':app' +include ':lib' From 18d5501279e17f5f93c4c450586f1c3ad29be316 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 00:51:08 +1100 Subject: [PATCH 041/415] Add java to build --- .travis.yml | 1 + Makefile | 8 +++++++- scripts/travis_before_install.sh | 12 ++++++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 952405ea0..f76c9f0c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,7 @@ before_install: - source ./scripts/travis_helper.sh - source ./scripts/flags.sh - export ANDROID_NDK_PATH="$(pwd)/android-ndk-r10c" +- export JAVA_HOME="$(pwd)/jdk1.7.0_71" - ./scripts/travis_before_install.sh - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then export LD_LIBRARY_PATH=`mason prefix mesa 10.3.1`/lib; fi - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then glxinfo; fi diff --git a/Makefile b/Makefile index 20a1cc870..a6a452f63 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,11 @@ android: export PATH="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env PATH`:${PATH}" && \ MASON_PLATFORM=android ./configure config-android.gypi && \ deps/run_gyp android/mapboxgl-app.gyp -Iconfig-android.gypi -Dplatform=android --depth=. --generator-output=./build/android -f make-android && \ - $(MAKE) -C build/android BUILDTYPE=$(BUILDTYPE) V=$(V) androidapp + $(MAKE) -C build/android BUILDTYPE=$(BUILDTYPE) V=$(V) androidapp && \ + mkdir -p android/java/lib/src/main/jniLibs/armeabi-v7a && \ + cp build/android/out/$(BUILDTYPE)/lib.target/libmapbox-gl.so android/java/lib/src/main/jniLibs/armeabi-v7a/libmapbox-gl.so && \ + cd android/java && \ + ./gradlew build ##### Test cases ############################################################### @@ -147,6 +151,8 @@ clean: clear_xcode_cache -rm -rf ./build/ -rm -rf ./macosx/build/ -rm -rf ./config.gypi ./config-ios.gypi ./config-android.gypi + -rm -rf ./android/java/build ./android/java/app/build ./android/java/lib/build + -rm -rf ./android/java/lib/src/main/jniLibs distclean: clean -rm -rf ./mason_packages diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 962a6901d..09818ee7a 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -4,8 +4,8 @@ set -o pipefail if [[ "${MASON_PLATFORM}" == "android" ]]; then - echo "debug: unpacking" - #mapbox_time "unpacking NDK" \ + echo "debug: installing 7z" + #mapbox_time "installing 7z" \ MASON_PLATFORM= ./.mason/mason install 7z 9.20 echo "debug: setting 7z path" @@ -22,6 +22,14 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then #mapbox_time "unpacking NDK" \ $SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > .tmp-ndk-log rm .tmp-ndk-log + + echo "debug: fetching JDK" + #mapbox_time "fetching JDK" \ + wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-linux-x64.tar.gz + + echo "debug: unpacking JDK" + #mapbox_time "unpacking JDK" \ + tar -xzf ./jdk-7u71-linux-x64.tar.gz fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From aebd3fdca56d7541784eb7f5ee3bd35dcd1d9288 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 01:22:40 +1100 Subject: [PATCH 042/415] Install android sdk --- .travis.yml | 2 ++ scripts/travis_before_install.sh | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/.travis.yml b/.travis.yml index f76c9f0c0..bc3fddfab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,9 @@ before_install: - source ./scripts/travis_helper.sh - source ./scripts/flags.sh - export ANDROID_NDK_PATH="$(pwd)/android-ndk-r10c" +- export NDK_HOME="$(pwd)/android-ndk-r10c" - export JAVA_HOME="$(pwd)/jdk1.7.0_71" +- export ANDROID_HOME="$(pwd)/android-sdk-linux" - ./scripts/travis_before_install.sh - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then export LD_LIBRARY_PATH=`mason prefix mesa 10.3.1`/lib; fi - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then glxinfo; fi diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 09818ee7a..903a8f41b 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -30,6 +30,18 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: unpacking JDK" #mapbox_time "unpacking JDK" \ tar -xzf ./jdk-7u71-linux-x64.tar.gz + + echo "debug: fetching SDK" + #mapbox_time "fetching SDK" \ + wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz + + echo "debug: unpacking SDK" + #mapbox_time "unpacking SDK" \ + tar -xzf ./android-sdk_r23.0.2-linux.tgz + + echo "debug: installing SDK" + #mapbox_time "installing SDK" \ + echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19 fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From c170b4293c65c0bbf3db9221b6910c6a5a8f7e6a Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 01:26:26 +1100 Subject: [PATCH 043/415] Clean up gradle output --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index bc3fddfab..6d6a70b55 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ env: - secure: "Cbvap9ubVKgjPe3hUhI6JGeDZzBXHpOG9RaYKh+SdoIPhKnlJiNOYm1egomi+e4uqJInlFKuVHTw7Ng9Cun6Zm0jIxpkSchv1GpsR7hmB3UGnGed19Dw8121FwuUaktN+4YnbVlsyd+u8EHD3+h58t4eELrLrZolM4rS7DL6caA=" - secure: "RiBIBfVhhaMjU5ksuwJO3shdvG9FpinBjdSv4co9jg9171SR8edNriedHjVKSIeBhSGNmZmX+twS3dJS/By6tl/LKh9sTynA+ZAYYljkE7jn881B/gMrlYvdAA6og5KvkhV1/0iJWlhuZrMTkhpDR200iLgg3EWBhWjltzmDW/I=" - LD_LIBRARY_PATH: '/usr/local/lib' + - TERM=dumb before_install: - source ./scripts/local_mason.sh From f353b5ba55fdb2ad0a8924e2e06262433ff26356 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 07:44:22 +1100 Subject: [PATCH 044/415] Add find for travis debug --- scripts/travis_before_install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 903a8f41b..b66adb572 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -42,6 +42,8 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: installing SDK" #mapbox_time "installing SDK" \ echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19 + + find fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From 12f58f0324e7ca1b4240bdae70cdc9725b4a8843 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 08:11:05 +1100 Subject: [PATCH 045/415] Add LINK to avoid flock --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index a6a452f63..8afb54c1a 100644 --- a/Makefile +++ b/Makefile @@ -74,6 +74,7 @@ android: export CXX="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ export CC="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CC`" && \ export LD="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env LD`" && \ + export LINK="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ export AR="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env AR`" && \ export RANLIB="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env RANLIB`" && \ export LDFLAGS="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env LDFLAGS` ${LDFLAGS}" && \ From 5a97eb3888f68d2632bb83c8f425824bfcbcf71b Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 08:28:07 +1100 Subject: [PATCH 046/415] Be more specific with find --- scripts/travis_before_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index b66adb572..672b697e8 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -43,7 +43,7 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then #mapbox_time "installing SDK" \ echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19 - find + find android-sdk-linux/ fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From cbe4df96855aa39aa6d0315aa8fbf2cfef76553d Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 08:43:17 +1100 Subject: [PATCH 047/415] Find with full path --- .mason | 2 +- scripts/travis_before_install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.mason b/.mason index 4c53b0a3a..a3ebabd0c 160000 --- a/.mason +++ b/.mason @@ -1 +1 @@ -Subproject commit 4c53b0a3a6c92987e16733d3dc2744810c6f7ba3 +Subproject commit a3ebabd0c6d57bbe2687b8a46e25aeff0ca2ab40 diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 672b697e8..fae7fdb7c 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -43,7 +43,7 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then #mapbox_time "installing SDK" \ echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19 - find android-sdk-linux/ + find find $PWD/android-sdk-linux/ fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From b1bfbbcc8f1b462c0980a1c214601ebe40b57a7c Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 08:58:57 +1100 Subject: [PATCH 048/415] Fix aapt issue --- scripts/travis_before_install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index fae7fdb7c..2dc48787a 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -41,9 +41,8 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: installing SDK" #mapbox_time "installing SDK" \ + sudo apt-get -y install lib32stdc++6 lib32z1 echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19 - - find find $PWD/android-sdk-linux/ fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From ff802228b7c5be366986827c1b63a9c5e1527525 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 17:56:27 +1100 Subject: [PATCH 049/415] Use latest SDK build --- android/java/app/build.gradle | 4 ++-- android/java/lib/build.gradle | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/java/app/build.gradle b/android/java/app/build.gradle index cb3750b7d..668ff7993 100644 --- a/android/java/app/build.gradle +++ b/android/java/app/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 19 + compileSdkVersion 21 buildToolsVersion "21.1.0" defaultConfig { applicationId "com.mapbox.mapboxgl.app" minSdkVersion 19 - targetSdkVersion 20 + targetSdkVersion 21 } buildTypes { diff --git a/android/java/lib/build.gradle b/android/java/lib/build.gradle index d6e4f141c..900bedd59 100644 --- a/android/java/lib/build.gradle +++ b/android/java/lib/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 19 + compileSdkVersion 21 buildToolsVersion "21.1.0" defaultConfig { applicationId "com.mapbox.mapboxgl.lib" minSdkVersion 19 - targetSdkVersion 20 + targetSdkVersion 21 ndk { moduleName "NativeMapView" From 53eeb0ca6eb4ae77b8678203b1c71023c13fa183 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 17:56:51 +1100 Subject: [PATCH 050/415] Reset lint levels --- android/java/app/lint.xml | 3 +-- android/java/lib/lint.xml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/android/java/app/lint.xml b/android/java/app/lint.xml index 7328a1fd1..309f4e478 100644 --- a/android/java/app/lint.xml +++ b/android/java/app/lint.xml @@ -1,4 +1,3 @@ - - \ No newline at end of file + diff --git a/android/java/lib/lint.xml b/android/java/lib/lint.xml index 84598b8d4..309f4e478 100644 --- a/android/java/lib/lint.xml +++ b/android/java/lib/lint.xml @@ -1,4 +1,3 @@ - - \ No newline at end of file + From c0c6d9d78e8a1ad2d927bfa2121db3d7a18272b4 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 18:56:33 +1100 Subject: [PATCH 051/415] Link all libs statically --- android/mapboxgl-app.gyp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/android/mapboxgl-app.gyp b/android/mapboxgl-app.gyp index 98b6b4ba4..3d421942b 100644 --- a/android/mapboxgl-app.gyp +++ b/android/mapboxgl-app.gyp @@ -14,9 +14,17 @@ 'cflags_cc': [ '-I<(boost_root)/include', ], + 'libraries': [ + '<@(openssl_static_libs)', + '<@(curl_static_libs)', + '<@(png_static_libs)', + '<@(sqlite3_static_libs)', + '<@(uv_static_libs)', + '<@(nu_static_libs)', + ], 'variables': { 'ldflags': [ - '-llog -landroid -lEGL -lGLESv2 ' + '-llog -landroid -lEGL -lGLESv2 -lstdc++ -latomic ' '<@(png_ldflags)', '<@(sqlite3_ldflags)', '<@(openssl_ldflags)', From 89fad21fb9e5d507a9a72767b2242fa28d67d547 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 18:56:50 +1100 Subject: [PATCH 052/415] Add GCC to travis to see if other messages --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6d6a70b55..46ca8c341 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,9 @@ matrix: - os: linux env: BUILDTYPE=Release JOBS=16 MASON_PLATFORM=android compiler: clang + - os: linux + env: BUILDTYPE=Release JOBS=16 MASON_PLATFORM=android + compiler: gcc - os: linux env: BUILDTYPE=Debug JOBS=16 compiler: clang From 6415518094ba3dc851f382aa5d7d7b4c023093a1 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 19:02:26 +1100 Subject: [PATCH 053/415] Fix spaces --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8afb54c1a..064fa3f76 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ android: export CXX="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ export CC="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CC`" && \ export LD="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env LD`" && \ - export LINK="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ + export LINK="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ export AR="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env AR`" && \ export RANLIB="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env RANLIB`" && \ export LDFLAGS="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env LDFLAGS` ${LDFLAGS}" && \ From 398e5f933a3489910d8ca995355179d152a486fe Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 20:39:00 +1100 Subject: [PATCH 054/415] Fix tabs --- Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 064fa3f76..24bd1c087 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ android: export CXX="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ export CC="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CC`" && \ export LD="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env LD`" && \ - export LINK="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ + export LINK="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ export AR="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env AR`" && \ export RANLIB="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env RANLIB`" && \ export LDFLAGS="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env LDFLAGS` ${LDFLAGS}" && \ @@ -83,11 +83,11 @@ android: export PATH="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env PATH`:${PATH}" && \ MASON_PLATFORM=android ./configure config-android.gypi && \ deps/run_gyp android/mapboxgl-app.gyp -Iconfig-android.gypi -Dplatform=android --depth=. --generator-output=./build/android -f make-android && \ - $(MAKE) -C build/android BUILDTYPE=$(BUILDTYPE) V=$(V) androidapp && \ - mkdir -p android/java/lib/src/main/jniLibs/armeabi-v7a && \ - cp build/android/out/$(BUILDTYPE)/lib.target/libmapbox-gl.so android/java/lib/src/main/jniLibs/armeabi-v7a/libmapbox-gl.so && \ - cd android/java && \ - ./gradlew build + $(MAKE) -C build/android BUILDTYPE=$(BUILDTYPE) V=$(V) androidapp && \ + mkdir -p android/java/lib/src/main/jniLibs/armeabi-v7a && \ + cp build/android/out/$(BUILDTYPE)/lib.target/libmapbox-gl.so android/java/lib/src/main/jniLibs/armeabi-v7a/libmapbox-gl.so && \ + cd android/java && \ + ./gradlew build ##### Test cases ############################################################### From bc375763f8d6e70470f5006f4d3c604df6fbc4e7 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 20:47:09 +1100 Subject: [PATCH 055/415] Fix bug with callback into Java --- android/cpp/NativeMapView.cpp | 38 ++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 7ced2fa68..1cd416617 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -78,11 +78,12 @@ NativeMapView::~NativeMapView() { ASSERT(vm != nullptr); ASSERT(obj != nullptr); + jint ret; JNIEnv* env = nullptr; - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_OK) { + if ((ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6)) == JNI_OK) { env->DeleteGlobalRef(obj); } else { - ERROR("GetEnv() failed"); + ERROR("GetEnv() failed with %i", ret); } obj = nullptr; vm = nullptr; @@ -452,18 +453,41 @@ void NativeMapView::notifyMapChange() { ASSERT(vm != nullptr); ASSERT(obj != nullptr); + JavaVMAttachArgs args = { + JNI_VERSION_1_2, + "NativeMapView::notify_map_change()", + NULL + }; + + jint ret; JNIEnv* env = nullptr; - // FIXME this returns failure - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { - ERROR("GetEnv() failed"); - return; + bool detach = false; + if ((ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6)) != JNI_OK) { + if (ret != JNI_EDETACHED) { + ERROR("GetEnv() failed with %i", ret); + return; + } else { + if ((ret = vm->AttachCurrentThread(&env, &args)) != JNI_OK) { + ERROR("AttachCurrentThread() failed with %i", ret); + return; + } + detach = true; + } } + env->CallVoidMethod(obj, on_map_changed_id); if (env->ExceptionCheck()) { env->ExceptionDescribe(); - return; } + + if (detach) { + if ((ret = vm->DetachCurrentThread()) != JNI_OK) { + ERROR("DetachCurrentThread() failed with %i", ret); + return; + } + } + env = nullptr; } void MBGLView::make_active() { From 4caf8b5b5022771d0edf59b1e2fc4d8060081640 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 20:52:50 +1100 Subject: [PATCH 056/415] Remove source functions --- android/cpp/JNI.cpp | 33 ------------------- .../java/com/mapbox/mapboxgl/lib/MapView.java | 3 -- .../mapbox/mapboxgl/lib/NativeMapView.java | 25 -------------- 3 files changed, 61 deletions(-) diff --git a/android/cpp/JNI.cpp b/android/cpp/JNI.cpp index 2dbfedcbe..c12828ae0 100644 --- a/android/cpp/JNI.cpp +++ b/android/cpp/JNI.cpp @@ -242,35 +242,6 @@ void JNICALL nativeCleanup(JNIEnv* env, jobject obj, jlong native_map_view_ptr) native_map_view->getMap()->cleanup(); } -// TODO remove these? -void JNICALL nativeAddDefaultSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeAddDefaultSource"); - ASSERT(native_map_view_ptr != 0); -// NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); -// native_map_view->getMap()->addDefaultSource(); -} - -void JNICALL nativeRemoveDefaultSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeRemoveDefaultSource"); - ASSERT(native_map_view_ptr != 0); -// NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); -// native_map_view->getMap()->removeDefaultSource(); -} - -void JNICALL nativeAddSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring name, jstring url) { - VERBOSE("nativeAddSource"); - ASSERT(native_map_view_ptr != 0); -// NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); -// native_map_view->getMap()->addSource(std_string_from_jstring(env, name), std_string_from_jstring(env, url)); -} - -void JNICALL nativeRemoveSource(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring name) { - VERBOSE("nativeRemoveSource"); - ASSERT(native_map_view_ptr != 0); -// NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); -// native_map_view->getMap()->removeSource(std_string_from_jstring(env, name)); -} - void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio) { VERBOSE("nativeResize"); ASSERT(native_map_view_ptr != 0); @@ -734,10 +705,6 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { { "nativeRerender", "(J)V", reinterpret_cast(&nativeRerender) }, { "nativeUpdate", "(J)V", reinterpret_cast(&nativeUpdate) }, { "nativeCleanup", "(J)V", reinterpret_cast(&nativeCleanup) }, - { "nativeAddDefaultSource", "(J)V", reinterpret_cast(&nativeAddDefaultSource) }, - { "nativeRemoveDefaultSource", "(J)V", reinterpret_cast(&nativeRemoveDefaultSource) }, - { "nativeAddSource", "(JLjava/lang/String;Ljava/lang/String;)V", reinterpret_cast(&nativeAddSource) }, - { "nativeRemoveSource", "(JLjava/lang/String;)V", reinterpret_cast(&nativeRemoveSource) }, { "nativeResize", "(JIIF)V", reinterpret_cast(static_cast(&nativeResize)) }, { "nativeResize", "(JIIFII)V", reinterpret_cast(static_cast(&nativeResize)) }, { "nativeSetAppliedClasses", "(JLjava/util/Set;)V", reinterpret_cast(&nativeSetAppliedClasses) }, diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 0f1961d24..87ab2e885 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -127,9 +127,6 @@ public class MapView extends SurfaceView { // Create the NativeMapView mNativeMapView = new NativeMapView(this, mDefaultStyleJSON); - mNativeMapView.addDefaultSource(); - // mNativeMapView.addSource("countries", - // "http://192.168.20.103/%d/%d/%d.pbf"); // Load the attributes TypedArray typedArray = context.obtainStyledAttributes(attrs, diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index 468d4713d..1d1fa5f4d 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -92,22 +92,6 @@ class NativeMapView { nativeCleanup(mNativeMapViewPtr); } - public void addDefaultSource() { - nativeAddDefaultSource(mNativeMapViewPtr); - } - - public void removeDefaultSource() { - nativeRemoveDefaultSource(mNativeMapViewPtr); - } - - public void addSource(String name, String url) { - nativeAddSource(mNativeMapViewPtr, name, url); - } - - public void removeSource(String name) { - nativeRemoveSource(mNativeMapViewPtr, name); - } - public void resize(int width, int height) { resize(width, height, 1.0f); } @@ -402,15 +386,6 @@ class NativeMapView { private native void nativeCleanup(long nativeMapViewPtr); - private native void nativeAddDefaultSource(long nativeMapViewPtr); - - private native void nativeRemoveDefaultSource(long nativeMapViewPtr); - - private native void nativeAddSource(long nativeMapViewPtr, String name, - String url); - - private native void nativeRemoveSource(long nativeMapViewPtr, String name); - private native void nativeResize(long nativeMapViewPtr, int width, int height, float ratio); From 38ec9f03c1af29e7f11ff72b7c93d862c198ea73 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 21:08:18 +1100 Subject: [PATCH 057/415] Rename angle to bearing --- android/cpp/JNI.cpp | 29 +++++++------------ .../java/com/mapbox/mapboxgl/lib/MapView.java | 26 +++++++---------- .../mapbox/mapboxgl/lib/NativeMapView.java | 22 +++++++------- 3 files changed, 32 insertions(+), 45 deletions(-) diff --git a/android/cpp/JNI.cpp b/android/cpp/JNI.cpp index c12828ae0..1b9613ab7 100644 --- a/android/cpp/JNI.cpp +++ b/android/cpp/JNI.cpp @@ -497,34 +497,25 @@ void JNICALL nativeRotateBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, native_map_view->getMap()->rotateBy(sx, sy, ex, ey, duration); } -void JNICALL nativeSetAngle(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble angle, jdouble duration) { - VERBOSE("nativeSetAngle"); +void JNICALL nativeSetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble degrees, jdouble duration) { + VERBOSE("nativeSetBearing"); ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - // TODO update java class - //native_map_view->getMap()->setAngle(angle, duration); - double degrees = -angle / M_PI * 180; native_map_view->getMap()->setBearing(degrees, duration); } -void JNICALL nativeSetAngle(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble angle, jdouble cx, jdouble cy) { - VERBOSE("nativeSetAngle"); +void JNICALL nativeSetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble degrees, jdouble cx, jdouble cy) { + VERBOSE("nativeSetBearing"); ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - // TODO update java class - //native_map_view->getMap()->setAngle(angle, cx, cy); - double degrees = -angle / M_PI * 180; native_map_view->getMap()->setBearing(degrees, cx, cy); } -jdouble JNICALL nativeGetAngle(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetAngle"); +jdouble JNICALL nativeGetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + VERBOSE("nativeGetBearing"); ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - // TODO update java class - //return native_map_view->getMap()->getAngle(); - double degrees = native_map_view->getMap()->getBearing(); - return -degrees * M_PI / 180; + return native_map_view->getMap()->getBearing(); } void JNICALL nativeResetNorth(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { @@ -732,9 +723,9 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { { "nativeGetMinZoom", "(J)D", reinterpret_cast(&nativeGetMinZoom) }, { "nativeGetMaxZoom", "(J)D", reinterpret_cast(&nativeGetMaxZoom) }, { "nativeRotateBy", "(JDDDDD)V", reinterpret_cast(&nativeRotateBy) }, - { "nativeSetAngle", "(JDD)V", reinterpret_cast(static_cast(&nativeSetAngle)) }, - { "nativeSetAngle", "(JDDD)V", reinterpret_cast(static_cast(&nativeSetAngle)) }, - { "nativeGetAngle", "(J)D", reinterpret_cast(&nativeGetAngle) }, + { "nativeSetBearing", "(JDD)V", reinterpret_cast(static_cast(&nativeSetBearing)) }, + { "nativeSetBearing", "(JDDD)V", reinterpret_cast(static_cast(&nativeSetBearing)) }, + { "nativeGetBearing", "(J)D", reinterpret_cast(&nativeGetBearing) }, { "nativeResetNorth", "(J)V", reinterpret_cast(&nativeResetNorth) }, { "nativeStartRotating", "(J)V", reinterpret_cast(&nativeStartRotating) }, { "nativeStopRotating", "(J)V", reinterpret_cast(&nativeStopRotating) }, diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 87ab2e885..ab15c91f4 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -213,9 +213,7 @@ public class MapView extends SurfaceView { } public double getDirection() { - double direction = mNativeMapView.getAngle(); - - direction *= 180 / Math.PI; + double direction = -mNativeMapView.getBearing(); while (direction > 360) direction -= 360; @@ -232,9 +230,7 @@ public class MapView extends SurfaceView { public void setDirection(double direction, boolean animated) { double duration = animated ? 0.3 : 0.0; - direction *= Math.PI / 180; - - mNativeMapView.setAngle(direction, duration); + mNativeMapView.setBearing(-direction, duration); } public void resetPosition() { @@ -362,7 +358,7 @@ public class MapView extends SurfaceView { if (savedInstanceState != null) { lonlat = (LonLat) savedInstanceState .getParcelable(STATE_CENTER_COORDINATE); - angle = savedInstanceState.getDouble(STATE_CENTER_DIRECTION); + bearing = savedInstanceState.getDouble(STATE_CENTER_DIRECTION); zoom = savedInstanceState.getDouble(STATE_ZOOM_LEVEL); setCenterCoordinate((LonLat) savedInstanceState .getParcelable(STATE_CENTER_COORDINATE)); @@ -418,7 +414,7 @@ public class MapView extends SurfaceView { public void onPause() { Log.v(TAG, "onPause"); lonlat = mNativeMapView.getLonLat(); - angle = mNativeMapView.getAngle(); + bearing = mNativeMapView.getBearing(); zoom = mNativeMapView.getZoom(); mNativeMapView.stop(); } @@ -429,14 +425,14 @@ public class MapView extends SurfaceView { // TODO need to fix this in Map C++ code // Seems map state gets reset when we start() private LonLat lonlat; - private double angle, zoom; + private double bearing, zoom; public void onResume() { Log.v(TAG, "onResume"); mNativeMapView.start(); if (lonlat != null) { mNativeMapView.setLonLat(lonlat); - mNativeMapView.setAngle(angle); + mNativeMapView.setBearing(bearing); mNativeMapView.setZoom(zoom); } } @@ -461,7 +457,7 @@ public class MapView extends SurfaceView { mNativeMapView.createSurface(holder.getSurface()); if (lonlat != null) { mNativeMapView.setLonLat(lonlat); - mNativeMapView.setAngle(angle); + mNativeMapView.setBearing(bearing); mNativeMapView.setZoom(zoom); } } @@ -834,10 +830,10 @@ public class MapView extends SurfaceView { mNativeMapView.cancelTransitions(); // Rotate the map - double angle = mNativeMapView.getAngle(); - angle -= detector.getRotationDegreesDelta() * Math.PI / 180.0; - Log.d("rotate", "rotate to " + angle); - mNativeMapView.setAngle(angle, detector.getFocusX(), + double bearing = mNativeMapView.getBearing(); + bearing += detector.getRotationDegreesDelta() * Math.PI / 180.0; + Log.d("rotate", "rotate to " + bearing); + mNativeMapView.setBearing(bearing, detector.getFocusX(), detector.getFocusY()); return true; diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index 1d1fa5f4d..bd72846d9 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -295,20 +295,20 @@ class NativeMapView { nativeRotateBy(mNativeMapViewPtr, sx, sy, ex, ey, duration); } - public void setAngle(double angle) { - setAngle(angle, 0.0); + public void setBearing(double degrees) { + setBearing(degrees, 0.0); } - public void setAngle(double angle, double duration) { - nativeSetAngle(mNativeMapViewPtr, angle, duration); + public void setBearing(double degrees, double duration) { + nativeSetBearing(mNativeMapViewPtr, degrees, duration); } - public void setAngle(double angle, double cx, double cy) { - nativeSetAngle(mNativeMapViewPtr, angle, cx, cy); + public void setBearing(double degrees, double cx, double cy) { + nativeSetBearing(mNativeMapViewPtr, degrees, cx, cy); } - public double getAngle() { - return nativeGetAngle(mNativeMapViewPtr); + public double getBearing() { + return nativeGetBearing(mNativeMapViewPtr); } public void resetNorth() { @@ -452,13 +452,13 @@ class NativeMapView { private native void nativeRotateBy(long nativeMapViewPtr, double sx, double sy, double ex, double ey, double duration); - private native void nativeSetAngle(long nativeMapViewPtr, double angle, + private native void nativeSetBearing(long nativeMapViewPtr, double degrees, double duration); - private native void nativeSetAngle(long nativeMapViewPtr, double angle, + private native void nativeSetBearing(long nativeMapViewPtr, double degrees, double cx, double cy); - private native double nativeGetAngle(long nativeMapViewPtr); + private native double nativeGetBearing(long nativeMapViewPtr); private native void nativeResetNorth(long nativeMapViewPtr); From 7b290b09c84f12a38bd7e4ea05a8228f6e3ba643 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 23:35:08 +1100 Subject: [PATCH 058/415] Add missing platform to travis --- scripts/travis_before_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 2dc48787a..1c57a261f 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -42,7 +42,7 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: installing SDK" #mapbox_time "installing SDK" \ sudo apt-get -y install lib32stdc++6 lib32z1 - echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19 + echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21 fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From 1ace7d6d18b7d7f1d068337c289dbb2c1a80f939 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 6 Nov 2014 23:53:01 +1100 Subject: [PATCH 059/415] Revert "Add GCC to travis to see if other messages" This reverts commit 89fad21fb9e5d507a9a72767b2242fa28d67d547. --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 46ca8c341..6d6a70b55 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,9 +7,6 @@ matrix: - os: linux env: BUILDTYPE=Release JOBS=16 MASON_PLATFORM=android compiler: clang - - os: linux - env: BUILDTYPE=Release JOBS=16 MASON_PLATFORM=android - compiler: gcc - os: linux env: BUILDTYPE=Debug JOBS=16 compiler: clang From aa39ffe74cebd061c32f6731fc9741473b4a2de8 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 7 Nov 2014 00:38:27 +1100 Subject: [PATCH 060/415] Fix bad count --- android/cpp/JNI.cpp | 3 ++- android/cpp/NativeMapView.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/android/cpp/JNI.cpp b/android/cpp/JNI.cpp index 1b9613ab7..31bbc963e 100644 --- a/android/cpp/JNI.cpp +++ b/android/cpp/JNI.cpp @@ -682,7 +682,8 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_ERR; } - std::array methods = {{ // Can remove the extra brace in C++14 + // NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the array correctly (too large) + std::array methods = {{ // Can remove the extra brace in C++14 { "nativeCreate", "(Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, { "nativeDestroy", "(J)V", reinterpret_cast(&nativeDestroy) }, { "nativeInitializeDisplay", "(J)V", reinterpret_cast(&nativeInitializeDisplay) }, diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 1cd416617..cb03204b3 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -543,7 +543,7 @@ void MBGLView::notify() { void MBGLView::notify_map_change(mbgl::MapChange change, mbgl::timestamp delay) { DEBUG("MBGLView::notify_map_change()"); nativeView->notifyMapChange(); - // TODO: use new variables + // TODO: use new variables - do i need to? (void)change; (void)delay; } From 5ad9e14926f70f8c3e40bbfb0a8ef6ab160cb990 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 7 Nov 2014 01:22:28 +1100 Subject: [PATCH 061/415] Enable debug rendering, work around make_inactive bug --- android/cpp/NativeMapView.cpp | 11 +++++++++-- .../main/java/com/mapbox/mapboxgl/lib/MapView.java | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index cb03204b3..facdbd48c 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -62,7 +62,7 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, // FIXME this asserts because it creates FileSource of different thread from run thread //map->setStyleJSON(default_style_json); - map->setStyleURL("https://raw.githubusercontent.com/mapbox/mapbox-gl-styles/mb-pages/styles/bright-v5.json"); + map->setStyleURL("https://raw.githubusercontent.com/mapbox/mapbox-gl-styles/mb-pages/styles/bright-v6.json"); } NativeMapView::~NativeMapView() { @@ -515,6 +515,7 @@ void MBGLView::make_active() { void MBGLView::make_inactive() { VERBOSE("MBGLView::make_inactive"); + // FIXME: this gets called before swap? bug? if (!eglMakeCurrent(nativeView->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", @@ -524,20 +525,26 @@ void MBGLView::make_inactive() { void MBGLView::swap() { VERBOSE("MBGLView::swap"); + // FIXME bug workaround + make_active(); + if (map->needsSwap() && (nativeView->display != EGL_NO_DISPLAY) && (nativeView->surface != EGL_NO_SURFACE)) { if (!eglSwapBuffers(nativeView->display, nativeView->surface)) { ERROR("eglSwapBuffers() returned error %d", eglGetError()); + ERROR("%u", reinterpret_cast(nativeView->surface)); // TODO remove } map->swapped(); } else { DEBUG("Not swapping as we are not ready"); } + // FIXME bug workaround + make_inactive(); } void MBGLView::notify() { DEBUG("MBGLView::notify()"); - // TODO + // noop } void MBGLView::notify_map_change(mbgl::MapChange change, mbgl::timestamp delay) { diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index ab15c91f4..51f627f63 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -150,7 +150,7 @@ public class MapView extends SurfaceView { setRotateEnabled(typedArray.getBoolean( R.styleable.MapView_rotateEnabled, true)); setDebugActive(typedArray.getBoolean( - R.styleable.MapView_debugActive, false)); + R.styleable.MapView_debugActive, true)); } finally { typedArray.recycle(); } From 56e26749d411b1f717a364538e0899ab590244a6 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 7 Nov 2014 01:39:44 +1100 Subject: [PATCH 062/415] Start working on android log hook --- gyp/mbgl-android.gypi | 2 +- platform/android/log_android.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 platform/android/log_android.cpp diff --git a/gyp/mbgl-android.gypi b/gyp/mbgl-android.gypi index 02c475b01..4601ead47 100644 --- a/gyp/mbgl-android.gypi +++ b/gyp/mbgl-android.gypi @@ -26,7 +26,7 @@ }, 'sources': [ '../platform/android/cache_database_data.cpp', - '../platform/default/log_stderr.cpp', + '../platform/android/log_android.cpp', '../platform/default/string_stdlib.cpp', '../platform/default/http_request_baton_curl.cpp', '../platform/default/image.cpp', diff --git a/platform/android/log_android.cpp b/platform/android/log_android.cpp new file mode 100644 index 000000000..0daf3f326 --- /dev/null +++ b/platform/android/log_android.cpp @@ -0,0 +1,32 @@ +#include + +#include +#include + +namespace mbgl { + +// TODO use android logging + +void StderrLogBackend::record(EventSeverity severity, Event event, const std::string &msg) { + std::cerr << "[" << severity << "] " << event << ": " << msg << std::endl; +} + +void StderrLogBackend::record(EventSeverity severity, Event event, const char* format, ...) { + std::cerr << "[" << severity << "] " << event << ": "; + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + std::cerr << std::endl; +} + +void StderrLogBackend::record(EventSeverity severity, Event event, int64_t code) { + std::cerr << "[" << severity << "] " << event << ": (" << code << ")" << std::endl; +} + +void StderrLogBackend::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) { + std::cerr << "[" << severity << "] " << event << ": (" << code << ") " << msg << std::endl; + +} + +} From 86f8f626f145700df775ec1966283fb89069a1d2 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 7 Nov 2014 01:40:05 +1100 Subject: [PATCH 063/415] Change clear to red to debug tile loading --- src/renderer/painter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/painter.cpp b/src/renderer/painter.cpp index 960168bd4..746b0d603 100644 --- a/src/renderer/painter.cpp +++ b/src/renderer/painter.cpp @@ -66,7 +66,7 @@ void Painter::setup() { glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // Set clear values - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClearColor(1.0f, 0.0f, 0.0f, 0.0f); glClearDepth(1.0f); glClearStencil(0x0); @@ -174,7 +174,7 @@ void Painter::clear() { glStencilMask(0xFF); depthMask(true); - glClearColor(0, 0, 0, 0); + glClearColor(1, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } From 4a35b831fc34a175ec0f202a4c53f13f065cff9d Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 7 Nov 2014 21:00:51 +1100 Subject: [PATCH 064/415] Better way to avoid warnings --- android/cpp/NativeMapView.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index facdbd48c..23badd2c6 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -547,12 +547,9 @@ void MBGLView::notify() { // noop } -void MBGLView::notify_map_change(mbgl::MapChange change, mbgl::timestamp delay) { +void MBGLView::notify_map_change(mbgl::MapChange /* change */, mbgl::timestamp /* delay */) { DEBUG("MBGLView::notify_map_change()"); nativeView->notifyMapChange(); - // TODO: use new variables - do i need to? - (void)change; - (void)delay; } } // namespace android From 7477d0c9dfcf4d7e354e05f79efe4b3bc63a2533 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 7 Nov 2014 23:46:28 +1100 Subject: [PATCH 065/415] Finish android log backend --- android/cpp/NativeMapView.cpp | 7 +-- include/mbgl/platform/android/log_android.hpp | 24 +++++++++ platform/android/log_android.cpp | 53 ++++++++++++++----- 3 files changed, 66 insertions(+), 18 deletions(-) create mode 100644 include/mbgl/platform/android/log_android.hpp diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 23badd2c6..a7bf2aafa 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -1,10 +1,9 @@ -//#include - #include #include #include +#include #include "log.h" @@ -50,9 +49,7 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, return; } - // TODO replace all printfs in map code with android logging - //freopen("/sdcard/stdout.txt", "w", stdout); // NOTE: can't use till NDK fix the stdout macro bug - //freopen("/sdcard/stderr.txt", "w", stderr); + mbgl::Log::Set(); view = new MBGLView(this); map = new mbgl::Map(*view); diff --git a/include/mbgl/platform/android/log_android.hpp b/include/mbgl/platform/android/log_android.hpp new file mode 100644 index 000000000..12ed3bd34 --- /dev/null +++ b/include/mbgl/platform/android/log_android.hpp @@ -0,0 +1,24 @@ +#ifndef MBGL_PLATFORM_ANDROID_LOG_ANDROID +#define MBGL_PLATFORM_ANDROID_LOG_ANDROID + +#include + +namespace mbgl { + +class AndroidLogBackend : public LogBackend { +private: + int severity_to_priority(EventSeverity severity); + +public: + inline ~AndroidLogBackend() = default; + + void record(EventSeverity severity, Event event, const std::string &msg); + void record(EventSeverity severity, Event event, const char* format, ...); + void record(EventSeverity severity, Event event, int64_t code); + void record(EventSeverity severity, Event event, int64_t code, const std::string &msg); +}; + + +} + +#endif diff --git a/platform/android/log_android.cpp b/platform/android/log_android.cpp index 0daf3f326..8a7e9df51 100644 --- a/platform/android/log_android.cpp +++ b/platform/android/log_android.cpp @@ -1,32 +1,59 @@ -#include +#include #include #include +#define __STDC_FORMAT_MACROS // NDK bug workaround: https://code.google.com/p/android/issues/detail?id=72349 +#include + +#include namespace mbgl { -// TODO use android logging +int AndroidLogBackend::severity_to_priority(EventSeverity severity) { + switch(severity) { + case EventSeverity::Debug: + return ANDROID_LOG_DEBUG; -void StderrLogBackend::record(EventSeverity severity, Event event, const std::string &msg) { - std::cerr << "[" << severity << "] " << event << ": " << msg << std::endl; + case EventSeverity::Info: + return ANDROID_LOG_INFO; + + case EventSeverity::Warning: + return ANDROID_LOG_WARN; + + case EventSeverity::Error: + return ANDROID_LOG_ERROR; + + default: + return ANDROID_LOG_VERBOSE; + } } -void StderrLogBackend::record(EventSeverity severity, Event event, const char* format, ...) { - std::cerr << "[" << severity << "] " << event << ": "; +void AndroidLogBackend::record(EventSeverity severity, Event event, const std::string &msg) { + __android_log_print(severity_to_priority(severity), EventClass(event).c_str(), "%s", msg.c_str()); +} + +void AndroidLogBackend::record(EventSeverity severity, Event event, const char* format, ...) { va_list args; va_start(args, format); - vfprintf(stderr, format, args); + + const int len = vsnprintf(nullptr, 0, format, args) + 1; + char* buf = new char[len]; + vsnprintf(buf, len, format, args); + va_end(args); - std::cerr << std::endl; + + __android_log_print(severity_to_priority(severity), EventClass(event).c_str(), "%s", buf); + + delete buf; + buf = nullptr; } -void StderrLogBackend::record(EventSeverity severity, Event event, int64_t code) { - std::cerr << "[" << severity << "] " << event << ": (" << code << ")" << std::endl; +void AndroidLogBackend::record(EventSeverity severity, Event event, int64_t code) { + __android_log_print(severity_to_priority(severity), EventClass(event).c_str(), "(%" PRId64 ")", code); } -void StderrLogBackend::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) { - std::cerr << "[" << severity << "] " << event << ": (" << code << ") " << msg << std::endl; - +void AndroidLogBackend::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) { + __android_log_print(severity_to_priority(severity), EventClass(event).c_str(), "(%" PRId64 ") %s", code, msg.c_str()); } } From 4a5f61e7a163cd9333c776460b68829bdde01e46 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 7 Nov 2014 23:47:17 +1100 Subject: [PATCH 066/415] Make sure to install mason before env --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 24bd1c087..0a1bc56e8 100644 --- a/Makefile +++ b/Makefile @@ -71,6 +71,7 @@ build/linux/mapboxgl-app.xcodeproj: linux/mapboxgl-app.gyp config.gypi .PHONY: android android: + ./scripts/local_mason.sh && \ export CXX="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ export CC="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CC`" && \ export LD="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env LD`" && \ From ee11f3609354d34c36c701b0f7c9bb622eebe5a7 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 7 Nov 2014 23:59:45 +1100 Subject: [PATCH 067/415] Non https style URL --- android/cpp/NativeMapView.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index a7bf2aafa..2b382e90e 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -57,9 +57,7 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, // FIXME need way to load this from Java map->setAccessToken("pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"); - // FIXME this asserts because it creates FileSource of different thread from run thread - //map->setStyleJSON(default_style_json); - map->setStyleURL("https://raw.githubusercontent.com/mapbox/mapbox-gl-styles/mb-pages/styles/bright-v6.json"); + map->setStyleURL("http://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"); } NativeMapView::~NativeMapView() { From 598bc92401caa6db676a58553e8008fefcc2e5d7 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 01:05:03 +1100 Subject: [PATCH 068/415] Make image.cpp use Log functions --- include/mbgl/platform/event.hpp | 2 ++ platform/default/image.cpp | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp index 2aa4aa0c9..3361e55ce 100644 --- a/include/mbgl/platform/event.hpp +++ b/include/mbgl/platform/event.hpp @@ -32,6 +32,7 @@ enum class Event : uint8_t { Database, HttpRequest, Sprite, + Image, }; MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { @@ -44,6 +45,7 @@ MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { { Event::Database, "Database" }, { Event::HttpRequest, "HttpRequest" }, { Event::Sprite, "Sprite" }, + { Event::Image, "Image" }, { Event(-1), "Unknown" }, }); diff --git a/platform/default/image.cpp b/platform/default/image.cpp index 68d178691..8368982d7 100644 --- a/platform/default/image.cpp +++ b/platform/default/image.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -15,14 +16,14 @@ std::string compress_png(int width, int height, void *rgba) { png_voidp error_ptr = 0; png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr, NULL, NULL); if (!png_ptr) { - fprintf(stderr, "Couldn't create png_ptr\n"); + Log::Error(Event::Image, "Couldn't create png_ptr"); return ""; } png_infop info_ptr = png_create_info_struct(png_ptr); if (!png_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)0); - fprintf(stderr, "Couldn't create info_ptr\n"); + Log::Error(Event::Image, "Couldn't create info_ptr"); return ""; } @@ -80,18 +81,19 @@ void readCallback(png_structp png, png_bytep data, png_size_t length) { } void errorHandler(png_structp, png_const_charp error_msg) { + Log::Error(Event::Image, "PNG: %s", error_msg); throw std::runtime_error(error_msg); } void warningHandler(png_structp, png_const_charp error_msg) { - fprintf(stderr, "PNG: %s\n", error_msg); + Log::Warning(Event::General, "PNG: %s", error_msg); } Image::Image(const std::string &data) { Buffer buffer(data); if (buffer.length < 8 || !png_check_sig((const png_bytep)buffer.data, 8)) { - fprintf(stderr, "image is not a valid PNG image\n"); + Log::Error(Event::Image, "image is not a valid PNG image"); return; } @@ -164,7 +166,7 @@ Image::Image(const std::string &data) { png_destroy_read_struct(&png, &info, nullptr); } catch (std::exception& e) { - fprintf(stderr, "loading PNG failed: %s\n", e.what()); + Log::Error(Event::Image, "loading PNG failed: %s", e.what()); png_destroy_read_struct(&png, &info, nullptr); if (img) { img.reset(); From a86fff1ab7684839c09827cfde95c44414dac0af Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 01:05:49 +1100 Subject: [PATCH 069/415] Rename old log functions --- android/cpp/JNI.cpp | 236 +++++++++++++++++----------------- android/cpp/NativeMapView.cpp | 184 +++++++++++++------------- android/cpp/log.h | 14 +- 3 files changed, 217 insertions(+), 217 deletions(-) diff --git a/android/cpp/JNI.cpp b/android/cpp/JNI.cpp index 31bbc963e..b8bc34c09 100644 --- a/android/cpp/JNI.cpp +++ b/android/cpp/JNI.cpp @@ -141,7 +141,7 @@ using namespace mbgl::android; // TODO: wrap other sorts of exceptions? eg coffee catch jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring default_style_json) { - VERBOSE("nativeCreate"); + LOG_VERBOSE("nativeCreate"); NativeMapView* native_map_view = new NativeMapView(env, obj, std_string_from_jstring(env, default_style_json)); if (native_map_view == nullptr) { throw_error(env, "Unable to create NativeMapView."); @@ -152,16 +152,16 @@ jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring default_style_json) } void JNICALL nativeDestroy(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeDestroy"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeDestroy"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); delete native_map_view; native_map_view = nullptr; } void JNICALL nativeInitializeDisplay(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeInitializeDisplay"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeInitializeDisplay"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); if (!native_map_view->initializeDisplay()) { throw_error(env, "Unable to initialize GL display."); @@ -169,15 +169,15 @@ void JNICALL nativeInitializeDisplay(JNIEnv* env, jobject obj, jlong native_map_ } void JNICALL nativeTerminateDisplay(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeTerminateDisplay"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeTerminateDisplay"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->terminateDisplay(); } void JNICALL nativeInitializeContext(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeInitializeContext"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeInitializeContext"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); if (!native_map_view->initializeContext()) { throw_error(env, "Unable to initialize GL context."); @@ -185,15 +185,15 @@ void JNICALL nativeInitializeContext(JNIEnv* env, jobject obj, jlong native_map_ } void JNICALL nativeTerminateContext(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeTerminateContext"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeTerminateContext"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->terminateContext(); } void JNICALL nativeCreateSurface(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject surface) { - VERBOSE("nativeCreateSurface"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeCreateSurface"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); if (!native_map_view->createSurface(ANativeWindow_fromSurface(env, surface))) { throw_error(env, "Unable to create GL surface."); @@ -201,127 +201,127 @@ void JNICALL nativeCreateSurface(JNIEnv* env, jobject obj, jlong native_map_view } void JNICALL nativeDestroySurface(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeDestroySurface"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeDestroySurface"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->destroySurface(); } void JNICALL nativeStart(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeStart"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeStart"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->start(); } void JNICALL nativeStop(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeStop"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeStop"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->stop(); } void JNICALL nativeRerender(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeRerender"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeRerender"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->rerender(); } void JNICALL nativeUpdate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeUpdate"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeUpdate"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->update(); } void JNICALL nativeCleanup(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeCleanup"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeCleanup"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->cleanup(); } void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio) { - VERBOSE("nativeResize"); - ASSERT(native_map_view_ptr != 0); - ASSERT(width >= 0); - ASSERT(height >= 0); - ASSERT(width <= UINT16_MAX); - ASSERT(height <= UINT16_MAX); + LOG_VERBOSE("nativeResize"); + LOG_ASSERT(native_map_view_ptr != 0); + LOG_ASSERT(width >= 0); + LOG_ASSERT(height >= 0); + LOG_ASSERT(width <= UINT16_MAX); + LOG_ASSERT(height <= UINT16_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->resize(width, height, ratio); } void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio, jint fb_width, jint fb_height) { - VERBOSE("nativeResize"); - ASSERT(native_map_view_ptr != 0); - ASSERT(width >= 0); - ASSERT(height >= 0); - ASSERT(width <= UINT16_MAX); - ASSERT(height <= UINT16_MAX); - ASSERT(fb_width >= 0); - ASSERT(fb_height >= 0); - ASSERT(fb_width <= UINT16_MAX); - ASSERT(fb_height <= UINT16_MAX); + LOG_VERBOSE("nativeResize"); + LOG_ASSERT(native_map_view_ptr != 0); + LOG_ASSERT(width >= 0); + LOG_ASSERT(height >= 0); + LOG_ASSERT(width <= UINT16_MAX); + LOG_ASSERT(height <= UINT16_MAX); + LOG_ASSERT(fb_width >= 0); + LOG_ASSERT(fb_height >= 0); + LOG_ASSERT(fb_width <= UINT16_MAX); + LOG_ASSERT(fb_height <= UINT16_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->resize(width, height, ratio, fb_width, fb_height); } void JNICALL nativeSetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject applied_classes) { - VERBOSE("nativeSetAppliedClasses"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeSetAppliedClasses"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->setAppliedClasses(std_vector_string_from_jobject(env, applied_classes)); } jobject JNICALL nativeGetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetAppliedClasses"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeGetAppliedClasses"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return std_vector_string_to_jobject(env, native_map_view->getMap()->getAppliedClasses()); } void JNICALL nativeSetDefaultTransitionDuration(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jlong duration_milliseconds) { - VERBOSE("nativeSetDefaultTransitionDuration"); - ASSERT(native_map_view_ptr != 0); - ASSERT(duration_milliseconds >= 0); - //ASSERT(duration_milliseconds <= UINT64_MAX); + LOG_VERBOSE("nativeSetDefaultTransitionDuration"); + LOG_ASSERT(native_map_view_ptr != 0); + LOG_ASSERT(duration_milliseconds >= 0); + //LOG_ASSERT(duration_milliseconds <= UINT64_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->setDefaultTransitionDuration(duration_milliseconds); } void JNICALL nativeSetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring new_style_json) { - VERBOSE("nativeSetStyleJSON"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeSetStyleJSON"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->setStyleJSON(std_string_from_jstring(env, new_style_json)); } jstring JNICALL nativeGetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetStyleJSON"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeGetStyleJSON"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return std_string_to_jstring(env, native_map_view->getMap()->getStyleJSON()); } void JNICALL nativeCancelTransitions(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeCancelTransitions"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeCancelTransitions"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->cancelTransitions(); } void JNICALL nativeMoveBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble dx, jdouble dy, jdouble duration) { - VERBOSE("nativeMoveBy"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeMoveBy"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->moveBy(dx, dy, duration); } void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat, jdouble duration) { - VERBOSE("nativeSetLonLat"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeSetLonLat"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); double lon = env->GetDoubleField(lon_lat, lon_lat_lon_id); @@ -337,13 +337,13 @@ void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr } // TODO remove when fixed - DEBUG("lon lat %f %f", lon, lat); + LOG_DEBUG("lon lat %f %f", lon, lat); native_map_view->getMap()->setLonLat(lon, lat, duration); } jobject JNICALL nativeGetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetLonLat"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeGetLonLat"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); double lon, lat; native_map_view->getMap()->getLonLat(lon, lat); @@ -358,64 +358,64 @@ jobject JNICALL nativeGetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ } void JNICALL nativeStartPanning(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeStartPanning"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeStartPanning"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->startPanning(); } void JNICALL nativeStopPanning(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeStopPanning"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeStopPanning"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->stopPanning(); } void JNICALL nativeResetPosition(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeResetPosition"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeResetPosition"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->resetPosition(); } void JNICALL nativeScaleBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble ds, jdouble cx, jdouble cy, jdouble duration) { - VERBOSE("nativeScaleBy"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeScaleBy"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->scaleBy(ds, cx, cy, duration); } void JNICALL nativeSetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble scale, jdouble cx, jdouble cy, jdouble duration) { - VERBOSE("nativeSetScale"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeSetScale"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->setScale(scale, cx, cy, duration); } jdouble JNICALL nativeGetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetScale"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeGetScale"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap()->getScale(); } void JNICALL nativeSetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble zoom, jdouble duration) { - VERBOSE("nativeSetZoom"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeSetZoom"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->setZoom(zoom, duration); } jdouble JNICALL nativeGetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetZoom"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeGetZoom"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap()->getZoom(); } void JNICALL nativeSetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat_zoom, jdouble duration) { - VERBOSE("nativeSetLonLatZoom"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeSetLonLatZoom"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); double lon = env->GetDoubleField(lon_lat_zoom, lon_lat_zoom_lon_id); @@ -440,8 +440,8 @@ void JNICALL nativeSetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view } jobject JNICALL nativeGetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetLonLatZoom"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeGetLonLatZoom"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); double lon, lat, zoom; native_map_view->getMap()->getLonLatZoom(lon, lat, zoom); @@ -456,113 +456,113 @@ jobject JNICALL nativeGetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_v } void JNICALL nativeResetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeResetZoom"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeResetZoom"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->resetZoom(); } void JNICALL nativeStartScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeStartScaling"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeStartScaling"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->startScaling(); } void JNICALL nativeStopScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeStopScaling"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeStopScaling"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->stopScaling(); } jdouble JNICALL nativeGetMinZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetMinZoom"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeGetMinZoom"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap()->getMinZoom(); } jdouble JNICALL nativeGetMaxZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetMaxZoom"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeGetMaxZoom"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap()->getMaxZoom(); } void JNICALL nativeRotateBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble sx, jdouble sy, jdouble ex, jdouble ey, jdouble duration) { - VERBOSE("nativeRotateBy"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeRotateBy"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->rotateBy(sx, sy, ex, ey, duration); } void JNICALL nativeSetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble degrees, jdouble duration) { - VERBOSE("nativeSetBearing"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeSetBearing"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->setBearing(degrees, duration); } void JNICALL nativeSetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble degrees, jdouble cx, jdouble cy) { - VERBOSE("nativeSetBearing"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeSetBearing"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->setBearing(degrees, cx, cy); } jdouble JNICALL nativeGetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetBearing"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeGetBearing"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap()->getBearing(); } void JNICALL nativeResetNorth(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeResetNorth"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeResetNorth"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->resetNorth(); } void JNICALL nativeStartRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeStartRotating"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeStartRotating"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->startRotating(); } void JNICALL nativeStopRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeStopRotating"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeStopRotating"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->stopRotating(); } jboolean JNICALL nativeCanRotate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeCanRotate"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeCanRotate"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap()->canRotate(); } void JNICALL nativeSetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jboolean debug) { - VERBOSE("nativeSetDebug"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeSetDebug"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->setDebug(debug); } void JNICALL nativeToggleDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeToggleDebug"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeToggleDebug"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap()->toggleDebug(); } jboolean JNICALL nativeGetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - VERBOSE("nativeGetDebug"); - ASSERT(native_map_view_ptr != 0); + LOG_VERBOSE("nativeGetDebug"); + LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap()->getDebug(); } @@ -572,11 +572,11 @@ jboolean JNICALL nativeGetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ extern "C" { extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { - VERBOSE("JNI_OnLoad"); + LOG_VERBOSE("JNI_OnLoad"); JNIEnv* env = nullptr; if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { - ERROR("GetEnv() failed"); + LOG_ERROR("GetEnv() failed"); return JNI_ERR; } @@ -775,11 +775,11 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { } extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { - VERBOSE("JNI_OnUnload"); + LOG_VERBOSE("JNI_OnUnload"); JNIEnv* env = nullptr; if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { - ERROR("GetEnv() failed"); + LOG_ERROR("GetEnv() failed"); return; } diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 2b382e90e..4dea3d6f0 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -15,28 +15,28 @@ namespace android { void log_egl_string(EGLDisplay display, EGLint name, const char* label) { const char* str = eglQueryString(display, name); if (str == nullptr) { - ERROR("eglQueryString(%d) returned error %d", name, eglGetError()); + LOG_ERROR("eglQueryString(%d) returned error %d", name, eglGetError()); } else { - INFO("EGL %s: %s", label, str); + LOG_INFO("EGL %s: %s", label, str); } } void log_gl_string(GLenum name, const char* label) { const GLubyte* str = glGetString(name); if (str == nullptr) { - ERROR("glGetString(%d) returned error %d", name, glGetError()); + LOG_ERROR("glGetString(%d) returned error %d", name, glGetError()); } else { - INFO("GL %s: %s", label, str); + LOG_INFO("GL %s: %s", label, str); } } NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, std::string default_style_json_) : default_style_json(default_style_json_) { - VERBOSE("NativeMapView::NativeMapView"); + LOG_VERBOSE("NativeMapView::NativeMapView"); - ASSERT(env != nullptr); - ASSERT(obj_ != nullptr); + LOG_ASSERT(env != nullptr); + LOG_ASSERT(obj_ != nullptr); if (env->GetJavaVM(&vm) < 0) { env->ExceptionDescribe(); @@ -61,7 +61,7 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, } NativeMapView::~NativeMapView() { - VERBOSE("NativeMapView::~NativeMapView"); + LOG_VERBOSE("NativeMapView::~NativeMapView"); terminateContext(); delete map; @@ -70,42 +70,42 @@ NativeMapView::~NativeMapView() { delete view; view = nullptr; - ASSERT(vm != nullptr); - ASSERT(obj != nullptr); + LOG_ASSERT(vm != nullptr); + LOG_ASSERT(obj != nullptr); jint ret; JNIEnv* env = nullptr; if ((ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6)) == JNI_OK) { env->DeleteGlobalRef(obj); } else { - ERROR("GetEnv() failed with %i", ret); + LOG_ERROR("GetEnv() failed with %i", ret); } obj = nullptr; vm = nullptr; } bool NativeMapView::initializeDisplay() { - VERBOSE("NativeMapView::initializeDisplay"); + LOG_VERBOSE("NativeMapView::initializeDisplay"); - ASSERT(display == EGL_NO_DISPLAY); - ASSERT(config == nullptr); - ASSERT(format < 0); + LOG_ASSERT(display == EGL_NO_DISPLAY); + LOG_ASSERT(config == nullptr); + LOG_ASSERT(format < 0); display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (display == EGL_NO_DISPLAY) { - ERROR("eglGetDisplay() returned error %d", eglGetError()); + LOG_ERROR("eglGetDisplay() returned error %d", eglGetError()); terminateDisplay(); return false; } EGLint major, minor; if (!eglInitialize(display, &major, &minor)) { - ERROR("eglInitialize() returned error %d", eglGetError()); + LOG_ERROR("eglInitialize() returned error %d", eglGetError()); terminateDisplay(); return false; } if ((major <= 1) && (minor < 3)) { - ERROR("EGL version is too low, need 1.3, got %d.%d", major, minor); + LOG_ERROR("EGL version is too low, need 1.3, got %d.%d", major, minor); terminateDisplay(); return false; } @@ -133,12 +133,12 @@ bool NativeMapView::initializeDisplay() { }; EGLint num_configs; if (!eglChooseConfig(display, config_attribs, nullptr, 0, &num_configs)) { - ERROR("eglChooseConfig(NULL) returned error %d", eglGetError()); + LOG_ERROR("eglChooseConfig(NULL) returned error %d", eglGetError()); terminateDisplay(); return false; } if (num_configs < 1) { - ERROR("eglChooseConfig() returned no configs"); + LOG_ERROR("eglChooseConfig() returned no configs"); terminateDisplay(); return false; } @@ -148,40 +148,40 @@ bool NativeMapView::initializeDisplay() { const std::unique_ptr configs(new EGLConfig[num_configs]); if (!eglChooseConfig(display, config_attribs, configs.get(), num_configs, &num_configs)) { - ERROR("eglChooseConfig() returned error %d", eglGetError()); + LOG_ERROR("eglChooseConfig() returned error %d", eglGetError()); terminateDisplay(); return false; } config = chooseConfig(configs.get(), num_configs, use565); if (config == nullptr) { - ERROR("No config chosen"); + LOG_ERROR("No config chosen"); terminateDisplay(); return false; } if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) { - ERROR("eglGetConfigAttrib() returned error %d", eglGetError()); + LOG_ERROR("eglGetConfigAttrib() returned error %d", eglGetError()); terminateDisplay(); return false; } - DEBUG("Chosen window format is %d", format); + LOG_DEBUG("Chosen window format is %d", format); return true; } void NativeMapView::terminateDisplay() { - VERBOSE("NativeMapView::terminateDisplay"); + LOG_VERBOSE("NativeMapView::terminateDisplay"); if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); } if (!eglTerminate(display)) { - ERROR("eglTerminate() returned error %d", eglGetError()); + LOG_ERROR("eglTerminate() returned error %d", eglGetError()); } } @@ -191,11 +191,11 @@ void NativeMapView::terminateDisplay() { } bool NativeMapView::initializeContext() { - VERBOSE("NativeMapView::initializeContext"); + LOG_VERBOSE("NativeMapView::initializeContext"); - ASSERT(display != EGL_NO_DISPLAY); - ASSERT(context == EGL_NO_CONTEXT); - ASSERT(config != nullptr); + LOG_ASSERT(display != EGL_NO_DISPLAY); + LOG_ASSERT(context == EGL_NO_CONTEXT); + LOG_ASSERT(config != nullptr); const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, @@ -204,18 +204,18 @@ bool NativeMapView::initializeContext() { context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attribs); if (context == EGL_NO_CONTEXT) { - ERROR("eglCreateContext() returned error %d", eglGetError()); + LOG_ERROR("eglCreateContext() returned error %d", eglGetError()); terminateContext(); return false; } - INFO("Context initialized"); + LOG_INFO("Context initialized"); return true; } void NativeMapView::terminateContext() { - VERBOSE("NativeMapView::terminateContext"); + LOG_VERBOSE("NativeMapView::terminateContext"); map->terminate(); @@ -228,13 +228,13 @@ void NativeMapView::terminateContext() { if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); } if (context != EGL_NO_CONTEXT) { if (!eglDestroyContext(display, context)) { - ERROR("eglDestroyContext() returned error %d", eglGetError()); + LOG_ERROR("eglDestroyContext() returned error %d", eglGetError()); } } } @@ -243,16 +243,16 @@ void NativeMapView::terminateContext() { } bool NativeMapView::createSurface(ANativeWindow* window_) { - VERBOSE("NativeMapView::createSurface"); + LOG_VERBOSE("NativeMapView::createSurface"); - ASSERT(window == nullptr); - ASSERT(window_ != nullptr); + LOG_ASSERT(window == nullptr); + LOG_ASSERT(window_ != nullptr); window = window_; - ASSERT(display != EGL_NO_DISPLAY); - ASSERT(surface == EGL_NO_SURFACE); - ASSERT(config != nullptr); - ASSERT(format >= 0); + LOG_ASSERT(display != EGL_NO_DISPLAY); + LOG_ASSERT(surface == EGL_NO_SURFACE); + LOG_ASSERT(config != nullptr); + LOG_ASSERT(format >= 0); ANativeWindow_setBuffersGeometry(window, 0, 0, format); @@ -261,7 +261,7 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { }; surface = eglCreateWindowSurface(display, config, window, surface_attribs); if (surface == EGL_NO_SURFACE) { - ERROR("eglCreateWindowSurface() returned error %d", eglGetError()); + LOG_ERROR("eglCreateWindowSurface() returned error %d", eglGetError()); destroySurface(); return false; } @@ -272,11 +272,11 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { } void NativeMapView::destroySurface() { - VERBOSE("NativeMapView::destroySurface"); + LOG_VERBOSE("NativeMapView::destroySurface"); if (surface != EGL_NO_SURFACE) { if (!eglDestroySurface(display, surface)) { - ERROR("eglDestroySurface() returned error %d", eglGetError()); + LOG_ERROR("eglDestroySurface() returned error %d", eglGetError()); } } @@ -290,88 +290,88 @@ void NativeMapView::destroySurface() { EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint num_configs, bool use565) const { - INFO("Found %d configs", num_configs); + LOG_INFO("Found %d configs", num_configs); int chosen_config = -1; for (int i = 0; i < num_configs; i++) { - INFO("Config %d:", i); + LOG_INFO("Config %d:", i); EGLint bits, red, green, blue, alpha, alpha_mask, depth, stencil, sample_buffers, samples; if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) { - ERROR("eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d", + LOG_ERROR("eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) { - ERROR("eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", + LOG_ERROR("eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) { - ERROR("eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", + LOG_ERROR("eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) { - ERROR("eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", + LOG_ERROR("eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) { - ERROR("eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", + LOG_ERROR("eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alpha_mask)) { - ERROR("eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d", + LOG_ERROR("eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) { - ERROR("eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", + LOG_ERROR("eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) { - ERROR("eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d", + LOG_ERROR("eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sample_buffers)) { - ERROR("eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d", + LOG_ERROR("eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) { - ERROR("eglGetConfigAttrib(EGL_SAMPLES) returned error %d", + LOG_ERROR("eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError()); return nullptr; } - INFO("Color: %d", bits); - INFO("Red: %d", red); - INFO("Green: %d", green); - INFO("Blue: %d", blue); - INFO("Alpha: %d", alpha); - INFO("Alpha mask: %d", alpha_mask); - INFO("Depth: %d", depth); - INFO("Stencil: %d", stencil); - INFO("Sample buffers: %d", sample_buffers); - INFO("Samples: %d", samples); + LOG_INFO("Color: %d", bits); + LOG_INFO("Red: %d", red); + LOG_INFO("Green: %d", green); + LOG_INFO("Blue: %d", blue); + LOG_INFO("Alpha: %d", alpha); + LOG_INFO("Alpha mask: %d", alpha_mask); + LOG_INFO("Depth: %d", depth); + LOG_INFO("Stencil: %d", stencil); + LOG_INFO("Sample buffers: %d", sample_buffers); + LOG_INFO("Samples: %d", samples); bool config_ok = true; config_ok &= bits == (use565 ? 16 : 32); @@ -390,7 +390,7 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], } } - DEBUG("Chosen config is %d", chosen_config); + LOG_DEBUG("Chosen config is %d", chosen_config); if (chosen_config >= 0) { return configs[chosen_config]; } else { @@ -399,7 +399,7 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], } void NativeMapView::start() { - VERBOSE("NativeMapView::start"); + LOG_VERBOSE("NativeMapView::start"); if (display == EGL_NO_DISPLAY) { initializeDisplay(); @@ -413,7 +413,7 @@ void NativeMapView::start() { && (context != EGL_NO_CONTEXT)) { if (!eglMakeCurrent(display, surface, surface, context)) { - ERROR("eglMakeCurrent() returned error %d", eglGetError()); + LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); } log_gl_string(GL_VENDOR, "Vendor"); @@ -424,18 +424,18 @@ void NativeMapView::start() { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); } map->start(); } else { - DEBUG("Not starting because we are not ready"); + LOG_DEBUG("Not starting because we are not ready"); } } void NativeMapView::stop() { - VERBOSE("NativeMapView::stop"); + LOG_VERBOSE("NativeMapView::stop"); if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) { map->stop(); @@ -443,10 +443,10 @@ void NativeMapView::stop() { } void NativeMapView::notifyMapChange() { - DEBUG("NativeMapView::notify_map_change()"); + LOG_DEBUG("NativeMapView::notify_map_change()"); - ASSERT(vm != nullptr); - ASSERT(obj != nullptr); + LOG_ASSERT(vm != nullptr); + LOG_ASSERT(obj != nullptr); JavaVMAttachArgs args = { JNI_VERSION_1_2, @@ -459,11 +459,11 @@ void NativeMapView::notifyMapChange() { bool detach = false; if ((ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6)) != JNI_OK) { if (ret != JNI_EDETACHED) { - ERROR("GetEnv() failed with %i", ret); + LOG_ERROR("GetEnv() failed with %i", ret); return; } else { if ((ret = vm->AttachCurrentThread(&env, &args)) != JNI_OK) { - ERROR("AttachCurrentThread() failed with %i", ret); + LOG_ERROR("AttachCurrentThread() failed with %i", ret); return; } detach = true; @@ -478,7 +478,7 @@ void NativeMapView::notifyMapChange() { if (detach) { if ((ret = vm->DetachCurrentThread()) != JNI_OK) { - ERROR("DetachCurrentThread() failed with %i", ret); + LOG_ERROR("DetachCurrentThread() failed with %i", ret); return; } } @@ -486,64 +486,64 @@ void NativeMapView::notifyMapChange() { } void MBGLView::make_active() { - VERBOSE("MBGLView::make_active"); + LOG_VERBOSE("MBGLView::make_active"); if ((nativeView->display != EGL_NO_DISPLAY) && (nativeView->surface != EGL_NO_SURFACE) && (nativeView->context != EGL_NO_CONTEXT)) { if (!eglMakeCurrent(nativeView->display, nativeView->surface, nativeView->surface, nativeView->context)) { - ERROR("eglMakeCurrent() returned error %d", eglGetError()); + LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); } // TODO still can't fix the black screen problem :-( EGLint width, height; if (!(eglQuerySurface(nativeView->display, nativeView->surface, EGL_WIDTH, &width) & eglQuerySurface(nativeView->display, nativeView->surface, EGL_HEIGHT, &height))) { - ERROR("eglQuerySurface() returned error %d", eglGetError()); + LOG_ERROR("eglQuerySurface() returned error %d", eglGetError()); } map->resize(width, height); } else { - DEBUG("Not activating as we are not ready"); + LOG_DEBUG("Not activating as we are not ready"); } } void MBGLView::make_inactive() { - VERBOSE("MBGLView::make_inactive"); + LOG_VERBOSE("MBGLView::make_inactive"); // FIXME: this gets called before swap? bug? if (!eglMakeCurrent(nativeView->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); } } void MBGLView::swap() { - VERBOSE("MBGLView::swap"); + LOG_VERBOSE("MBGLView::swap"); // FIXME bug workaround make_active(); if (map->needsSwap() && (nativeView->display != EGL_NO_DISPLAY) && (nativeView->surface != EGL_NO_SURFACE)) { if (!eglSwapBuffers(nativeView->display, nativeView->surface)) { - ERROR("eglSwapBuffers() returned error %d", eglGetError()); - ERROR("%u", reinterpret_cast(nativeView->surface)); // TODO remove + LOG_ERROR("eglSwapBuffers() returned error %d", eglGetError()); + LOG_ERROR("%u", reinterpret_cast(nativeView->surface)); // TODO remove } map->swapped(); } else { - DEBUG("Not swapping as we are not ready"); + LOG_DEBUG("Not swapping as we are not ready"); } // FIXME bug workaround make_inactive(); } void MBGLView::notify() { - DEBUG("MBGLView::notify()"); + LOG_DEBUG("MBGLView::notify()"); // noop } void MBGLView::notify_map_change(mbgl::MapChange /* change */, mbgl::timestamp /* delay */) { - DEBUG("MBGLView::notify_map_change()"); + LOG_DEBUG("MBGLView::notify_map_change()"); nativeView->notifyMapChange(); } diff --git a/android/cpp/log.h b/android/cpp/log.h index 461528a4c..aa1950843 100644 --- a/android/cpp/log.h +++ b/android/cpp/log.h @@ -7,12 +7,12 @@ #define S_(x) S(x) #define S__LINE__ S_(__LINE__) -#define VERBOSE(fmt, args...) __android_log_print(ANDROID_LOG_VERBOSE, __FILE__ ":" S__LINE__, fmt, ##args) -#define DEBUG(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__ ":" S__LINE__, fmt, ##args) -#define INFO(fmt, args...) __android_log_print(ANDROID_LOG_INFO, __FILE__ ":" S__LINE__, fmt, ##args) -#define WARN(fmt, args...) __android_log_print(ANDROID_LOG_WARN, __FILE__ ":" S__LINE__, fmt, ##args) -#define ERROR(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, __FILE__ ":" S__LINE__, fmt, ##args) -#define ASSERT(cond, ...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, NULL)) -#define ASSERTMSG(cond, fmt, args...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, fmt, ##args)) +#define LOG_VERBOSE(fmt, args...) __android_log_print(ANDROID_LOG_VERBOSE, __FILE__ ":" S__LINE__, fmt, ##args) +#define LOG_DEBUG(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__ ":" S__LINE__, fmt, ##args) +#define LOG_INFO(fmt, args...) __android_log_print(ANDROID_LOG_INFO, __FILE__ ":" S__LINE__, fmt, ##args) +#define LOG_WARN(fmt, args...) __android_log_print(ANDROID_LOG_WARN, __FILE__ ":" S__LINE__, fmt, ##args) +#define LOG_ERROR(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, __FILE__ ":" S__LINE__, fmt, ##args) +#define LOG_ASSERT(cond, ...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, NULL)) +#define LOG_ASSERTMSG(cond, fmt, args...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, fmt, ##args)) #endif // LOG_H From f01428b697e58d2f65e70ae687d09ce3182fefcc Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 02:14:53 +1100 Subject: [PATCH 070/415] Work around gamma crash --- platform/default/image.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/default/image.cpp b/platform/default/image.cpp index 8368982d7..933d98894 100644 --- a/platform/default/image.cpp +++ b/platform/default/image.cpp @@ -134,11 +134,12 @@ Image::Image(const std::string &data) { png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER); } - double gamma; + // FIXME work around gamma crash + /*double gamma; if (png_get_gAMA(png, info, &gamma)) png_set_gamma(png, 2.2, gamma); - png_set_alpha_mode(png, PNG_ALPHA_PREMULTIPLIED, 2.2); + png_set_alpha_mode(png, PNG_ALPHA_PREMULTIPLIED, 2.2);*/ png_read_update_info(png, info); From 585805a754650d1d855d5341735e7fe112dddcac Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 02:15:19 +1100 Subject: [PATCH 071/415] Add logging of http requests --- src/storage/http_request.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/storage/http_request.cpp b/src/storage/http_request.cpp index 46b1d5e19..e73a3b8f6 100644 --- a/src/storage/http_request.cpp +++ b/src/storage/http_request.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -22,6 +23,8 @@ struct CacheRequestBaton { HTTPRequest::HTTPRequest(ResourceType type_, const std::string &path_, uv_loop_t *loop_, util::ptr store_) : BaseRequest(path_), thread_id(uv_thread_self()), loop(loop_), store(store_), type(type_) { + Log::Error(Event::HttpRequest, "HTTPRequest %s", path.c_str()); + if (store) { startCacheRequest(); } else { From 6c5331ee73616dc2dcfd95dcd4c6ec810a68cc0e Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 02:29:07 +1100 Subject: [PATCH 072/415] Add more detail to TileJSON error --- src/map/source.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map/source.cpp b/src/map/source.cpp index 9c0f7caf2..bcb8ee343 100644 --- a/src/map/source.cpp +++ b/src/map/source.cpp @@ -39,9 +39,9 @@ void Source::load(Map& map) { std::string url = util::mapbox::normalizeSourceURL(info->url, map.getAccessToken()); util::ptr source = shared_from_this(); - map.getFileSource()->request(ResourceType::JSON, url)->onload([source, &map](const Response &res) { + map.getFileSource()->request(ResourceType::JSON, url)->onload([url, source, &map](const Response &res) { if (res.code != 200) { - Log::Warning(Event::General, "failed to load source TileJSON"); + Log::Warning(Event::General, "failed to load source TileJSON %s (%li) %s", url.c_str(), res.code, res.message.c_str()); return; } From 01ead83b39b38f042c9088596610e5f1ee420f58 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 08:07:47 +1100 Subject: [PATCH 073/415] Android mapbogl now loads maps! (had to change JSON loading to http - temp fix) --- src/util/mapbox.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/mapbox.cpp b/src/util/mapbox.cpp index 277b647f3..1245be405 100644 --- a/src/util/mapbox.cpp +++ b/src/util/mapbox.cpp @@ -12,7 +12,8 @@ std::string normalizeURL(const std::string& url, const std::string& accessToken) if (accessToken.empty()) throw std::runtime_error("You must provide a Mapbox API access token for Mapbox tile sources"); - return std::string("https://api.tiles.mapbox.com/v4/") + // FIXME for test only change back to https after + return std::string("http://api.tiles.mapbox.com/v4/") + url.substr(mapbox.length()) + "?access_token=" + accessToken; From 0ac4247f72b37462813f9ef1c4271108614a3189 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 08:44:39 +1100 Subject: [PATCH 074/415] Temp disable SSL cert validation --- android/cpp/NativeMapView.cpp | 3 +-- android/mapboxgl-app.gyp | 5 +++++ platform/default/http_request_baton_curl.cpp | 4 +++- src/storage/http_request.cpp | 2 +- src/util/mapbox.cpp | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 4dea3d6f0..1ca474af6 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -56,8 +56,7 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, // FIXME need way to load this from Java map->setAccessToken("pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"); - - map->setStyleURL("http://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"); + map->setStyleURL("https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"); } NativeMapView::~NativeMapView() { diff --git a/android/mapboxgl-app.gyp b/android/mapboxgl-app.gyp index 3d421942b..b4702be4f 100644 --- a/android/mapboxgl-app.gyp +++ b/android/mapboxgl-app.gyp @@ -44,7 +44,12 @@ 'dependencies': [ '../mapboxgl.gyp:mbgl-standalone', '../mapboxgl.gyp:mbgl-android', + '../mapboxgl.gyp:copy_certificate_bundle', ], + 'copies': [{ + 'files': [ '../styles/styles' ], + 'destination': '<(PRODUCT_DIR)' + }], }, ], } diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index 37b6a03a4..4b967c6c1 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -406,7 +406,9 @@ void start_request(void *const ptr) { // Carry on the shared pointer in the private information of the CURL handle. curl_easy_setopt(handle, CURLOPT_PRIVATE, context.release()); - curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"); + // FIXME temp work around + curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); + //curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"); curl_easy_setopt(handle, CURLOPT_URL, baton->path.c_str()); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_write_cb); curl_easy_setopt(handle, CURLOPT_WRITEDATA, &baton->response->data); diff --git a/src/storage/http_request.cpp b/src/storage/http_request.cpp index e73a3b8f6..bce74649a 100644 --- a/src/storage/http_request.cpp +++ b/src/storage/http_request.cpp @@ -23,7 +23,7 @@ struct CacheRequestBaton { HTTPRequest::HTTPRequest(ResourceType type_, const std::string &path_, uv_loop_t *loop_, util::ptr store_) : BaseRequest(path_), thread_id(uv_thread_self()), loop(loop_), store(store_), type(type_) { - Log::Error(Event::HttpRequest, "HTTPRequest %s", path.c_str()); + Log::Debug(Event::HttpRequest, "HTTPRequest %s", path.c_str()); if (store) { startCacheRequest(); diff --git a/src/util/mapbox.cpp b/src/util/mapbox.cpp index 1245be405..dbde5d5d6 100644 --- a/src/util/mapbox.cpp +++ b/src/util/mapbox.cpp @@ -13,7 +13,7 @@ std::string normalizeURL(const std::string& url, const std::string& accessToken) throw std::runtime_error("You must provide a Mapbox API access token for Mapbox tile sources"); // FIXME for test only change back to https after - return std::string("http://api.tiles.mapbox.com/v4/") + return std::string("https://api.tiles.mapbox.com/v4/") + url.substr(mapbox.length()) + "?access_token=" + accessToken; From 8f4a6452e6c1b1d700d5daf81c9fc454053a4a14 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 7 Nov 2014 16:28:11 -0800 Subject: [PATCH 075/415] require GL ES 2 (even if ignored by emulator) --- android/java/app/src/main/AndroidManifest.xml | 2 +- android/java/lib/src/main/AndroidManifest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/java/app/src/main/AndroidManifest.xml b/android/java/app/src/main/AndroidManifest.xml index 83995b8d3..b3e55dd97 100644 --- a/android/java/app/src/main/AndroidManifest.xml +++ b/android/java/app/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ android:versionCode="1" android:versionName="1.0" > - + diff --git a/android/java/lib/src/main/AndroidManifest.xml b/android/java/lib/src/main/AndroidManifest.xml index b947bc7d0..6a2e41b79 100644 --- a/android/java/lib/src/main/AndroidManifest.xml +++ b/android/java/lib/src/main/AndroidManifest.xml @@ -3,7 +3,7 @@ android:versionCode="1" android:versionName="1.0" > - + From ee482fbf6ece4486af19e2bf879e729343cf7b07 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 7 Nov 2014 16:28:19 -0800 Subject: [PATCH 076/415] fix styles path --- android/mapboxgl-app.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/mapboxgl-app.gyp b/android/mapboxgl-app.gyp index b4702be4f..6c6dc72e3 100644 --- a/android/mapboxgl-app.gyp +++ b/android/mapboxgl-app.gyp @@ -47,7 +47,7 @@ '../mapboxgl.gyp:copy_certificate_bundle', ], 'copies': [{ - 'files': [ '../styles/styles' ], + 'files': [ '../styles' ], 'destination': '<(PRODUCT_DIR)' }], }, From 7de5c03cfbe7ab3af62a642b986fcfae7feb45ee Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 11:56:30 +1100 Subject: [PATCH 077/415] Move style url and spi key to java Try 565 config then 8888 Remove unused perm --- android/cpp/JNI.cpp | 6 +-- android/cpp/NativeMapView.cpp | 43 ++++++++++++------- android/cpp/NativeMapView.hpp | 5 ++- android/java/lib/src/main/AndroidManifest.xml | 2 - .../java/com/mapbox/mapboxgl/lib/MapView.java | 19 ++++---- .../mapbox/mapboxgl/lib/NativeMapView.java | 6 +-- 6 files changed, 45 insertions(+), 36 deletions(-) diff --git a/android/cpp/JNI.cpp b/android/cpp/JNI.cpp index b8bc34c09..44ecea0b4 100644 --- a/android/cpp/JNI.cpp +++ b/android/cpp/JNI.cpp @@ -140,9 +140,9 @@ using namespace mbgl::android; // TODO: wrap C++ exceptions? // TODO: wrap other sorts of exceptions? eg coffee catch -jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring default_style_json) { +jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring style_url, jstring api_key) { LOG_VERBOSE("nativeCreate"); - NativeMapView* native_map_view = new NativeMapView(env, obj, std_string_from_jstring(env, default_style_json)); + NativeMapView* native_map_view = new NativeMapView(env, obj, std_string_from_jstring(env, style_url), std_string_from_jstring(env, api_key)); if (native_map_view == nullptr) { throw_error(env, "Unable to create NativeMapView."); return 0; @@ -684,7 +684,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { // NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the array correctly (too large) std::array methods = {{ // Can remove the extra brace in C++14 - { "nativeCreate", "(Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, + { "nativeCreate", "(Ljava/lang/String;Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, { "nativeDestroy", "(J)V", reinterpret_cast(&nativeDestroy) }, { "nativeInitializeDisplay", "(J)V", reinterpret_cast(&nativeInitializeDisplay) }, { "nativeTerminateDisplay", "(J)V", reinterpret_cast(&nativeTerminateDisplay) }, diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 1ca474af6..080b18d3b 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -31,8 +31,8 @@ void log_gl_string(GLenum name, const char* label) { } NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, - std::string default_style_json_) : - default_style_json(default_style_json_) { + std::string style_url_, std::string api_key_) : + style_url(style_url_), api_key(api_key_) { LOG_VERBOSE("NativeMapView::NativeMapView"); LOG_ASSERT(env != nullptr); @@ -54,9 +54,8 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, view = new MBGLView(this); map = new mbgl::Map(*view); - // FIXME need way to load this from Java - map->setAccessToken("pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"); - map->setStyleURL("https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"); + map->setAccessToken(api_key); + map->setStyleURL(style_url); } NativeMapView::~NativeMapView() { @@ -114,18 +113,17 @@ bool NativeMapView::initializeDisplay() { log_egl_string(display, EGL_CLIENT_APIS, "Client APIs"); log_egl_string(display, EGL_EXTENSIONS, "Client Extensions"); - // TODO should try 565, then 8888? + // Try 565 first (faster) bool use565 = true; - - const EGLint config_attribs[] = { + EGLint config_attribs[] = { EGL_CONFIG_CAVEAT, EGL_NONE, EGL_CONFORMANT, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, - EGL_BUFFER_SIZE, use565 ? 16 : 32, // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode) - EGL_RED_SIZE, use565 ? 5 : 8, - EGL_GREEN_SIZE, use565 ? 6 : 8, - EGL_BLUE_SIZE, use565 ? 5 : 8, + EGL_BUFFER_SIZE, 16, + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, EGL_NONE @@ -137,9 +135,24 @@ bool NativeMapView::initializeDisplay() { return false; } if (num_configs < 1) { - LOG_ERROR("eglChooseConfig() returned no configs"); - terminateDisplay(); - return false; + LOG_WARN("eglChooseConfig() returned no configs for 565"); + + // Now try 8888 + use565 = false; + config_attribs[9] = 32; // EGL_BUFFER_SIZE // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode) + config_attribs[11] = 8; // EGL_RED_SIZE + config_attribs[13] = 8; // EGL_GREEN_SIZE + config_attribs[15] = 8; // EGL_BLUE_SIZE + if (!eglChooseConfig(display, config_attribs, nullptr, 0, &num_configs)) { + LOG_ERROR("eglChooseConfig(NULL) returned error %d", eglGetError()); + terminateDisplay(); + return false; + } + if (num_configs < 1) { + LOG_ERROR("eglChooseConfig() returned no configs for 8888"); + terminateDisplay(); + return false; + } } // TODO make_unique missing in Android NDK? diff --git a/android/cpp/NativeMapView.hpp b/android/cpp/NativeMapView.hpp index 046cd7c8f..a3081e2ed 100644 --- a/android/cpp/NativeMapView.hpp +++ b/android/cpp/NativeMapView.hpp @@ -41,7 +41,7 @@ class NativeMapView { friend class MBGLView; public: - NativeMapView(JNIEnv* env, jobject obj, std::string default_style_json); + NativeMapView(JNIEnv* env, jobject obj, std::string style_url, std::string api_key); ~NativeMapView(); mbgl::Map* getMap() const { @@ -82,7 +82,8 @@ private: EGLConfig config = nullptr; EGLint format = -1; - std::string default_style_json; + std::string style_url; + std::string api_key; }; class MBGLView: public mbgl::View { diff --git a/android/java/lib/src/main/AndroidManifest.xml b/android/java/lib/src/main/AndroidManifest.xml index b947bc7d0..482bfb4d3 100644 --- a/android/java/lib/src/main/AndroidManifest.xml +++ b/android/java/lib/src/main/AndroidManifest.xml @@ -5,8 +5,6 @@ - diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 51f627f63..f76750c5c 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -57,7 +57,10 @@ public class MapView extends SurfaceView { private NativeMapView mNativeMapView; // Used to style the map - private String mDefaultStyleJSON; + private String mStyleUrl; + + // Used to load map tiles + private String mApiKey; // Touch gesture detectors private GestureDetector mGestureDetector; @@ -115,18 +118,12 @@ public class MapView extends SurfaceView { return; } - // Load the map style - try { - mDefaultStyleJSON = IOUtils.toString(context.getResources() - // .openRawResource(R.raw.style_leith), Charsets.UTF_8); - .openRawResource(R.raw.style), Charsets.UTF_8); - } catch (IOException e) { - throw new RuntimeException( - "Couldn't load default style JSON resource", e); - } + // Load the map style and API key + mStyleUrl = "https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"; + mApiKey = "pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"; // Create the NativeMapView - mNativeMapView = new NativeMapView(this, mDefaultStyleJSON); + mNativeMapView = new NativeMapView(this, mStyleUrl, mApiKey); // Load the attributes TypedArray typedArray = context.obtainStyledAttributes(attrs, diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index bd72846d9..5aa692e2a 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -37,11 +37,11 @@ class NativeMapView { // Constructors // - public NativeMapView(MapView mapView, String defaultStyleJSON) { + public NativeMapView(MapView mapView, String styleUrl, String apiKey) { mMapView = mapView; // Create the NativeMapView - mNativeMapViewPtr = nativeCreate(defaultStyleJSON); + mNativeMapViewPtr = nativeCreate(styleUrl, apiKey); } // @@ -359,7 +359,7 @@ class NativeMapView { super.finalize(); } - private native long nativeCreate(String defaultStyleJSON); + private native long nativeCreate(String styleUrl, String apiKey); private native void nativeDestroy(long nativeMapViewPtr); From c0bcd1e2601d8edd6dd8d346e3874a83e2ddae0c Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 12:49:46 +1100 Subject: [PATCH 078/415] Add cache path variable from Java Use cache path for db location Add Assetbridge to extract asset files to cache path Fix SSL by using extracted ca bundle Use file:// style URL using extracted styles --- Makefile | 5 +- android/LICENSE_ASSETBRIDGE | 23 ++++ android/cpp/JNI.cpp | 7 +- android/cpp/NativeMapView.hpp | 2 + android/java/.gitignore | 3 + .../arieslabs/assetbridge/Assetbridge.java | 107 ++++++++++++++++++ .../java/com/mapbox/mapboxgl/lib/MapView.java | 12 +- .../mapbox/mapboxgl/lib/NativeMapView.java | 6 +- platform/android/cache_database_data.cpp | 3 +- platform/default/http_request_baton_curl.cpp | 11 +- 10 files changed, 167 insertions(+), 12 deletions(-) create mode 100644 android/LICENSE_ASSETBRIDGE create mode 100644 android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java diff --git a/Makefile b/Makefile index 0a1bc56e8..783d348d9 100644 --- a/Makefile +++ b/Makefile @@ -87,6 +87,9 @@ android: $(MAKE) -C build/android BUILDTYPE=$(BUILDTYPE) V=$(V) androidapp && \ mkdir -p android/java/lib/src/main/jniLibs/armeabi-v7a && \ cp build/android/out/$(BUILDTYPE)/lib.target/libmapbox-gl.so android/java/lib/src/main/jniLibs/armeabi-v7a/libmapbox-gl.so && \ + mkdir -p android/java/lib/src/main/assets && \ + cp build/android/out/$(BUILDTYPE)/ca-bundle.crt android/java/lib/src/main/assets/ca-bundle.crt && \ + cp -r build/android/out/$(BUILDTYPE)/styles android/java/lib/src/main/assets/styles && \ cd android/java && \ ./gradlew build @@ -154,7 +157,7 @@ clean: clear_xcode_cache -rm -rf ./macosx/build/ -rm -rf ./config.gypi ./config-ios.gypi ./config-android.gypi -rm -rf ./android/java/build ./android/java/app/build ./android/java/lib/build - -rm -rf ./android/java/lib/src/main/jniLibs + -rm -rf ./android/java/lib/src/main/jniLibs ./android/java/lib/src/main/assets distclean: clean -rm -rf ./mason_packages diff --git a/android/LICENSE_ASSETBRIDGE b/android/LICENSE_ASSETBRIDGE new file mode 100644 index 000000000..09d4125bc --- /dev/null +++ b/android/LICENSE_ASSETBRIDGE @@ -0,0 +1,23 @@ +Copyright (c) 2013 Steve Havelka + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + diff --git a/android/cpp/JNI.cpp b/android/cpp/JNI.cpp index 44ecea0b4..d664ef1ad 100644 --- a/android/cpp/JNI.cpp +++ b/android/cpp/JNI.cpp @@ -20,6 +20,8 @@ namespace mbgl { namespace android { +std::string cache_path; + jmethodID on_map_changed_id = nullptr; jclass lon_lat_class = nullptr; @@ -140,8 +142,9 @@ using namespace mbgl::android; // TODO: wrap C++ exceptions? // TODO: wrap other sorts of exceptions? eg coffee catch -jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring style_url, jstring api_key) { +jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_, jstring style_url, jstring api_key) { LOG_VERBOSE("nativeCreate"); + cache_path = std_string_from_jstring(env, cache_path_); NativeMapView* native_map_view = new NativeMapView(env, obj, std_string_from_jstring(env, style_url), std_string_from_jstring(env, api_key)); if (native_map_view == nullptr) { throw_error(env, "Unable to create NativeMapView."); @@ -684,7 +687,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { // NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the array correctly (too large) std::array methods = {{ // Can remove the extra brace in C++14 - { "nativeCreate", "(Ljava/lang/String;Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, + { "nativeCreate", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, { "nativeDestroy", "(J)V", reinterpret_cast(&nativeDestroy) }, { "nativeInitializeDisplay", "(J)V", reinterpret_cast(&nativeInitializeDisplay) }, { "nativeTerminateDisplay", "(J)V", reinterpret_cast(&nativeTerminateDisplay) }, diff --git a/android/cpp/NativeMapView.hpp b/android/cpp/NativeMapView.hpp index a3081e2ed..459ef9343 100644 --- a/android/cpp/NativeMapView.hpp +++ b/android/cpp/NativeMapView.hpp @@ -14,6 +14,8 @@ namespace mbgl { namespace android { +extern std::string cache_path; + extern jmethodID on_map_changed_id; extern jclass lon_lat_class; diff --git a/android/java/.gitignore b/android/java/.gitignore index 478d78698..7c3a4c469 100644 --- a/android/java/.gitignore +++ b/android/java/.gitignore @@ -10,5 +10,8 @@ build/ */build/ *.so +# Lib assets +lib/src/main/assets/ + # Local settings local.properties diff --git a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java new file mode 100644 index 000000000..e322d1c61 --- /dev/null +++ b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java @@ -0,0 +1,107 @@ +package com.arieslabs.assetbridge; + +import android.content.Context; +import android.content.res.AssetManager; +import android.util.Log; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + + + +public class Assetbridge { + /*static { + System.loadLibrary("assetbridge"); + }*/ + + + // unpack + public static void unpack(Context c) { + + try { + // first let's get the temp directory + String tmpdir = c.getCacheDir().getPath(); + + // now we need the assetmanager + AssetManager am = c.getAssets(); + String[] assets = am.list(""); + + // iterate on the files... + for(String asset : assets) { + copyAssetFolder(am, asset, tmpdir + "/" + asset); + } + + // last, set the ASSETDIR environment variable for the C + // parts of the procee + //setassetdir(c.getCacheDir().getPath()); + + } catch (IOException e) { + Log.e("Assetbridge", "Can't unpack assets from APK", e); + } + + } + + + public static void copyAssetFolder(AssetManager am, String src, String dest) + throws IOException{ + + InputStream srcIS = null; + File destfh; + + // this is the only way we can tell if this is a file or a + // folder - we have to open the asset, and if the open fails, + // it's a folder... + boolean isDir = false; + try { + srcIS = am.open(src); + } catch (FileNotFoundException e) { + isDir = true; + } + + // either way, we'll use the dest as a File + destfh = new File(dest); + + // and now, depending on .. + if(isDir) { + + // If the directory doesn't yet exist, create it + if( !destfh.exists() ){ + destfh.mkdir(); + } + + // list the assets in the directory... + String assets[] = am.list(src); + + // and copy them all using same. + for(String asset : assets) { + copyAssetFolder(am, src + "/" + asset, dest + "/" + asset); + } + + } else { + + int count, buffer_len = 2048; + byte[] data = new byte[buffer_len]; + + // copy the file from the assets subsystem to the filesystem + FileOutputStream destOS = new FileOutputStream(destfh); + + //copy the file content in bytes + while( (count = srcIS.read(data, 0, buffer_len)) != -1) { + destOS.write(data, 0, count); + } + + // and close the two files + srcIS.close(); + destOS.close(); + + } + } + + + // the native method to set the environment variable + //public static native void setassetdir(String s); +} diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index f76750c5c..1bd092c73 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -27,6 +27,7 @@ import android.widget.ZoomButtonsController; import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector; +import com.arieslabs.assetbridge.Assetbridge; // Custom view that shows a Map // Based on SurfaceView as we use OpenGL ES to render @@ -118,12 +119,19 @@ public class MapView extends SurfaceView { return; } + // Get the cache path + String cachePath = context.getCacheDir().getPath(); + + // Extract the asset files + Assetbridge.unpack(context); + // Load the map style and API key - mStyleUrl = "https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"; + //mStyleUrl = "https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"; + mStyleUrl = "file://" + cachePath + "/styles/styles/bright-v6.json"; mApiKey = "pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"; // Create the NativeMapView - mNativeMapView = new NativeMapView(this, mStyleUrl, mApiKey); + mNativeMapView = new NativeMapView(this, cachePath, mStyleUrl, mApiKey); // Load the attributes TypedArray typedArray = context.obtainStyledAttributes(attrs, diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index 5aa692e2a..5be250cf9 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -37,11 +37,11 @@ class NativeMapView { // Constructors // - public NativeMapView(MapView mapView, String styleUrl, String apiKey) { + public NativeMapView(MapView mapView, String cachePath, String styleUrl, String apiKey) { mMapView = mapView; // Create the NativeMapView - mNativeMapViewPtr = nativeCreate(styleUrl, apiKey); + mNativeMapViewPtr = nativeCreate(cachePath, styleUrl, apiKey); } // @@ -359,7 +359,7 @@ class NativeMapView { super.finalize(); } - private native long nativeCreate(String styleUrl, String apiKey); + private native long nativeCreate(String cachePath, String styleUrl, String apiKey); private native void nativeDestroy(long nativeMapViewPtr); diff --git a/platform/android/cache_database_data.cpp b/platform/android/cache_database_data.cpp index e1765efcd..61c4efc15 100644 --- a/platform/android/cache_database_data.cpp +++ b/platform/android/cache_database_data.cpp @@ -1,4 +1,5 @@ #include +#include "../../android/cpp/NativeMapView.hpp" namespace mbgl { namespace platform { @@ -6,7 +7,7 @@ namespace platform { // Returns the path to the default cache database on this system. std::string defaultCacheDatabase() { // TODO need to use get cache dir from java - return "/data/data/com.mapbox.mapboxgl.app/mbgl-cache.db"; + return mbgl::android::cache_path + "/mbgl-cache.db"; } } diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index 4b967c6c1..8ca778159 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "../../android/cpp/NativeMapView.hpp" #include #include @@ -404,11 +405,15 @@ void start_request(void *const ptr) { baton->response = std::make_unique(); } +#ifndef __ANDROID__ + std::string ca_path = "ca-bundle.crt"; +#else + std::string ca_path = mbgl::android::cache_path + "/ca-bundle.crt"; +#endif + // Carry on the shared pointer in the private information of the CURL handle. curl_easy_setopt(handle, CURLOPT_PRIVATE, context.release()); - // FIXME temp work around - curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0); - //curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"); + curl_easy_setopt(handle, CURLOPT_CAINFO, ca_path.c_str()); curl_easy_setopt(handle, CURLOPT_URL, baton->path.c_str()); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_write_cb); curl_easy_setopt(handle, CURLOPT_WRITEDATA, &baton->response->data); From 39aa91b633831418a8466f95ee1b4404819ec8d3 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 13:06:15 +1100 Subject: [PATCH 079/415] Add android build instructions --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index 022377e99..3ce05871b 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,37 @@ Or proceed to building the debug application with: make linux +## Android (on Linux) + +Follow Linux steps until ./configure + +Install Oracle JDK 7 + + wget http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-linux-x64.tar.gz + tar -xzf jdk-7u71-linux-x64.tar.gz + export JAVA_HOME="/dir/to/jdk1.7.0_71" + +Install Android NDK + + wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin + chmod +x android-ndk-r10c-linux-x86_64.bin + ./android-ndk-r10c-linux-x86_64.bin + export ANDROID_NDK_PATH="/dir/to/android-ndk-r10c" + +Install Android SDK + + wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz + tar -xzf ./android-sdk_r23.0.2-linux.tgz + ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21 + export ANDROID_HOME="/dir/to/android-sdk-linux" + +Run + + make android + +APKs for testing are output to android/java/app/build/outputs/apk/app-debug.apk + +You can also open android/java in Android Studio. # Troubleshooting From 160712274db92aa1c0e83b4f296087cd6adbf05e Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 13:10:16 +1100 Subject: [PATCH 080/415] Fix Linux build --- platform/default/http_request_baton_curl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index 8ca778159..5549abea1 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -3,7 +3,9 @@ #include #include #include -#include "../../android/cpp/NativeMapView.hpp" +#ifdef __ANDROID__ + #include "../../android/cpp/NativeMapView.hpp" +#endif #include #include From 5b6c7aa870dc355022075763a18038f136b26383 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 16:23:43 +1100 Subject: [PATCH 081/415] Stop file not found error in Makefile Update JNI methods Tiny up Java --- Makefile | 1 + android/cpp/JNI.cpp | 145 +++++++++++++----- android/cpp/NativeMapView.cpp | 7 +- android/cpp/NativeMapView.hpp | 10 +- .../com/mapbox/mapboxgl/app/MainActivity.java | 3 +- android/java/lib/build.gradle | 3 +- .../gesturedetectors/BaseGestureDetector.java | 3 +- .../gesturedetectors/MoveGestureDetector.java | 9 +- .../TwoFingerGestureDetector.java | 19 +-- .../arieslabs/assetbridge/Assetbridge.java | 17 +- .../java/com/mapbox/mapboxgl/lib/LonLat.java | 6 +- .../com/mapbox/mapboxgl/lib/LonLatZoom.java | 9 +- .../java/com/mapbox/mapboxgl/lib/MapView.java | 36 ++--- .../mapbox/mapboxgl/lib/NativeMapView.java | 84 ++++++++-- 14 files changed, 222 insertions(+), 130 deletions(-) diff --git a/Makefile b/Makefile index 783d348d9..70e9dd815 100644 --- a/Makefile +++ b/Makefile @@ -72,6 +72,7 @@ build/linux/mapboxgl-app.xcodeproj: linux/mapboxgl-app.gyp config.gypi .PHONY: android android: ./scripts/local_mason.sh && \ + MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env PATH && \ export CXX="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ export CC="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CC`" && \ export LD="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env LD`" && \ diff --git a/android/cpp/JNI.cpp b/android/cpp/JNI.cpp index d664ef1ad..e1731aa99 100644 --- a/android/cpp/JNI.cpp +++ b/android/cpp/JNI.cpp @@ -37,11 +37,11 @@ jfieldID lon_lat_zoom_zoom_id = nullptr; jclass runtime_exception_class = nullptr; -jmethodID set_to_array_id = nullptr; +jmethodID list_to_array_id = nullptr; -jclass tree_set_class = nullptr; -jmethodID tree_set_constructor_id = nullptr; -jmethodID tree_set_add_id = nullptr; +jclass array_list_class = nullptr; +jmethodID array_list_constructor_id = nullptr; +jmethodID array_list_add_id = nullptr; bool throw_error(JNIEnv* env, const char* msg) { if (env->ThrowNew(runtime_exception_class, msg) < 0) { @@ -83,12 +83,10 @@ jstring std_string_to_jstring(JNIEnv* env, std::string str) { return jstr; } - -// TODO: change from Java TreeSet to ArrayList -std::vector std_vector_string_from_jobject(JNIEnv* env, jobject jset) { +std::vector std_vector_string_from_jobject(JNIEnv* env, jobject jlist) { std::vector vector; - jobjectArray array = reinterpret_cast(env->CallObjectMethod(jset, set_to_array_id)); + jobjectArray array = reinterpret_cast(env->CallObjectMethod(jlist, list_to_array_id)); if (env->ExceptionCheck() || (array == nullptr)) { env->ExceptionDescribe(); return vector; @@ -113,23 +111,22 @@ std::vector std_vector_string_from_jobject(JNIEnv* env, jobject jse return vector; } -// TODO: change from Java TreeSet to ArrayList jobject std_vector_string_to_jobject(JNIEnv* env, std::vector vector) { - jobject jset = env->NewObject(tree_set_class, tree_set_constructor_id); - if (jset == nullptr) { + jobject jlist = env->NewObject(array_list_class, array_list_constructor_id); + if (jlist == nullptr) { env->ExceptionDescribe(); return nullptr; } for (std::string str : vector) { - env->CallBooleanMethod(jset, tree_set_add_id, std_string_to_jstring(env, str)); + env->CallBooleanMethod(jlist, array_list_add_id, std_string_to_jstring(env, str)); if (env->ExceptionCheck()) { env->ExceptionDescribe(); return nullptr; } } - return jset; + return jlist; } } // namespace android @@ -142,10 +139,10 @@ using namespace mbgl::android; // TODO: wrap C++ exceptions? // TODO: wrap other sorts of exceptions? eg coffee catch -jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_, jstring style_url, jstring api_key) { +jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_) { LOG_VERBOSE("nativeCreate"); cache_path = std_string_from_jstring(env, cache_path_); - NativeMapView* native_map_view = new NativeMapView(env, obj, std_string_from_jstring(env, style_url), std_string_from_jstring(env, api_key)); + NativeMapView* native_map_view = new NativeMapView(env, obj); if (native_map_view == nullptr) { throw_error(env, "Unable to create NativeMapView."); return 0; @@ -224,6 +221,13 @@ void JNICALL nativeStop(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { native_map_view->stop(); } +void JNICALL nativeRun(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + LOG_VERBOSE("nativeRun"); + LOG_ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->run(); +} + void JNICALL nativeRerender(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeRerender"); LOG_ASSERT(native_map_view_ptr != 0); @@ -245,6 +249,27 @@ void JNICALL nativeCleanup(JNIEnv* env, jobject obj, jlong native_map_view_ptr) native_map_view->getMap()->cleanup(); } +void JNICALL nativeTerminate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + LOG_VERBOSE("nativeTerminate"); + LOG_ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->terminate(); +} + +jboolean JNICALL nativeNeedsSwap(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + LOG_VERBOSE("nativeNeedsSwap"); + LOG_ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + return native_map_view->getMap()->needsSwap(); +} + +void JNICALL nativeSwapped(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + LOG_VERBOSE("nativeSwapped"); + LOG_ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->swapped(); +} + void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio) { LOG_VERBOSE("nativeResize"); LOG_ASSERT(native_map_view_ptr != 0); @@ -271,11 +296,11 @@ void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, j native_map_view->getMap()->resize(width, height, ratio, fb_width, fb_height); } -void JNICALL nativeSetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject applied_classes) { +void JNICALL nativeSetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject classes) { LOG_VERBOSE("nativeSetAppliedClasses"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setAppliedClasses(std_vector_string_from_jobject(env, applied_classes)); + native_map_view->getMap()->setAppliedClasses(std_vector_string_from_jobject(env, classes)); } jobject JNICALL nativeGetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { @@ -294,11 +319,18 @@ void JNICALL nativeSetDefaultTransitionDuration(JNIEnv* env, jobject obj, jlong native_map_view->getMap()->setDefaultTransitionDuration(duration_milliseconds); } -void JNICALL nativeSetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring new_style_json) { +void JNICALL nativeSetStyleURL(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring url) { + LOG_VERBOSE("nativeSetStyleURL"); + LOG_ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->setStyleURL(std_string_from_jstring(env, url)); +} + +void JNICALL nativeSetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring new_style_json, jstring base) { LOG_VERBOSE("nativeSetStyleJSON"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setStyleJSON(std_string_from_jstring(env, new_style_json)); + native_map_view->getMap()->setStyleJSON(std_string_from_jstring(env, new_style_json), std_string_from_jstring(env, base)); } jstring JNICALL nativeGetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { @@ -308,6 +340,20 @@ jstring JNICALL nativeGetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_vi return std_string_to_jstring(env, native_map_view->getMap()->getStyleJSON()); } +void JNICALL nativeSetAccessToken(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring access_token) { + LOG_VERBOSE("nativeSetAccessToken"); + LOG_ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->setAccessToken(std_string_from_jstring(env, access_token)); +} + +jstring JNICALL nativeGetAccessToken(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + LOG_VERBOSE("nativeGetAccessToken"); + LOG_ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + return std_string_to_jstring(env, native_map_view->getMap()->getAccessToken()); +} + void JNICALL nativeCancelTransitions(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeCancelTransitions"); LOG_ASSERT(native_map_view_ptr != 0); @@ -570,6 +616,13 @@ jboolean JNICALL nativeGetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ return native_map_view->getMap()->getDebug(); } +void JNICALL nativeSetReachability(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jboolean status) { + LOG_VERBOSE("nativeSetReachability"); + LOG_ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->getMap()->setReachability(status); +} + } // namespace extern "C" { @@ -655,39 +708,39 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_ERR; } - jclass set_class = env->FindClass("java/util/Set"); - if (set_class == nullptr) { + jclass list_class = env->FindClass("java/util/List"); + if (list_class == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } - set_to_array_id = env->GetMethodID(set_class, "toArray", "()[Ljava/lang/Object;"); - if (set_to_array_id == nullptr) { + list_to_array_id = env->GetMethodID(list_class, "toArray", "()[Ljava/lang/Object;"); + if (list_to_array_id == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } - tree_set_class = env->FindClass("java/util/TreeSet"); - if (tree_set_class == nullptr) { + array_list_class = env->FindClass("java/util/ArrayList"); + if (array_list_class == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } - tree_set_constructor_id = env->GetMethodID(tree_set_class, "", "()V"); - if (tree_set_constructor_id == nullptr) { + array_list_constructor_id = env->GetMethodID(array_list_class, "", "()V"); + if (array_list_constructor_id == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } - tree_set_add_id = env->GetMethodID(tree_set_class, "add", "(Ljava/lang/Object;)Z"); - if (tree_set_add_id == nullptr) { + array_list_add_id = env->GetMethodID(array_list_class, "add", "(Ljava/lang/Object;)Z"); + if (array_list_add_id == nullptr) { env->ExceptionDescribe(); return JNI_ERR; } // NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the array correctly (too large) - std::array methods = {{ // Can remove the extra brace in C++14 - { "nativeCreate", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, + std::array methods = {{ // Can remove the extra brace in C++14 + { "nativeCreate", "(Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, { "nativeDestroy", "(J)V", reinterpret_cast(&nativeDestroy) }, { "nativeInitializeDisplay", "(J)V", reinterpret_cast(&nativeInitializeDisplay) }, { "nativeTerminateDisplay", "(J)V", reinterpret_cast(&nativeTerminateDisplay) }, @@ -697,16 +750,23 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { { "nativeDestroySurface", "(J)V", reinterpret_cast(&nativeDestroySurface) }, { "nativeStart", "(J)V", reinterpret_cast(&nativeStart) }, { "nativeStop", "(J)V", reinterpret_cast(&nativeStop) }, + { "nativeRun", "(J)V", reinterpret_cast(&nativeRun) }, { "nativeRerender", "(J)V", reinterpret_cast(&nativeRerender) }, { "nativeUpdate", "(J)V", reinterpret_cast(&nativeUpdate) }, { "nativeCleanup", "(J)V", reinterpret_cast(&nativeCleanup) }, + { "nativeTerminate", "(J)V", reinterpret_cast(&nativeTerminate) }, + { "nativeNeedsSwap", "(J)Z", reinterpret_cast(&nativeNeedsSwap) }, + { "nativeSwapped", "(J)V", reinterpret_cast(&nativeSwapped) }, { "nativeResize", "(JIIF)V", reinterpret_cast(static_cast(&nativeResize)) }, { "nativeResize", "(JIIFII)V", reinterpret_cast(static_cast(&nativeResize)) }, - { "nativeSetAppliedClasses", "(JLjava/util/Set;)V", reinterpret_cast(&nativeSetAppliedClasses) }, - { "nativeGetAppliedClasses", "(J)Ljava/util/Set;", reinterpret_cast(&nativeGetAppliedClasses) }, + { "nativeSetAppliedClasses", "(JLjava/util/List;)V", reinterpret_cast(&nativeSetAppliedClasses) }, + { "nativeGetAppliedClasses", "(J)Ljava/util/List;", reinterpret_cast(&nativeGetAppliedClasses) }, { "nativeSetDefaultTransitionDuration", "(JJ)V", reinterpret_cast(&nativeSetDefaultTransitionDuration) }, - { "nativeSetStyleJSON", "(JLjava/lang/String;)V", reinterpret_cast(&nativeSetStyleJSON) }, + { "nativeSetStyleURL", "(JLjava/lang/String;)V", reinterpret_cast(&nativeSetStyleURL) }, + { "nativeSetStyleJSON", "(JLjava/lang/String;Ljava/lang/String;)V", reinterpret_cast(&nativeSetStyleJSON) }, { "nativeGetStyleJSON", "(J)Ljava/lang/String;", reinterpret_cast(&nativeGetStyleJSON) }, + { "nativeSetAccessToken", "(JLjava/lang/String;)V", reinterpret_cast(&nativeSetAccessToken) }, + { "nativeGetAccessToken", "(J)Ljava/lang/String;", reinterpret_cast(&nativeGetAccessToken) }, { "nativeCancelTransitions", "(J)V", reinterpret_cast(&nativeCancelTransitions) }, { "nativeMoveBy", "(JDDD)V", reinterpret_cast(&nativeMoveBy) }, { "nativeSetLonLat", "(JLcom/mapbox/mapboxgl/lib/LonLat;D)V", reinterpret_cast(&nativeSetLonLat) }, @@ -736,7 +796,8 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { { "nativeCanRotate", "(J)Z", reinterpret_cast(&nativeCanRotate) }, { "nativeSetDebug", "(JZ)V", reinterpret_cast(&nativeSetDebug) }, { "nativeToggleDebug", "(J)V", reinterpret_cast(&nativeToggleDebug) }, - { "nativeGetDebug", "(J)Z", reinterpret_cast(&nativeGetDebug) } + { "nativeGetDebug", "(J)Z", reinterpret_cast(&nativeGetDebug) }, + { "nativeSetReachability", "(JZ)V", reinterpret_cast(&nativeSetReachability) } }}; if (env->RegisterNatives(native_map_view_class, methods.data(), methods.size()) < 0) { @@ -765,8 +826,8 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_ERR; } - tree_set_class = reinterpret_cast(env->NewGlobalRef(tree_set_class)); - if (tree_set_class == nullptr) { + array_list_class = reinterpret_cast(env->NewGlobalRef(array_list_class)); + if (array_list_class == nullptr) { env->ExceptionDescribe(); env->DeleteGlobalRef(lon_lat_class); env->DeleteGlobalRef(lon_lat_zoom_class); @@ -804,12 +865,12 @@ extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { env->DeleteGlobalRef(runtime_exception_class); runtime_exception_class = nullptr; - set_to_array_id = nullptr; + list_to_array_id = nullptr; - env->DeleteGlobalRef(tree_set_class); - tree_set_class = nullptr; - tree_set_constructor_id = nullptr; - tree_set_add_id = nullptr; + env->DeleteGlobalRef(array_list_class); + array_list_class = nullptr; + array_list_constructor_id = nullptr; + array_list_add_id = nullptr; } } // extern "C" diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 080b18d3b..4765661b2 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -30,9 +30,7 @@ void log_gl_string(GLenum name, const char* label) { } } -NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, - std::string style_url_, std::string api_key_) : - style_url(style_url_), api_key(api_key_) { +NativeMapView::NativeMapView(JNIEnv* env, jobject obj_) { LOG_VERBOSE("NativeMapView::NativeMapView"); LOG_ASSERT(env != nullptr); @@ -53,9 +51,6 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_, view = new MBGLView(this); map = new mbgl::Map(*view); - - map->setAccessToken(api_key); - map->setStyleURL(style_url); } NativeMapView::~NativeMapView() { diff --git a/android/cpp/NativeMapView.hpp b/android/cpp/NativeMapView.hpp index 459ef9343..866365c6d 100644 --- a/android/cpp/NativeMapView.hpp +++ b/android/cpp/NativeMapView.hpp @@ -31,11 +31,11 @@ extern jfieldID lon_lat_zoom_zoom_id; extern jclass runtime_exception_class; -extern jmethodID set_to_array_id; +extern jmethodID list_to_array_id; -extern jclass tree_set_class; -extern jmethodID tree_set_constructor_id; -extern jmethodID tree_set_add_id; +extern jclass array_list_class; +extern jmethodID array_list_constructor_id; +extern jmethodID array_list_add_id; class MBGLView; @@ -43,7 +43,7 @@ class NativeMapView { friend class MBGLView; public: - NativeMapView(JNIEnv* env, jobject obj, std::string style_url, std::string api_key); + NativeMapView(JNIEnv* env, jobject obj); ~NativeMapView(); mbgl::Map* getMap() const { diff --git a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java index 43537f9ac..798e05a84 100644 --- a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java +++ b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java @@ -2,6 +2,7 @@ package com.mapbox.mapboxgl.app; import android.app.Activity; import android.os.Bundle; +import android.support.annotation.NonNull; import android.util.Log; import com.mapbox.mapboxgl.lib.MapView; @@ -88,7 +89,7 @@ public class MainActivity extends Activity { // Called before activity is destroyed @Override - protected void onSaveInstanceState(Bundle outState) { + protected void onSaveInstanceState(@NonNull Bundle outState) { Log.v(TAG, "onSaveInstanceState"); // Need to retrieve any saved state from the map diff --git a/android/java/lib/build.gradle b/android/java/lib/build.gradle index 900bedd59..3784d2a9f 100644 --- a/android/java/lib/build.gradle +++ b/android/java/lib/build.gradle @@ -27,5 +27,6 @@ android { } dependencies { - compile "commons-io:commons-io:2.4" + compile 'commons-io:commons-io:2.4' + compile 'com.android.support:support-annotations:21.0.0' } diff --git a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java index 86d1d50f6..720a1f541 100644 --- a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java +++ b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java @@ -91,7 +91,8 @@ public abstract class BaseGestureDetector { * handling in this implementation may set the gesture out of progress (via * mGestureInProgress). * - * @param action + * + * @param actionCode * @param event */ protected abstract void handleInProgressEvent(int actionCode, diff --git a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java index 0136183ed..2430f3f92 100644 --- a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java +++ b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java @@ -74,8 +74,6 @@ public class MoveGestureDetector extends BaseGestureDetector { private final OnMoveGestureListener mListener; - private PointF mCurrFocusInternal; - private PointF mPrevFocusInternal; private PointF mFocusExternal = new PointF(); private PointF mFocusDeltaExternal = new PointF(); @@ -134,8 +132,8 @@ public class MoveGestureDetector extends BaseGestureDetector { final MotionEvent prev = mPrevEvent; // Focus intenal - mCurrFocusInternal = determineFocalPoint(curr); - mPrevFocusInternal = determineFocalPoint(prev); + PointF mCurrFocusInternal = determineFocalPoint(curr); + PointF mPrevFocusInternal = determineFocalPoint(prev); // Focus external // - Prevent skipping of focus delta when a finger is added or removed @@ -155,8 +153,7 @@ public class MoveGestureDetector extends BaseGestureDetector { * Determine (multi)finger focal point (a.k.a. center point between all * fingers) * - * @param MotionEvent - * e + * @param e * @return PointF focal point */ private PointF determineFocalPoint(MotionEvent e) { diff --git a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java index 5215a91bb..e26d6b5ae 100644 --- a/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java +++ b/android/java/lib/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java @@ -38,8 +38,6 @@ import android.view.ViewConfiguration; public abstract class TwoFingerGestureDetector extends BaseGestureDetector { private final float mEdgeSlop; - private float mRightSlopEdge; - private float mBottomSlopEdge; protected float mPrevFingerDiffX; protected float mPrevFingerDiffY; @@ -168,22 +166,20 @@ public abstract class TwoFingerGestureDetector extends BaseGestureDetector { protected boolean isSloppyGesture(MotionEvent event) { // As orientation can change, query the metrics in touch down DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); - mRightSlopEdge = metrics.widthPixels - mEdgeSlop; - mBottomSlopEdge = metrics.heightPixels - mEdgeSlop; + float mRightSlopEdge = metrics.widthPixels - mEdgeSlop; + float mBottomSlopEdge = metrics.heightPixels - mEdgeSlop; final float edgeSlop = mEdgeSlop; - final float rightSlop = mRightSlopEdge; - final float bottomSlop = mBottomSlopEdge; final float x0 = event.getRawX(); final float y0 = event.getRawY(); final float x1 = getRawX(event, 1); final float y1 = getRawY(event, 1); - boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlop - || y0 > bottomSlop; - boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlop - || y1 > bottomSlop; + boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > mRightSlopEdge + || y0 > mBottomSlopEdge; + boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > mRightSlopEdge + || y1 > mBottomSlopEdge; if (p0sloppy && p1sloppy) { return true; @@ -199,8 +195,7 @@ public abstract class TwoFingerGestureDetector extends BaseGestureDetector { * Determine (multi)finger focal point (a.k.a. center point between all * fingers) * - * @param MotionEvent - * e + * @param e * @return PointF focal point */ public static PointF determineFocalPoint(MotionEvent e) { diff --git a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java index e322d1c61..8730cf4ca 100644 --- a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java +++ b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java @@ -3,22 +3,18 @@ package com.arieslabs.assetbridge; import android.content.Context; import android.content.res.AssetManager; import android.util.Log; + import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; - - public class Assetbridge { /*static { System.loadLibrary("assetbridge"); }*/ - // unpack public static void unpack(Context c) { @@ -47,7 +43,7 @@ public class Assetbridge { public static void copyAssetFolder(AssetManager am, String src, String dest) - throws IOException{ + throws IOException { InputStream srcIS = null; File destfh; @@ -69,8 +65,10 @@ public class Assetbridge { if(isDir) { // If the directory doesn't yet exist, create it - if( !destfh.exists() ){ - destfh.mkdir(); + if( !destfh.exists() ) { + if (!destfh.mkdir()) { + throw new IOException("Failed to mkdir"); + } } // list the assets in the directory... @@ -101,7 +99,6 @@ public class Assetbridge { } } - // the native method to set the environment variable //public static native void setassetdir(String s); } diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLat.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLat.java index 87260c2a9..69e9a6835 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLat.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLat.java @@ -65,11 +65,7 @@ public class LonLat implements Parcelable { if (getClass() != obj.getClass()) return false; LonLat other = (LonLat) obj; - if (Double.doubleToLongBits(lat) != Double.doubleToLongBits(other.lat)) - return false; - if (Double.doubleToLongBits(lon) != Double.doubleToLongBits(other.lon)) - return false; - return true; + return Double.doubleToLongBits(lat) == Double.doubleToLongBits(other.lat) && Double.doubleToLongBits(lon) == Double.doubleToLongBits(other.lon); } @Override diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLatZoom.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLatZoom.java index d20cccd47..ad8b5dd47 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLatZoom.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/LonLatZoom.java @@ -93,14 +93,7 @@ public class LonLatZoom implements Parcelable { if (getClass() != obj.getClass()) return false; LonLatZoom other = (LonLatZoom) obj; - if (Double.doubleToLongBits(lat) != Double.doubleToLongBits(other.lat)) - return false; - if (Double.doubleToLongBits(lon) != Double.doubleToLongBits(other.lon)) - return false; - if (Double.doubleToLongBits(zoom) != Double - .doubleToLongBits(other.zoom)) - return false; - return true; + return Double.doubleToLongBits(lat) == Double.doubleToLongBits(other.lat) && Double.doubleToLongBits(lon) == Double.doubleToLongBits(other.lon) && Double.doubleToLongBits(zoom) == Double.doubleToLongBits(other.zoom); } @Override diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 1bd092c73..6360bd164 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -1,9 +1,5 @@ package com.mapbox.mapboxgl.lib; -import java.io.IOException; - -import org.apache.commons.io.Charsets; -import org.apache.commons.io.IOUtils; import org.json.JSONException; import org.json.JSONObject; @@ -12,6 +8,7 @@ import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.graphics.PointF; import android.os.Bundle; +import android.support.annotation.NonNull; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; @@ -61,7 +58,7 @@ public class MapView extends SurfaceView { private String mStyleUrl; // Used to load map tiles - private String mApiKey; + private String mAccessToken; // Touch gesture detectors private GestureDetector mGestureDetector; @@ -90,19 +87,19 @@ public class MapView extends SurfaceView { // Called when no properties are being set from XML public MapView(Context context) { super(context); - initialize(context, null, 0); + initialize(context, null); } // Called when properties are being set from XML public MapView(Context context, AttributeSet attrs) { super(context, attrs); - initialize(context, attrs, 0); + initialize(context, attrs); } // Called when properties are being set from XML public MapView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - initialize(context, attrs, defStyle); + initialize(context, attrs); } // @@ -110,7 +107,7 @@ public class MapView extends SurfaceView { // // Common initialization code goes here - private void initialize(Context context, AttributeSet attrs, int defStyle) { + private void initialize(Context context, AttributeSet attrs) { Log.v(TAG, "initialize"); // Check if we are in Eclipse UI editor @@ -128,10 +125,12 @@ public class MapView extends SurfaceView { // Load the map style and API key //mStyleUrl = "https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"; mStyleUrl = "file://" + cachePath + "/styles/styles/bright-v6.json"; - mApiKey = "pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"; + mAccessToken = "pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"; // Create the NativeMapView - mNativeMapView = new NativeMapView(this, cachePath, mStyleUrl, mApiKey); + mNativeMapView = new NativeMapView(this, cachePath); + mNativeMapView.setStyleURL(mStyleUrl); + mNativeMapView.setAccessToken(mAccessToken); // Load the attributes TypedArray typedArray = context.obtainStyledAttributes(attrs, @@ -500,7 +499,7 @@ public class MapView extends SurfaceView { // Called when view is hidden and shown @Override - protected void onVisibilityChanged(View changedView, int visibility) { + protected void onVisibilityChanged(@NonNull View changedView, int visibility) { // Required by ZoomButtonController (from Android SDK documentation) if ((mZoomButtonsController != null) && (visibility != View.VISIBLE)) { mZoomButtonsController.setVisible(false); @@ -542,7 +541,7 @@ public class MapView extends SurfaceView { // Called when user touches the screen, all positions are absolute @Override - public boolean onTouchEvent(MotionEvent event) { + public boolean onTouchEvent(@NonNull MotionEvent event) { // Check and ignore non touch or left clicks if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { @@ -563,11 +562,7 @@ public class MapView extends SurfaceView { case MotionEvent.ACTION_POINTER_DOWN: // Second pointer down - if (event.getPointerCount() == 2) { - mTwoTap = true; - } else { - mTwoTap = false; - } + mTwoTap = event.getPointerCount() == 2; break; case MotionEvent.ACTION_POINTER_UP: @@ -598,6 +593,7 @@ public class MapView extends SurfaceView { } // Do not change this code! It will break very easily. + // TODO fix up these warnings boolean retVal = rotateRetVal || scaleRetVal; retVal = mGestureDetector.onTouchEvent(event) || retVal; return retVal || super.onTouchEvent(event); @@ -871,7 +867,7 @@ public class MapView extends SurfaceView { // Called when the user presses a key, also called for repeating keys held // down @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { + public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { // If the user has held the scroll key down for a while then accelerate // the scroll speed double scrollDist = event.getRepeatCount() >= 5 ? 50.0 : 10.0; @@ -1123,7 +1119,7 @@ public class MapView extends SurfaceView { // Called when the mouse pointer enters or exits the view // or when it fades in or out due to movement @Override - public boolean onHoverEvent(MotionEvent event) { + public boolean onHoverEvent(@NonNull MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_HOVER_ENTER: case MotionEvent.ACTION_HOVER_MOVE: diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index 5be250cf9..56578eeb2 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -1,10 +1,10 @@ package com.mapbox.mapboxgl.lib; -import java.util.Set; - import android.util.Log; import android.view.Surface; +import java.util.List; + // Class that wraps the native methods for convenience class NativeMapView { @@ -37,11 +37,11 @@ class NativeMapView { // Constructors // - public NativeMapView(MapView mapView, String cachePath, String styleUrl, String apiKey) { + public NativeMapView(MapView mapView, String cachePath) { mMapView = mapView; // Create the NativeMapView - mNativeMapViewPtr = nativeCreate(cachePath, styleUrl, apiKey); + mNativeMapViewPtr = nativeCreate(cachePath); } // @@ -80,6 +80,10 @@ class NativeMapView { nativeStop(mNativeMapViewPtr); } + public void run() { + nativeRun(mNativeMapViewPtr); + } + public void rerender() { nativeRerender(mNativeMapViewPtr); } @@ -92,6 +96,18 @@ class NativeMapView { nativeCleanup(mNativeMapViewPtr); } + public void terminate() { + nativeTerminate(mNativeMapViewPtr); + } + + public boolean needsSwap() { + return nativeNeedsSwap(mNativeMapViewPtr); + } + + public void swapped() { + nativeSwapped(mNativeMapViewPtr); + } + public void resize(int width, int height) { resize(width, height, 1.0f); } @@ -148,11 +164,15 @@ class NativeMapView { nativeResize(mNativeMapViewPtr, width, height, ratio, fbWidth, fbHeight); } - public void setAppliedClasses(Set appliedClasses) { - nativeSetAppliedClasses(mNativeMapViewPtr, appliedClasses); + /*public void Set getActiveSources() { + return nativeGetActiveSources(mNativeMapViewPtr); + }*/ + + public void setAppliedClasses(List classes) { + nativeSetAppliedClasses(mNativeMapViewPtr, classes); } - public Set getAppliedClasses() { + public List getAppliedClasses() { return nativeGetAppliedClasses(mNativeMapViewPtr); } @@ -170,14 +190,30 @@ class NativeMapView { durationMilliseconds); } + public void setStyleURL(String url) { + nativeSetStyleURL(mNativeMapViewPtr, url); + } + public void setStyleJSON(String newStyleJSON) { - nativeSetStyleJSON(mNativeMapViewPtr, newStyleJSON); + setStyleJSON(newStyleJSON, ""); + } + + public void setStyleJSON(String newStyleJSON, String base) { + nativeSetStyleJSON(mNativeMapViewPtr, newStyleJSON, base); } public String getStyleJSON() { return nativeGetStyleJSON(mNativeMapViewPtr); } + public void setAccessToken(String accessToken) { + nativeSetAccessToken(mNativeMapViewPtr, accessToken); + } + + public String getAccessToken() { + return nativeGetAccessToken(mNativeMapViewPtr); + } + public void cancelTransitions() { nativeCancelTransitions(mNativeMapViewPtr); } @@ -339,6 +375,10 @@ class NativeMapView { return nativeGetDebug(mNativeMapViewPtr); } + public void setReachability(boolean status) { + nativeSetReachability(mNativeMapViewPtr, status); + } + // // Callbacks // @@ -359,7 +399,7 @@ class NativeMapView { super.finalize(); } - private native long nativeCreate(String cachePath, String styleUrl, String apiKey); + private native long nativeCreate(String cachePath); private native void nativeDestroy(long nativeMapViewPtr); @@ -380,31 +420,47 @@ class NativeMapView { private native void nativeStop(long nativeMapViewPtr); + private native void nativeRun(long nativeMapViewPtr); + private native void nativeRerender(long nativeMapViewPtr); private native void nativeUpdate(long nativeMapViewPtr); private native void nativeCleanup(long nativeMapViewPtr); + private native void nativeTerminate(long nativeMapViewPtr); + + private native boolean nativeNeedsSwap(long nativeMapViewPtr); + + private native void nativeSwapped(long nativeMapViewPtr); + private native void nativeResize(long nativeMapViewPtr, int width, int height, float ratio); private native void nativeResize(long nativeMapViewPtr, int width, int height, float ratio, int fbWidth, int fbHeight); - private native void nativeSetAppliedClasses(long nativeMapViewPtr, - Set appliedClasses); + //private native void Set nativeGetActiveSources(long nativeMapViewPtr); - private native Set nativeGetAppliedClasses(long nativeMapViewPtr); + private native void nativeSetAppliedClasses(long nativeMapViewPtr, + List classes); + + private native List nativeGetAppliedClasses(long nativeMapViewPtr); private native void nativeSetDefaultTransitionDuration( long nativeMapViewPtr, long durationMilliseconds); + private native void nativeSetStyleURL(long nativeMapViewPtr, String url); + private native void nativeSetStyleJSON(long nativeMapViewPtr, - String newStyleJSON); + String newStyleJSON, String base); private native String nativeGetStyleJSON(long nativeMapViewPtr); + private native void nativeSetAccessToken(long nativeMapViewPtr, String accessToken); + + private native String nativeGetAccessToken(long nativeMapViewPtr); + private native void nativeCancelTransitions(long nativeMapViewPtr); private native void nativeMoveBy(long nativeMapViewPtr, double dx, @@ -473,4 +529,6 @@ class NativeMapView { private native void nativeToggleDebug(long nativeMapViewPtr); private native boolean nativeGetDebug(long nativeMapViewPtr); + + private native void nativeSetReachability(long nativeMapViewPtr, boolean status); } From 529af6de24aeb2885ecf03c5ed4eeb1919b84eb3 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 16:53:55 +1100 Subject: [PATCH 082/415] Fix travis --- scripts/travis_before_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 1c57a261f..36681b1bf 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -42,7 +42,7 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: installing SDK" #mapbox_time "installing SDK" \ sudo apt-get -y install lib32stdc++6 lib32z1 - echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21 + echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then From 78059715acc1fb37b266f4beb495b0e8325c0b6b Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 19:04:26 +1100 Subject: [PATCH 083/415] Tidy up config choosing --- android/cpp/NativeMapView.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 4765661b2..7fac3bd9c 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -385,11 +385,10 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], config_ok &= red == (use565 ? 5 : 8); config_ok &= green == (use565 ? 6 : 8); config_ok &= blue == (use565 ? 5 : 8); - //config_ok &= (alpha == 0) || (alpha == 8); // Can be either 0 for RGBX or 8 for RGBA but we don't care either way - config_ok &= alpha == 0; // TODO should prefer no alpha over alpha, and 565 over 8888? - //config_ok &= depth == 16; - //config_ok &= stencil == 8; - //config_ok &= sample_buffers == 0; + config_ok &= (alpha == 0) || (alpha == 8); // Can be either 0 for RGBX or 8 for RGBA but we don't care either way + config_ok &= depth == 16; + config_ok &= stencil == 8; + config_ok &= sample_buffers == 0; config_ok &= samples == 0; if (config_ok) { // Choose the last matching config, that way we get RGBX if possible (since it is sorted highest to lowest bits) From 3f4623b8b7ae753df3d4cc39548715b4d94de881 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 21:41:57 +1100 Subject: [PATCH 084/415] Enable debug layer Less verbose logcat spew Fix problem with checking for depth buffer size Disable make_inactive for testing --- android/cpp/NativeMapView.cpp | 15 ++++++++------- .../java/com/mapbox/mapboxgl/lib/MapView.java | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 7fac3bd9c..a3b8c75d3 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -386,7 +386,7 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], config_ok &= green == (use565 ? 6 : 8); config_ok &= blue == (use565 ? 5 : 8); config_ok &= (alpha == 0) || (alpha == 8); // Can be either 0 for RGBX or 8 for RGBA but we don't care either way - config_ok &= depth == 16; + config_ok &= (depth == 24) || (depth == 16); config_ok &= stencil == 8; config_ok &= sample_buffers == 0; config_ok &= samples == 0; @@ -444,12 +444,13 @@ void NativeMapView::stop() { LOG_VERBOSE("NativeMapView::stop"); if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) { + // FIXME make_inactive before this so no valid context map->stop(); } } void NativeMapView::notifyMapChange() { - LOG_DEBUG("NativeMapView::notify_map_change()"); + LOG_VERBOSE("NativeMapView::notify_map_change()"); LOG_ASSERT(vm != nullptr); LOG_ASSERT(obj != nullptr); @@ -515,13 +516,13 @@ void MBGLView::make_active() { } void MBGLView::make_inactive() { - LOG_VERBOSE("MBGLView::make_inactive"); - // FIXME: this gets called before swap? bug? + // FIXME for testing + /*LOG_VERBOSE("MBGLView::make_inactive"); if (!eglMakeCurrent(nativeView->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); - } + }*/ } void MBGLView::swap() { @@ -544,12 +545,12 @@ void MBGLView::swap() { } void MBGLView::notify() { - LOG_DEBUG("MBGLView::notify()"); + LOG_VERBOSE("MBGLView::notify()"); // noop } void MBGLView::notify_map_change(mbgl::MapChange /* change */, mbgl::timestamp /* delay */) { - LOG_DEBUG("MBGLView::notify_map_change()"); + LOG_VERBOSE("MBGLView::notify_map_change()"); nativeView->notifyMapChange(); } diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 6360bd164..85b4ef726 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -131,6 +131,7 @@ public class MapView extends SurfaceView { mNativeMapView = new NativeMapView(this, cachePath); mNativeMapView.setStyleURL(mStyleUrl); mNativeMapView.setAccessToken(mAccessToken); + mNativeMapView.setDebug(true); // Load the attributes TypedArray typedArray = context.obtainStyledAttributes(attrs, From 30a420d9a3e2bcef2402cba17d1e3028ce5b9d1d Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 8 Nov 2014 22:59:16 +1100 Subject: [PATCH 085/415] More tidying up --- android/cpp/NativeMapView.cpp | 24 +++++------------- .../java/com/mapbox/mapboxgl/lib/MapView.java | 25 +------------------ src/map/map.cpp | 9 +++++++ 3 files changed, 16 insertions(+), 42 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index a3b8c75d3..add3e3bdb 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -180,6 +180,8 @@ bool NativeMapView::initializeDisplay() { void NativeMapView::terminateDisplay() { LOG_VERBOSE("NativeMapView::terminateDisplay"); + map->terminate(); + if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { @@ -281,6 +283,8 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { void NativeMapView::destroySurface() { LOG_VERBOSE("NativeMapView::destroySurface"); + map->terminate(); + if (surface != EGL_NO_SURFACE) { if (!eglDestroySurface(display, surface)) { LOG_ERROR("eglDestroySurface() returned error %d", eglGetError()); @@ -444,7 +448,6 @@ void NativeMapView::stop() { LOG_VERBOSE("NativeMapView::stop"); if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) { - // FIXME make_inactive before this so no valid context map->stop(); } } @@ -501,47 +504,32 @@ void MBGLView::make_active() { nativeView->surface, nativeView->context)) { LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); } - - // TODO still can't fix the black screen problem :-( - EGLint width, height; - if (!(eglQuerySurface(nativeView->display, nativeView->surface, EGL_WIDTH, &width) - & eglQuerySurface(nativeView->display, nativeView->surface, EGL_HEIGHT, &height))) { - LOG_ERROR("eglQuerySurface() returned error %d", eglGetError()); - } - map->resize(width, height); - } else { LOG_DEBUG("Not activating as we are not ready"); } } void MBGLView::make_inactive() { - // FIXME for testing - /*LOG_VERBOSE("MBGLView::make_inactive"); + LOG_VERBOSE("MBGLView::make_inactive"); if (!eglMakeCurrent(nativeView->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); - }*/ + } } void MBGLView::swap() { LOG_VERBOSE("MBGLView::swap"); - // FIXME bug workaround - make_active(); if (map->needsSwap() && (nativeView->display != EGL_NO_DISPLAY) && (nativeView->surface != EGL_NO_SURFACE)) { if (!eglSwapBuffers(nativeView->display, nativeView->surface)) { LOG_ERROR("eglSwapBuffers() returned error %d", eglGetError()); - LOG_ERROR("%u", reinterpret_cast(nativeView->surface)); // TODO remove } map->swapped(); } else { LOG_DEBUG("Not swapping as we are not ready"); } - // FIXME bug workaround - make_inactive(); } void MBGLView::notify() { diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 85b4ef726..3c1910758 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -361,10 +361,6 @@ public class MapView extends SurfaceView { public void onCreate(Bundle savedInstanceState) { Log.v(TAG, "onCreate"); if (savedInstanceState != null) { - lonlat = (LonLat) savedInstanceState - .getParcelable(STATE_CENTER_COORDINATE); - bearing = savedInstanceState.getDouble(STATE_CENTER_DIRECTION); - zoom = savedInstanceState.getDouble(STATE_ZOOM_LEVEL); setCenterCoordinate((LonLat) savedInstanceState .getParcelable(STATE_CENTER_COORDINATE)); setDirection(savedInstanceState.getDouble(STATE_CENTER_DIRECTION)); @@ -418,28 +414,15 @@ public class MapView extends SurfaceView { // Must be called from Activity onPause public void onPause() { Log.v(TAG, "onPause"); - lonlat = mNativeMapView.getLonLat(); - bearing = mNativeMapView.getBearing(); - zoom = mNativeMapView.getZoom(); mNativeMapView.stop(); } // Called when we need to start the render thread // Must be called from Activity onResume - // TODO need to fix this in Map C++ code - // Seems map state gets reset when we start() - private LonLat lonlat; - private double bearing, zoom; - public void onResume() { Log.v(TAG, "onResume"); mNativeMapView.start(); - if (lonlat != null) { - mNativeMapView.setLonLat(lonlat); - mNativeMapView.setBearing(bearing); - mNativeMapView.setZoom(zoom); - } } // This class handles SurfaceHolder callbacks @@ -460,11 +443,6 @@ public class MapView extends SurfaceView { public void surfaceCreated(SurfaceHolder holder) { Log.v(TAG, "surfaceCreated"); mNativeMapView.createSurface(holder.getSurface()); - if (lonlat != null) { - mNativeMapView.setLonLat(lonlat); - mNativeMapView.setBearing(bearing); - mNativeMapView.setZoom(zoom); - } } // Called when the native surface buffer has been destroyed @@ -484,7 +462,6 @@ public class MapView extends SurfaceView { Log.v(TAG, "surfaceChanged"); Log.i(TAG, "resize " + format + " " + width + " " + height); mNativeMapView.resize(width, height); - // TODO fix bug when rotating - sometimes 1/2 map black } } @@ -515,7 +492,7 @@ public class MapView extends SurfaceView { // Draw events // - // TODO: onDraw for editor? + // TODO: onDraw for UI editor mockup? // By default it just shows a gray screen with "MapView" // Not too important but perhaps we could put a static demo map image there diff --git a/src/map/map.cpp b/src/map/map.cpp index d0a279b64..ab16eff13 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -215,7 +215,9 @@ void Map::cleanup(uv_async_t *async) { } void Map::terminate() { + view.make_active(); painter.terminate(); + view.make_inactive(); } void Map::setReachability(bool reachable) { @@ -239,7 +241,9 @@ void Map::render(uv_async_t *async) { if (map->is_clean.test_and_set() == false) { map->render(); map->is_swapped.clear(); + map->view.make_active(); map->view.swap(); + map->view.make_inactive(); } else { // We set the rendered flag in the test above, so we have to reset it // now that we're not actually rendering because the map is clean. @@ -254,6 +258,9 @@ void Map::terminate(uv_async_t *async) { Map *map = static_cast(async->data); assert(uv_thread_self() == map->map_thread); + // Makre sure we have a GL context + map->view.make_active(); + // Remove all of these to make sure they are destructed in the correct thread. map->glyphStore.reset(); map->fileSource.reset(); @@ -261,6 +268,8 @@ void Map::terminate(uv_async_t *async) { map->workers.reset(); map->activeSources.clear(); + map->view.make_inactive(); + uv_close((uv_handle_t *)map->async_cleanup.get(), nullptr); uv_close((uv_handle_t *)map->async_render.get(), nullptr); uv_close((uv_handle_t *)map->async_terminate.get(), nullptr); From 54d176fed0931f802bf5060a09beffeeb797384b Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 01:22:38 +1100 Subject: [PATCH 086/415] Implement GL extensions --- android/cpp/NativeMapView.cpp | 28 ++++++++++++++++++++++++++++ android/cpp/NativeMapView.hpp | 2 ++ 2 files changed, 30 insertions(+) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index add3e3bdb..a59c59857 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -432,6 +432,8 @@ void NativeMapView::start() { log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); log_gl_string(GL_EXTENSIONS, "Extensions"); + loadExtensions(); + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", @@ -444,6 +446,32 @@ void NativeMapView::start() { } } +void NativeMapView::loadExtensions() { + const GLubyte* str = glGetString(GL_EXTENSIONS); + if (str == nullptr) { + LOG_ERROR("glGetString(GL_EXTENSIONS) returned error %d", glGetError()); + return; + } + + std::string extensions(reinterpret_cast(str)); + + if (extensions.find("GL_OES_vertex_array_object") != std::string::npos) { + LOG_INFO("Using GL_OES_vertex_array_object."); + gl::BindVertexArray = (gl::PFNGLBINDVERTEXARRAYPROC)eglGetProcAddress("glBindVertexArrayOES"); + gl::DeleteVertexArrays = (gl::PFNGLDELETEVERTEXARRAYSPROC)eglGetProcAddress("glDeleteVertexArraysOES"); + gl::GenVertexArrays = (gl::PFNGLGENVERTEXARRAYSPROC)eglGetProcAddress("glGenVertexArraysOES"); + gl::IsVertexArray = (gl::PFNGLISVERTEXARRAYPROC)eglGetProcAddress("glIsVertexArrayOES"); + + if ((gl::BindVertexArray == nullptr) || (gl::DeleteVertexArrays == nullptr) || (gl::GenVertexArrays == nullptr) || (gl::IsVertexArray == nullptr)) { + LOG_ERROR("Could not load GL_OES_vertex_array_object functions."); + gl::BindVertexArray = nullptr; + gl::DeleteVertexArrays = nullptr; + gl::GenVertexArrays = nullptr; + gl::IsVertexArray = nullptr; + } + } +} + void NativeMapView::stop() { LOG_VERBOSE("NativeMapView::stop"); if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) diff --git a/android/cpp/NativeMapView.hpp b/android/cpp/NativeMapView.hpp index 866365c6d..aafd76189 100644 --- a/android/cpp/NativeMapView.hpp +++ b/android/cpp/NativeMapView.hpp @@ -68,6 +68,8 @@ private: EGLConfig chooseConfig(const EGLConfig configs[], EGLint num_configs, bool use565) const; + void loadExtensions(); + private: JavaVM* vm = nullptr; jobject obj = nullptr; From 811455d15a531a05d2b0d844865398dd2a86d167 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 08:50:43 +1100 Subject: [PATCH 087/415] Update build instructions --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ce05871b..3b66a7d6f 100644 --- a/README.md +++ b/README.md @@ -119,12 +119,13 @@ Install Android NDK chmod +x android-ndk-r10c-linux-x86_64.bin ./android-ndk-r10c-linux-x86_64.bin export ANDROID_NDK_PATH="/dir/to/android-ndk-r10c" + export NDK_PATH=$(ANDROID_NDK_PATH) Install Android SDK wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz tar -xzf ./android-sdk_r23.0.2-linux.tgz - ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21 + ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository export ANDROID_HOME="/dir/to/android-sdk-linux" Run From cc83df8bcdc072880a972461e36f21b8d0743d06 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 09:04:48 +1100 Subject: [PATCH 088/415] Try more Travis build types --- .travis.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6d6a70b55..2e9303787 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,15 +7,33 @@ matrix: - os: linux env: BUILDTYPE=Release JOBS=16 MASON_PLATFORM=android compiler: clang + - os: linux + env: BUILDTYPE=Debug JOBS=16 MASON_PLATFORM=android + compiler: clang + - os: linux + env: BUILDTYPE=Release JOBS=16 + compiler: clang - os: linux env: BUILDTYPE=Debug JOBS=16 compiler: clang - os: linux env: BUILDTYPE=Release JOBS=8 compiler: gcc + - os: linux + env: BUILDTYPE=Debug JOBS=8 + compiler: gcc + - os: osx + env: BUILDTYPE=Release JOBS=8 + compiler: clang - os: osx env: BUILDTYPE=Debug JOBS=8 compiler: clang + - os: osx + env: BUILDTYPE=Release JOBS=8 MASON_PLATFORM=android + compiler: clang + - os: osx + env: BUILDTYPE=Debug JOBS=8 MASON_PLATFORM=android + compiler: clang env: global: From 59d8cb279149b4bd4c7f3a441ffa00ab3fbdbf3b Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 09:06:58 +1100 Subject: [PATCH 089/415] Fix typo --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2e9303787..2b7f6ba5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,10 @@ matrix: - os: linux env: BUILDTYPE=Release JOBS=16 MASON_PLATFORM=android compiler: clang - - os: linux + - os: linux env: BUILDTYPE=Debug JOBS=16 MASON_PLATFORM=android compiler: clang - - os: linux + - os: linux env: BUILDTYPE=Release JOBS=16 compiler: clang - os: linux From 9f74c79c7dc103e6c718d56dcbb3da5516a67972 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 09:37:04 +1100 Subject: [PATCH 090/415] First attempt at osx android in travis --- .travis.yml | 2 +- scripts/travis_before_install.sh | 111 ++++++++++++++++++++++--------- 2 files changed, 81 insertions(+), 32 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2b7f6ba5f..935e2c495 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,7 +50,7 @@ before_install: - export ANDROID_NDK_PATH="$(pwd)/android-ndk-r10c" - export NDK_HOME="$(pwd)/android-ndk-r10c" - export JAVA_HOME="$(pwd)/jdk1.7.0_71" -- export ANDROID_HOME="$(pwd)/android-sdk-linux" +- export ANDROID_HOME="$(pwd)/android-sdk" - ./scripts/travis_before_install.sh - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then export LD_LIBRARY_PATH=`mason prefix mesa 10.3.1`/lib; fi - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then glxinfo; fi diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 36681b1bf..7404f5d1f 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -4,45 +4,94 @@ set -o pipefail if [[ "${MASON_PLATFORM}" == "android" ]]; then - echo "debug: installing 7z" - #mapbox_time "installing 7z" \ - MASON_PLATFORM= ./.mason/mason install 7z 9.20 - echo "debug: setting 7z path" - SEVEN_ZIP_PATH="$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin/7za" + if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then - echo "debug: fetching NDK" - #mapbox_time "fetching NDK" \ - wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin + echo "debug: installing 7z" + #mapbox_time "installing 7z" \ + MASON_PLATFORM= ./.mason/mason install 7z 9.20 - echo "debug: chmod NDK" - chmod a+x ./android-ndk-r10c-linux-x86_64.bin + echo "debug: setting 7z path" + SEVEN_ZIP_PATH="$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin/7za" - echo "debug: unpacking NDK" - #mapbox_time "unpacking NDK" \ - $SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > .tmp-ndk-log - rm .tmp-ndk-log + echo "debug: fetching NDK" + #mapbox_time "fetching NDK" \ + wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin - echo "debug: fetching JDK" - #mapbox_time "fetching JDK" \ - wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-linux-x64.tar.gz + echo "debug: chmod NDK" + chmod a+x ./android-ndk-r10c-linux-x86_64.bin - echo "debug: unpacking JDK" - #mapbox_time "unpacking JDK" \ - tar -xzf ./jdk-7u71-linux-x64.tar.gz + echo "debug: unpacking NDK" + #mapbox_time "unpacking NDK" \ + $SEVEN_ZIP_PATH x ./android-ndk-r10c-linux-x86_64.bin > .tmp-ndk-log + rm .tmp-ndk-log - echo "debug: fetching SDK" - #mapbox_time "fetching SDK" \ - wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz + echo "debug: fetching JDK" + #mapbox_time "fetching JDK" \ + wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-linux-x64.tar.gz - echo "debug: unpacking SDK" - #mapbox_time "unpacking SDK" \ - tar -xzf ./android-sdk_r23.0.2-linux.tgz + echo "debug: unpacking JDK" + #mapbox_time "unpacking JDK" \ + tar -xzf ./jdk-7u71-linux-x64.tar.gz + + echo "debug: fetching SDK" + #mapbox_time "fetching SDK" \ + wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz + + echo "debug: unpacking SDK" + #mapbox_time "unpacking SDK" \ + tar -xzf ./android-sdk_r23.0.2-linux.tgz + mv ./android-sdk-linux ./android-sdk + + echo "debug: installing SDK" + #mapbox_time "installing SDK" \ + sudo apt-get -y install lib32stdc++6 lib32z1 + echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository + +elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then + + echo "debug: installing 7z" + #mapbox_time "installing 7z" \ + MASON_PLATFORM= ./.mason/mason install 7z 9.20 + + echo "debug: setting 7z path" + SEVEN_ZIP_PATH="$(MASON_PLATFORM= ./.mason/mason prefix 7z 9.20)/bin/7za" + + echo "debug: fetching NDK" + #mapbox_time "fetching NDK" \ + wget http://dl.google.com/android/ndk/android-ndk-r10c-darwin-x86_64.bin + + echo "debug: chmod NDK" + chmod a+x ./android-ndk-r10c-darwin-x86_64.bin + + echo "debug: unpacking NDK" + #mapbox_time "unpacking NDK" \ + $SEVEN_ZIP_PATH x ./android-ndk-r10c-darwin-x86_64.bin > .tmp-ndk-log + rm .tmp-ndk-log + + echo "debug: fetching JDK" + #mapbox_time "fetching JDK" \ + wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-macosx-x64.dmg + + echo "debug: unpacking JDK" + #mapbox_time "unpacking JDK" \ + # TODO how to do this? + + echo "debug: fetching SDK" + #mapbox_time "fetching SDK" \ + wget http://dl.google.com/android/android-sdk_r23.0.2-macosx.zip + + echo "debug: unpacking SDK" + #mapbox_time "unpacking SDK" \ + unzip android-sdk_r23.0.2-macosx.zip + mv ./android-sdk-macosx ./android-sdk + + echo "debug: installing SDK" + #mapbox_time "installing SDK" \ + echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository + + fi - echo "debug: installing SDK" - #mapbox_time "installing SDK" \ - sudo apt-get -y install lib32stdc++6 lib32z1 - echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository fi if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then @@ -74,7 +123,7 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then llvm-3.4 # required for mesa echo "install_mesa" - MASON_PLATFORM= mason install mesa 10.3.1 + MASON_PLATFORM = mason install mesa 10.3.1 echo "install_awscli" sudo pip install awscli From 50431652c84372136517d567141de8165a006e4e Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 09:46:29 +1100 Subject: [PATCH 091/415] More tweaks to osx android on travis --- .travis.yml | 14 +++++++------- scripts/travis_before_install.sh | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 935e2c495..1ecf79fce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,18 +22,18 @@ matrix: - os: linux env: BUILDTYPE=Debug JOBS=8 compiler: gcc - - os: osx - env: BUILDTYPE=Release JOBS=8 - compiler: clang - - os: osx - env: BUILDTYPE=Debug JOBS=8 - compiler: clang - os: osx env: BUILDTYPE=Release JOBS=8 MASON_PLATFORM=android compiler: clang - os: osx env: BUILDTYPE=Debug JOBS=8 MASON_PLATFORM=android compiler: clang + - os: osx + env: BUILDTYPE=Release JOBS=8 + compiler: clang + - os: osx + env: BUILDTYPE=Debug JOBS=8 + compiler: clang env: global: @@ -49,7 +49,7 @@ before_install: - source ./scripts/flags.sh - export ANDROID_NDK_PATH="$(pwd)/android-ndk-r10c" - export NDK_HOME="$(pwd)/android-ndk-r10c" -- export JAVA_HOME="$(pwd)/jdk1.7.0_71" +- if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then export JAVA_HOME="$(pwd)/jdk1.7.0_71"; fi - export ANDROID_HOME="$(pwd)/android-sdk" - ./scripts/travis_before_install.sh - if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then export LD_LIBRARY_PATH=`mason prefix mesa 10.3.1`/lib; fi diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 7404f5d1f..a7e778f53 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -83,7 +83,7 @@ elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then echo "debug: unpacking SDK" #mapbox_time "unpacking SDK" \ - unzip android-sdk_r23.0.2-macosx.zip + unzip -qq android-sdk_r23.0.2-macosx.zip mv ./android-sdk-macosx ./android-sdk echo "debug: installing SDK" From 25251ee63494f5ebe19697d55189767e13938227 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 09:49:04 +1100 Subject: [PATCH 092/415] Fix sdk path on travis --- scripts/travis_before_install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index a7e778f53..05bd60aba 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -46,7 +46,7 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: installing SDK" #mapbox_time "installing SDK" \ sudo apt-get -y install lib32stdc++6 lib32z1 - echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository + echo y | ./android-sdk/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then @@ -88,7 +88,7 @@ elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then echo "debug: installing SDK" #mapbox_time "installing SDK" \ - echo y | ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository + echo y | ./android-sdk/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository fi From 4854527e31d81a9726307d841d0b17df5d0c3bd8 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 16:40:39 +1100 Subject: [PATCH 093/415] Work around emulator GL ES 2.0 bug --- android/cpp/NativeMapView.cpp | 63 ++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index a59c59857..ebed4c0d1 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -1,5 +1,8 @@ +#include #include +#include + #include #include @@ -108,19 +111,31 @@ bool NativeMapView::initializeDisplay() { log_egl_string(display, EGL_CLIENT_APIS, "Client APIs"); log_egl_string(display, EGL_EXTENSIONS, "Client Extensions"); + // Detect if we are in emulator + char prop[PROP_VALUE_MAX]; + __system_property_get("ro.kernel.qemu", prop); + bool in_emulator = strtol(prop, nullptr, 0) == 1; + LOG_INFO("ro.kernel.qemu = %s", prop); + if (in_emulator) { + LOG_INFO("In emulator! Enabling hacks :-("); + } + // Try 565 first (faster) bool use565 = true; EGLint config_attribs[] = { EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_CONFORMANT, EGL_OPENGL_ES2_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + //EGL_CONFORMANT, EGL_OPENGL_ES2_BIT, // Emulator does not support this, in here, but will return conformant anyway, what? EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, + //EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, // Emulator does not support this, what? EGL_BUFFER_SIZE, 16, EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6, EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, + (in_emulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT, // Ugly hack + (in_emulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER, // Ugly hack EGL_NONE }; EGLint num_configs; @@ -134,10 +149,16 @@ bool NativeMapView::initializeDisplay() { // Now try 8888 use565 = false; - config_attribs[9] = 32; // EGL_BUFFER_SIZE // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode) - config_attribs[11] = 8; // EGL_RED_SIZE - config_attribs[13] = 8; // EGL_GREEN_SIZE - config_attribs[15] = 8; // EGL_BLUE_SIZE + // TODO removed these due to different indices + //config_attribs[9] = 32; // EGL_BUFFER_SIZE // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode) + //config_attribs[11] = 8; // EGL_RED_SIZE + //config_attribs[13] = 8; // EGL_GREEN_SIZE + //config_attribs[15] = 8; // EGL_BLUE_SIZE + + config_attribs[5] = 32; // EGL_BUFFER_SIZE // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode) + config_attribs[7] = 8; // EGL_RED_SIZE + config_attribs[9] = 8; // EGL_GREEN_SIZE + config_attribs[11] = 8; // EGL_BLUE_SIZE if (!eglChooseConfig(display, config_attribs, nullptr, 0, &num_configs)) { LOG_ERROR("eglChooseConfig(NULL) returned error %d", eglGetError()); terminateDisplay(); @@ -304,12 +325,26 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], LOG_INFO("Found %d configs", num_configs); int chosen_config = -1; + bool is_caveat = false; + bool is_conformant = false; for (int i = 0; i < num_configs; i++) { LOG_INFO("Config %d:", i); - EGLint bits, red, green, blue, alpha, alpha_mask, depth, stencil, + EGLint caveat, conformant, bits, red, green, blue, alpha, alpha_mask, depth, stencil, sample_buffers, samples; + if (!eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)) { + LOG_ERROR("eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d", + eglGetError()); + return nullptr; + } + + if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) { + LOG_ERROR("eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", + eglGetError()); + return nullptr; + } + if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) { LOG_ERROR("eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d", eglGetError()); @@ -372,7 +407,9 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], eglGetError()); return nullptr; } - + + LOG_INFO("Caveat: %d", caveat); + LOG_INFO("Conformant: %d", conformant); LOG_INFO("Color: %d", bits); LOG_INFO("Red: %d", red); LOG_INFO("Green: %d", green); @@ -397,6 +434,8 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], if (config_ok) { // Choose the last matching config, that way we get RGBX if possible (since it is sorted highest to lowest bits) chosen_config = i; + is_conformant = (conformant & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT; + is_caveat = caveat != EGL_NONE; } } @@ -406,6 +445,12 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], } else { return nullptr; } + if (!is_caveat) { + LOG_WARN("Chosen config has a caveat."); + } + if (!is_conformant) { + LOG_WARN("Chosen config is not conformant."); + } } void NativeMapView::start() { @@ -429,7 +474,7 @@ void NativeMapView::start() { log_gl_string(GL_VENDOR, "Vendor"); log_gl_string(GL_RENDERER, "Renderer"); log_gl_string(GL_VERSION, "Version"); - log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); + log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); // In the emulator this returns NULL with error code 0? https://code.google.com/p/android/issues/detail?id=78977 log_gl_string(GL_EXTENSIONS, "Extensions"); loadExtensions(); From 9cb874c0f2828ebfcc3e3f3823db820313de5571 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 17:19:11 +1100 Subject: [PATCH 094/415] Reduce log noise --- android/cpp/NativeMapView.cpp | 2 +- android/cpp/log.h | 11 +++++++++-- .../main/java/com/mapbox/mapboxgl/lib/MapView.java | 5 ++--- .../java/com/mapbox/mapboxgl/lib/NativeMapView.java | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index ebed4c0d1..394e01bdb 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -537,6 +537,7 @@ void NativeMapView::notifyMapChange() { NULL }; + // TODO do this only once per thread? jint ret; JNIEnv* env = nullptr; bool detach = false; @@ -553,7 +554,6 @@ void NativeMapView::notifyMapChange() { } } - env->CallVoidMethod(obj, on_map_changed_id); if (env->ExceptionCheck()) { env->ExceptionDescribe(); diff --git a/android/cpp/log.h b/android/cpp/log.h index aa1950843..3cf203853 100644 --- a/android/cpp/log.h +++ b/android/cpp/log.h @@ -7,12 +7,19 @@ #define S_(x) S(x) #define S__LINE__ S_(__LINE__) +#ifdef DEBUG #define LOG_VERBOSE(fmt, args...) __android_log_print(ANDROID_LOG_VERBOSE, __FILE__ ":" S__LINE__, fmt, ##args) #define LOG_DEBUG(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__ ":" S__LINE__, fmt, ##args) +#define LOG_ASSERT(cond, ...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, NULL)) +#define LOG_ASSERTMSG(cond, fmt, args...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, fmt, ##args)) +#else +#define LOG_VERBOSE(fmt, args...) +#define LOG_DEBUG(fmt, args...) +#define LOG_ASSERT(cond, ...) +#define LOG_ASSERTMSG(cond, fmt, args...) +#endif #define LOG_INFO(fmt, args...) __android_log_print(ANDROID_LOG_INFO, __FILE__ ":" S__LINE__, fmt, ##args) #define LOG_WARN(fmt, args...) __android_log_print(ANDROID_LOG_WARN, __FILE__ ":" S__LINE__, fmt, ##args) #define LOG_ERROR(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, __FILE__ ":" S__LINE__, fmt, ##args) -#define LOG_ASSERT(cond, ...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, NULL)) -#define LOG_ASSERTMSG(cond, fmt, args...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, fmt, ##args)) #endif // LOG_H diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 3c1910758..e9e815ded 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -29,7 +29,6 @@ import com.arieslabs.assetbridge.Assetbridge; // Custom view that shows a Map // Based on SurfaceView as we use OpenGL ES to render public class MapView extends SurfaceView { - // TODO try TextureView // // Static members @@ -112,7 +111,7 @@ public class MapView extends SurfaceView { // Check if we are in Eclipse UI editor if (isInEditMode()) { - // TODO editor does not load properly because + // TODO editor does not load properly because we dont implement this return; } @@ -1142,7 +1141,7 @@ public class MapView extends SurfaceView { // Called via JNI from NativeMapView // Need to update anything that relies on map state protected void onMapChanged() { - Log.v(TAG, "onMapChanged"); + //Log.v(TAG, "onMapChanged"); if (mOnMapChangedListener != null) { mOnMapChangedListener.onMapChanged(); } diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index 56578eeb2..01ee430df 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -384,7 +384,7 @@ class NativeMapView { // protected void onMapChanged() { - Log.v(TAG, "onMapChanged"); + //Log.v(TAG, "onMapChanged"); mMapView.onMapChanged(); } From b3e3c21129671884e130d34507057676484656b4 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 17:36:27 +1100 Subject: [PATCH 095/415] Reduce calls to context swapping --- src/map/map.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/map/map.cpp b/src/map/map.cpp index ab16eff13..b720f663e 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -215,9 +215,7 @@ void Map::cleanup(uv_async_t *async) { } void Map::terminate() { - view.make_active(); painter.terminate(); - view.make_inactive(); } void Map::setReachability(bool reachable) { @@ -241,9 +239,7 @@ void Map::render(uv_async_t *async) { if (map->is_clean.test_and_set() == false) { map->render(); map->is_swapped.clear(); - map->view.make_active(); map->view.swap(); - map->view.make_inactive(); } else { // We set the rendered flag in the test above, so we have to reset it // now that we're not actually rendering because the map is clean. @@ -258,9 +254,6 @@ void Map::terminate(uv_async_t *async) { Map *map = static_cast(async->data); assert(uv_thread_self() == map->map_thread); - // Makre sure we have a GL context - map->view.make_active(); - // Remove all of these to make sure they are destructed in the correct thread. map->glyphStore.reset(); map->fileSource.reset(); @@ -281,7 +274,6 @@ void Map::setup() { assert(uv_thread_self() == map_thread); view.make_active(); painter.setup(); - view.make_inactive(); } void Map::setStyleURL(const std::string &url) { @@ -649,14 +641,10 @@ void Map::prepare() { } void Map::render() { - view.make_active(); - painter.render(*style, activeSources, state, animationTime); // Schedule another rerender when we definitely need a next frame. if (transform.needsTransition() || style->hasTransitions()) { update(); } - - view.make_inactive(); } From 28d66bb6f856b3836616f9574c38dbb9fe749d92 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 22:36:37 +1100 Subject: [PATCH 096/415] Add Map::pause/resume Fixed bug after switching apps --- android/cpp/JNI.cpp | 18 +++- android/cpp/NativeMapView.cpp | 87 ++++++++++-------- android/cpp/NativeMapView.hpp | 3 + .../java/com/mapbox/mapboxgl/lib/MapView.java | 13 ++- .../mapbox/mapboxgl/lib/NativeMapView.java | 12 +++ include/mbgl/map/map.hpp | 15 ++++ include/mbgl/util/uv.hpp | 2 + include/mbgl/util/uv_detail.hpp | 18 ++++ src/map/map.cpp | 89 ++++++++++++++++++- 9 files changed, 214 insertions(+), 43 deletions(-) diff --git a/android/cpp/JNI.cpp b/android/cpp/JNI.cpp index e1731aa99..3b1801c4c 100644 --- a/android/cpp/JNI.cpp +++ b/android/cpp/JNI.cpp @@ -221,6 +221,20 @@ void JNICALL nativeStop(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { native_map_view->stop(); } +void JNICALL nativePause(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + LOG_VERBOSE("nativePause"); + LOG_ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->pause(); +} + +void JNICALL nativeResume(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { + LOG_VERBOSE("nativeResume"); + LOG_ASSERT(native_map_view_ptr != 0); + NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); + native_map_view->resume(); +} + void JNICALL nativeRun(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeRun"); LOG_ASSERT(native_map_view_ptr != 0); @@ -739,7 +753,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { } // NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the array correctly (too large) - std::array methods = {{ // Can remove the extra brace in C++14 + std::array methods = {{ // Can remove the extra brace in C++14 { "nativeCreate", "(Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, { "nativeDestroy", "(J)V", reinterpret_cast(&nativeDestroy) }, { "nativeInitializeDisplay", "(J)V", reinterpret_cast(&nativeInitializeDisplay) }, @@ -750,6 +764,8 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { { "nativeDestroySurface", "(J)V", reinterpret_cast(&nativeDestroySurface) }, { "nativeStart", "(J)V", reinterpret_cast(&nativeStart) }, { "nativeStop", "(J)V", reinterpret_cast(&nativeStop) }, + { "nativePause", "(J)V", reinterpret_cast(&nativePause) }, + { "nativeResume", "(J)V", reinterpret_cast(&nativeResume) }, { "nativeRun", "(J)V", reinterpret_cast(&nativeRun) }, { "nativeRerender", "(J)V", reinterpret_cast(&nativeRerender) }, { "nativeUpdate", "(J)V", reinterpret_cast(&nativeUpdate) }, diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 394e01bdb..6761f3ede 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -149,12 +149,6 @@ bool NativeMapView::initializeDisplay() { // Now try 8888 use565 = false; - // TODO removed these due to different indices - //config_attribs[9] = 32; // EGL_BUFFER_SIZE // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode) - //config_attribs[11] = 8; // EGL_RED_SIZE - //config_attribs[13] = 8; // EGL_GREEN_SIZE - //config_attribs[15] = 8; // EGL_BLUE_SIZE - config_attribs[5] = 32; // EGL_BUFFER_SIZE // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode) config_attribs[7] = 8; // EGL_RED_SIZE config_attribs[9] = 8; // EGL_GREEN_SIZE @@ -296,7 +290,25 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { return false; } - start(); + if (!eglMakeCurrent(display, surface, surface, context)) { + LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); + } + + log_gl_string(GL_VENDOR, "Vendor"); + log_gl_string(GL_RENDERER, "Renderer"); + log_gl_string(GL_VERSION, "Version"); + log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); // In the emulator this returns NULL with error code 0? https://code.google.com/p/android/issues/detail?id=78977 + log_gl_string(GL_EXTENSIONS, "Extensions"); + + loadExtensions(); + + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } + + resume(); return true; } @@ -304,7 +316,7 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { void NativeMapView::destroySurface() { LOG_VERBOSE("NativeMapView::destroySurface"); - map->terminate(); + pause(); if (surface != EGL_NO_SURFACE) { if (!eglDestroySurface(display, surface)) { @@ -464,31 +476,10 @@ void NativeMapView::start() { initializeContext(); } - if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) - && (context != EGL_NO_CONTEXT)) { - - if (!eglMakeCurrent(display, surface, surface, context)) { - LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); - } - - log_gl_string(GL_VENDOR, "Vendor"); - log_gl_string(GL_RENDERER, "Renderer"); - log_gl_string(GL_VERSION, "Version"); - log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); // In the emulator this returns NULL with error code 0? https://code.google.com/p/android/issues/detail?id=78977 - log_gl_string(GL_EXTENSIONS, "Extensions"); - - loadExtensions(); - - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT)) { - LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", - eglGetError()); - } - - map->start(); - } else { - LOG_DEBUG("Not starting because we are not ready"); - } + LOG_ASSERT(display != EGL_NO_DISPLAY); + LOG_ASSERT(context != EGL_NO_CONTEXT); + + map->start(); } void NativeMapView::loadExtensions() { @@ -519,9 +510,33 @@ void NativeMapView::loadExtensions() { void NativeMapView::stop() { LOG_VERBOSE("NativeMapView::stop"); - if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) - && (context != EGL_NO_CONTEXT)) { - map->stop(); + + LOG_ASSERT(display != EGL_NO_DISPLAY); + LOG_ASSERT(context != EGL_NO_CONTEXT); + + map->stop(); +} + + +void NativeMapView::pause() { + LOG_VERBOSE("NativeMapView::pause"); + + LOG_ASSERT(display != EGL_NO_DISPLAY); + LOG_ASSERT(context != EGL_NO_CONTEXT); + + map->pause(); +} + +void NativeMapView::resume() { + LOG_VERBOSE("NativeMapView::resume"); + + LOG_ASSERT(display != EGL_NO_DISPLAY); + LOG_ASSERT(context != EGL_NO_CONTEXT); + + if (surface != EGL_NO_SURFACE) { + map->resume(); + } else { + LOG_DEBUG("Not resuming because we are not ready"); } } diff --git a/android/cpp/NativeMapView.hpp b/android/cpp/NativeMapView.hpp index aafd76189..05aa11b88 100644 --- a/android/cpp/NativeMapView.hpp +++ b/android/cpp/NativeMapView.hpp @@ -62,6 +62,9 @@ public: void start(); void stop(); + void resume(); + void pause(); + void notifyMapChange(); private: diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index e9e815ded..2b150e8ba 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -373,6 +373,7 @@ public class MapView extends SurfaceView { } mNativeMapView.initializeDisplay(); + mNativeMapView.initializeContext(); } // Called when we need to save instance state @@ -392,6 +393,7 @@ public class MapView extends SurfaceView { // Must be called from Activity onDestroy public void onDestroy() { Log.v(TAG, "onDestroy"); + mNativeMapView.terminateContext(); mNativeMapView.terminateDisplay(); } @@ -399,21 +401,22 @@ public class MapView extends SurfaceView { // Must be called from Activity onStart public void onStart() { Log.v(TAG, "onStart"); - mNativeMapView.initializeContext(); + mNativeMapView.pause(); + mNativeMapView.start(); } // Called when we need to terminate the GL context // Must be called from Activity onPause public void onStop() { Log.v(TAG, "onStop"); - mNativeMapView.terminateContext(); + mNativeMapView.stop(); } // Called when we need to stop the render thread // Must be called from Activity onPause public void onPause() { Log.v(TAG, "onPause"); - mNativeMapView.stop(); + mNativeMapView.pause(); } // Called when we need to start the render thread @@ -421,7 +424,7 @@ public class MapView extends SurfaceView { public void onResume() { Log.v(TAG, "onResume"); - mNativeMapView.start(); + mNativeMapView.resume(); } // This class handles SurfaceHolder callbacks @@ -464,6 +467,8 @@ public class MapView extends SurfaceView { } } + // TODO examine how GLSurvaceView hadles attach/detach from window + // Called when view is no longer connected @Override protected void onDetachedFromWindow() { diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index 01ee430df..974c408b6 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -80,6 +80,14 @@ class NativeMapView { nativeStop(mNativeMapViewPtr); } + public void pause() { + nativePause(mNativeMapViewPtr); + } + + public void resume() { + nativeResume(mNativeMapViewPtr); + } + public void run() { nativeRun(mNativeMapViewPtr); } @@ -420,6 +428,10 @@ class NativeMapView { private native void nativeStop(long nativeMapViewPtr); + private native void nativePause(long nativeMapViewPtr); + + private native void nativeResume(long nativeMapViewPtr); + private native void nativeRun(long nativeMapViewPtr); private native void nativeRerender(long nativeMapViewPtr); diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 43402fe2e..f2f10665e 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -45,6 +45,12 @@ public: // function. void stop(stop_callback cb = nullptr, void *data = nullptr); + // Pauses the render thread. The render thread will stop running but will not be terminated and will not lose state until resumed. + void pause(); + + // Resumes a paused render thread + void resume(); + // Runs the map event loop. ONLY run this function when you want to get render a single frame // with this map object. It will *not* spawn a separate thread and instead block until the // frame is completely rendered. @@ -145,6 +151,9 @@ private: static void terminate(uv_async_t *async); static void cleanup(uv_async_t *async); + // Checks if render thread needs to pause + void check_for_pause(); + // Setup void setup(); @@ -167,6 +176,12 @@ private: std::unique_ptr async_render; std::unique_ptr async_cleanup; +private: + bool terminating = false; + bool is_paused = false; + std::unique_ptr mutex_pause; + std::unique_ptr cond_resume; + private: // If cleared, the next time the render thread attempts to render the map, it will *actually* // render the map. diff --git a/include/mbgl/util/uv.hpp b/include/mbgl/util/uv.hpp index 3c533cfbf..0e828bb4f 100644 --- a/include/mbgl/util/uv.hpp +++ b/include/mbgl/util/uv.hpp @@ -21,6 +21,8 @@ class thread; class rwlock; class loop; class worker; +class mutex; +class cond; } diff --git a/include/mbgl/util/uv_detail.hpp b/include/mbgl/util/uv_detail.hpp index 3bbfa4ac5..112339c1b 100644 --- a/include/mbgl/util/uv_detail.hpp +++ b/include/mbgl/util/uv_detail.hpp @@ -52,6 +52,7 @@ private: }; class mutex { + friend class cond; public: inline mutex() { if (uv_mutex_init(&mtx) != 0) { @@ -66,6 +67,23 @@ private: uv_mutex_t mtx; }; +class cond { +public: + inline cond() { + if (uv_cond_init(&cnd) != 0) { + throw std::runtime_error("failed to initialize condition variable"); + } + } + inline ~cond() { uv_cond_destroy(&cnd); } + inline void signal() { uv_cond_signal(&cnd); } + inline void broadcast() { uv_cond_broadcast(&cnd); } + inline void wait(mutex &mtx) { uv_cond_wait(&cnd, &mtx.mtx); } + inline void timedwait(mutex &mtx, uint64_t timeout) { uv_cond_timedwait(&cnd, &mtx.mtx, timeout); } + +private: + uv_cond_t cnd; +}; + class lock { public: lock(mutex &mtx_) : mtx(mtx_) { mtx.lock(); } diff --git a/src/map/map.cpp b/src/map/map.cpp index b720f663e..ed105375f 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -36,6 +36,8 @@ Map::Map(View& view_) async_terminate(new uv_async_t()), async_render(new uv_async_t()), async_cleanup(new uv_async_t()), + mutex_pause(std::make_unique()), + cond_resume(std::make_unique()), view(view_), #ifndef NDEBUG main_thread(uv_thread_self()), @@ -76,7 +78,7 @@ uv::worker &Map::getWorker() { return *workers; } -void Map::start() { +void Map::start() { // TODO add pause arg assert(uv_thread_self() == main_thread); assert(!async); @@ -136,6 +138,8 @@ void Map::stop(stop_callback cb, void *data) { uv_async_send(async_terminate.get()); + resume(); + if (cb) { // Wait until the render thread stopped. We are using this construct instead of plainly // relying on the thread_join because the system might need to run things in the current @@ -155,6 +159,45 @@ void Map::stop(stop_callback cb, void *data) { async = false; } +void Map::pause() { + assert(uv_thread_self() == main_thread); + Log::Info(Event::General, "Map::pause() start"); + + // TODO wait until paused before return + + + Log::Info(Event::General, "Map::pause() locking"); + mutex_pause->lock(); + Log::Info(Event::General, "Map::pause() locked"); + is_paused = true; + Log::Info(Event::General, "Map::pause() set is_paused = true"); + Log::Info(Event::General, "Map::pause() unlocking"); + mutex_pause->unlock(); + Log::Info(Event::General, "Map::pause() unlocked"); + + Log::Info(Event::General, "Map::pause() stopping loop"); + uv_stop(**loop); + Log::Info(Event::General, "Map::pause() stopping done"); +} + +void Map::resume() { + assert(uv_thread_self() == main_thread); + Log::Info(Event::General, "Map::resume() start"); + + Log::Info(Event::General, "Map::resume() locking"); + mutex_pause->lock(); + Log::Info(Event::General, "Map::resume() locked"); + is_paused = false; + Log::Info(Event::General, "Map::resume() set is_paused = false"); + Log::Info(Event::General, "Map::resume() broadcasting"); + cond_resume->broadcast(); + Log::Info(Event::General, "Map::resume() broadcasted"); + Log::Info(Event::General, "Map::resume() unlocking"); + mutex_pause->unlock(); + Log::Info(Event::General, "Map::resume() unlocked"); + Log::Info(Event::General, "Map::resume() done"); +} + void Map::run() { #ifndef NDEBUG if (!async) { @@ -163,9 +206,28 @@ void Map::run() { #endif assert(uv_thread_self() == map_thread); + check_for_pause(); + setup(); prepare(); - uv_run(**loop, UV_RUN_DEFAULT); + + Log::Info(Event::General, "Map::run() start"); + terminating = false; + Log::Info(Event::General, "Map::run() set terminating = false"); + + Log::Info(Event::General, "Map::run() entering run loop"); + while(!terminating) { + Log::Info(Event::General, "Map::run() next run loop, terminating = %s", terminating ? "true" : "false"); + Log::Info(Event::General, "Map::run() uv_run enter"); + uv_run(**loop, UV_RUN_DEFAULT); + Log::Info(Event::General, "Map::run() exit"); + + check_for_pause(); + + //uv_run(**loop, UV_RUN_DEFAULT); + } + + Log::Info(Event::General, "Map::run() run loop exit"); // Run the event loop once more to make sure our async delete handlers are called. uv_run(**loop, UV_RUN_ONCE); @@ -180,6 +242,23 @@ void Map::run() { } } +void Map::check_for_pause() { + Log::Info(Event::General, "Map::check_for_pause() locking"); + mutex_pause->lock(); + Log::Info(Event::General, "Map::check_for_pause() locked"); + Log::Info(Event::General, "Map::check_for_pause() entering pause loop"); + while (is_paused) { + Log::Info(Event::General, "Map::check_for_pause() next pause loop, is_paused = %s", is_paused ? "true" : "false"); + Log::Info(Event::General, "Map::check_for_pause() waiting"); + cond_resume->wait(*mutex_pause); + Log::Info(Event::General, "Map::check_for_pause() waited"); + } + Log::Info(Event::General, "Map::check_for_pause() exit pause loop"); + Log::Info(Event::General, "Map::check_for_pause() unlocking"); + mutex_pause->unlock(); + Log::Info(Event::General, "Map::check_for_pause() unlocked"); +} + void Map::rerender() { // We only send render events if we want to continuously update the map // (== async rendering). @@ -250,6 +329,7 @@ void Map::render(uv_async_t *async) { } void Map::terminate(uv_async_t *async) { + Log::Info(Event::General, "Map::terminate() start"); // Closes all open handles on the loop. This means that the loop will automatically terminate. Map *map = static_cast(async->data); assert(uv_thread_self() == map->map_thread); @@ -263,9 +343,14 @@ void Map::terminate(uv_async_t *async) { map->view.make_inactive(); + map->terminating = true; + Log::Info(Event::General, "Map::terminate() set terminating = true"); + uv_close((uv_handle_t *)map->async_cleanup.get(), nullptr); uv_close((uv_handle_t *)map->async_render.get(), nullptr); uv_close((uv_handle_t *)map->async_terminate.get(), nullptr); + + Log::Info(Event::General, "Map::terminate() done"); } #pragma mark - Setup From 3696e051935648c38a003291bd1e6bea202a2e3d Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 22:54:05 +1100 Subject: [PATCH 097/415] Add async asserts --- android/cpp/NativeMapView.cpp | 2 +- .../main/java/com/mapbox/mapboxgl/lib/MapView.java | 1 - include/mbgl/map/map.hpp | 4 ++-- src/map/map.cpp | 11 +++++++++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 6761f3ede..d046642dd 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -479,7 +479,7 @@ void NativeMapView::start() { LOG_ASSERT(display != EGL_NO_DISPLAY); LOG_ASSERT(context != EGL_NO_CONTEXT); - map->start(); + map->start(true); } void NativeMapView::loadExtensions() { diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 2b150e8ba..d58bfa036 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -401,7 +401,6 @@ public class MapView extends SurfaceView { // Must be called from Activity onStart public void onStart() { Log.v(TAG, "onStart"); - mNativeMapView.pause(); mNativeMapView.start(); } diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index f2f10665e..6dbcd3550 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -36,7 +36,7 @@ public: ~Map(); // Start the map render thread. It is asynchronous. - void start(); + void start(bool start_paused = false); // Stop the map render thread. This call will block until the map rendering thread stopped. // The optional callback function will be invoked repeatedly until the map thread is stopped. @@ -46,7 +46,7 @@ public: void stop(stop_callback cb = nullptr, void *data = nullptr); // Pauses the render thread. The render thread will stop running but will not be terminated and will not lose state until resumed. - void pause(); + void pause(bool wait_for_pause = false); // Resumes a paused render thread void resume(); diff --git a/src/map/map.cpp b/src/map/map.cpp index ed105375f..2de77cd3c 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -78,7 +78,7 @@ uv::worker &Map::getWorker() { return *workers; } -void Map::start() { // TODO add pause arg +void Map::start(bool start_paused) { assert(uv_thread_self() == main_thread); assert(!async); @@ -89,6 +89,11 @@ void Map::start() { // TODO add pause arg // Reset the flag. is_stopped = false; + // Do we need to pause first? + if (start_paused) { + pause(); + } + // Setup async notifications // Iron out the differences between libuv 0.10 and 0.11 @@ -159,8 +164,9 @@ void Map::stop(stop_callback cb, void *data) { async = false; } -void Map::pause() { +void Map::pause(bool wait_for_pause) { assert(uv_thread_self() == main_thread); + assert(async); Log::Info(Event::General, "Map::pause() start"); // TODO wait until paused before return @@ -182,6 +188,7 @@ void Map::pause() { void Map::resume() { assert(uv_thread_self() == main_thread); + assert(async); Log::Info(Event::General, "Map::resume() start"); Log::Info(Event::General, "Map::resume() locking"); From 1e55c332cda0a2baab2b6dc06286725a86c8e71e Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 23:52:34 +1100 Subject: [PATCH 098/415] Wait for pause Tidy up Remove red fill --- android/cpp/NativeMapView.cpp | 8 +-- android/cpp/NativeMapView.hpp | 2 +- include/mbgl/map/map.hpp | 5 +- src/map/map.cpp | 97 ++++++++++++++--------------------- 4 files changed, 47 insertions(+), 65 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index d046642dd..40bf35a38 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -316,7 +316,7 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { void NativeMapView::destroySurface() { LOG_VERBOSE("NativeMapView::destroySurface"); - pause(); + pause(true); if (surface != EGL_NO_SURFACE) { if (!eglDestroySurface(display, surface)) { @@ -518,13 +518,13 @@ void NativeMapView::stop() { } -void NativeMapView::pause() { - LOG_VERBOSE("NativeMapView::pause"); +void NativeMapView::pause(bool wait_for_pause) { + LOG_VERBOSE("NativeMapView::pause %s", (wait_for_pause) ? "true" : "false"); LOG_ASSERT(display != EGL_NO_DISPLAY); LOG_ASSERT(context != EGL_NO_CONTEXT); - map->pause(); + map->pause(wait_for_pause); } void NativeMapView::resume() { diff --git a/android/cpp/NativeMapView.hpp b/android/cpp/NativeMapView.hpp index 05aa11b88..7a7ad1817 100644 --- a/android/cpp/NativeMapView.hpp +++ b/android/cpp/NativeMapView.hpp @@ -63,7 +63,7 @@ public: void stop(); void resume(); - void pause(); + void pause(bool wait_for_pause = false); void notifyMapChange(); diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 6dbcd3550..135811daa 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -178,9 +178,12 @@ private: private: bool terminating = false; + bool pausing = false; bool is_paused = false; + std::unique_ptr mutex_run; + std::unique_ptr cond_run; std::unique_ptr mutex_pause; - std::unique_ptr cond_resume; + std::unique_ptr cond_pause; private: // If cleared, the next time the render thread attempts to render the map, it will *actually* diff --git a/src/map/map.cpp b/src/map/map.cpp index 2de77cd3c..ba298c00e 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -36,8 +36,10 @@ Map::Map(View& view_) async_terminate(new uv_async_t()), async_render(new uv_async_t()), async_cleanup(new uv_async_t()), + mutex_run(std::make_unique()), + cond_run(std::make_unique()), mutex_pause(std::make_unique()), - cond_resume(std::make_unique()), + cond_pause(std::make_unique()), view(view_), #ifndef NDEBUG main_thread(uv_thread_self()), @@ -89,11 +91,6 @@ void Map::start(bool start_paused) { // Reset the flag. is_stopped = false; - // Do we need to pause first? - if (start_paused) { - pause(); - } - // Setup async notifications // Iron out the differences between libuv 0.10 and 0.11 @@ -117,6 +114,11 @@ void Map::start(bool start_paused) { #undef UV_ASYNC_CALLBACK + // Do we need to pause first? + if (start_paused) { + pause(); + } + uv_thread_create(*thread, [](void *arg) { Map *map = static_cast(arg); #ifndef NDEBUG @@ -167,42 +169,30 @@ void Map::stop(stop_callback cb, void *data) { void Map::pause(bool wait_for_pause) { assert(uv_thread_self() == main_thread); assert(async); - Log::Info(Event::General, "Map::pause() start"); + mutex_run->lock(); + pausing = true; + mutex_run->unlock(); - // TODO wait until paused before return - - - Log::Info(Event::General, "Map::pause() locking"); - mutex_pause->lock(); - Log::Info(Event::General, "Map::pause() locked"); - is_paused = true; - Log::Info(Event::General, "Map::pause() set is_paused = true"); - Log::Info(Event::General, "Map::pause() unlocking"); - mutex_pause->unlock(); - Log::Info(Event::General, "Map::pause() unlocked"); - - Log::Info(Event::General, "Map::pause() stopping loop"); uv_stop(**loop); - Log::Info(Event::General, "Map::pause() stopping done"); + rerender(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause + + if (wait_for_pause) { + mutex_pause->lock(); + while (!is_paused) { + cond_pause->wait(*mutex_pause); + } + mutex_pause->unlock(); + } } void Map::resume() { assert(uv_thread_self() == main_thread); assert(async); - Log::Info(Event::General, "Map::resume() start"); - - Log::Info(Event::General, "Map::resume() locking"); - mutex_pause->lock(); - Log::Info(Event::General, "Map::resume() locked"); - is_paused = false; - Log::Info(Event::General, "Map::resume() set is_paused = false"); - Log::Info(Event::General, "Map::resume() broadcasting"); - cond_resume->broadcast(); - Log::Info(Event::General, "Map::resume() broadcasted"); - Log::Info(Event::General, "Map::resume() unlocking"); - mutex_pause->unlock(); - Log::Info(Event::General, "Map::resume() unlocked"); - Log::Info(Event::General, "Map::resume() done"); + + mutex_run->lock(); + pausing = false; + cond_run->broadcast(); + mutex_run->unlock(); } void Map::run() { @@ -218,24 +208,12 @@ void Map::run() { setup(); prepare(); - Log::Info(Event::General, "Map::run() start"); terminating = false; - Log::Info(Event::General, "Map::run() set terminating = false"); - - Log::Info(Event::General, "Map::run() entering run loop"); while(!terminating) { - Log::Info(Event::General, "Map::run() next run loop, terminating = %s", terminating ? "true" : "false"); - Log::Info(Event::General, "Map::run() uv_run enter"); uv_run(**loop, UV_RUN_DEFAULT); - Log::Info(Event::General, "Map::run() exit"); - check_for_pause(); - - //uv_run(**loop, UV_RUN_DEFAULT); } - Log::Info(Event::General, "Map::run() run loop exit"); - // Run the event loop once more to make sure our async delete handlers are called. uv_run(**loop, UV_RUN_ONCE); @@ -250,20 +228,21 @@ void Map::run() { } void Map::check_for_pause() { - Log::Info(Event::General, "Map::check_for_pause() locking"); - mutex_pause->lock(); - Log::Info(Event::General, "Map::check_for_pause() locked"); - Log::Info(Event::General, "Map::check_for_pause() entering pause loop"); - while (is_paused) { - Log::Info(Event::General, "Map::check_for_pause() next pause loop, is_paused = %s", is_paused ? "true" : "false"); - Log::Info(Event::General, "Map::check_for_pause() waiting"); - cond_resume->wait(*mutex_pause); - Log::Info(Event::General, "Map::check_for_pause() waited"); + mutex_run->lock(); + while (pausing) { + mutex_pause->lock(); + is_paused = true; + cond_pause->broadcast(); + mutex_pause->unlock(); + + cond_run->wait(*mutex_run); } - Log::Info(Event::General, "Map::check_for_pause() exit pause loop"); - Log::Info(Event::General, "Map::check_for_pause() unlocking"); + + mutex_pause->lock(); + is_paused = false; mutex_pause->unlock(); - Log::Info(Event::General, "Map::check_for_pause() unlocked"); + + mutex_run->unlock(); } void Map::rerender() { From 145296ac115c223628f1cd57c760431f90821a0f Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 9 Nov 2014 23:55:00 +1100 Subject: [PATCH 099/415] Actually remove read fill --- src/renderer/painter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/painter.cpp b/src/renderer/painter.cpp index 746b0d603..960168bd4 100644 --- a/src/renderer/painter.cpp +++ b/src/renderer/painter.cpp @@ -66,7 +66,7 @@ void Painter::setup() { glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // Set clear values - glClearColor(1.0f, 0.0f, 0.0f, 0.0f); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearDepth(1.0f); glClearStencil(0x0); @@ -174,7 +174,7 @@ void Painter::clear() { glStencilMask(0xFF); depthMask(true); - glClearColor(1, 0, 0, 0); + glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } From b6616f0aa31e05a4bc05896022def63b70a240ec Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Mon, 10 Nov 2014 00:04:20 +1100 Subject: [PATCH 100/415] Remove old comments --- android/cpp/NativeMapView.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 40bf35a38..d46ea913c 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -243,12 +243,6 @@ void NativeMapView::terminateContext() { map->terminate(); - // TODO: there is a bug when you double tap home to go app switcher, as map is black if you immediately switch to the map again - // TODO: this is in the onPause/onResume path - // TODO: the bug causes an GL_INVALID_VALUE with glDelteProgram (I think due to context being deleted first) - // TODO: we need to free resources before we terminate - // TODO: but cause terminate and stop is async they try to do stuff with no context and crash! - if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { From 6b65aa62a1ea6564c7482fa88738d064e0b8281f Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Mon, 10 Nov 2014 00:23:09 +1100 Subject: [PATCH 101/415] Fix flixker after switch --- android/cpp/NativeMapView.cpp | 30 +++++++++++-------- android/cpp/NativeMapView.hpp | 2 ++ .../java/com/mapbox/mapboxgl/lib/MapView.java | 6 ++-- src/map/map.cpp | 4 +++ 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index d46ea913c..3d9a3b89e 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -284,22 +284,26 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { return false; } - if (!eglMakeCurrent(display, surface, surface, context)) { - LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); - } + if (!first_time) { + first_time = true; - log_gl_string(GL_VENDOR, "Vendor"); - log_gl_string(GL_RENDERER, "Renderer"); - log_gl_string(GL_VERSION, "Version"); - log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); // In the emulator this returns NULL with error code 0? https://code.google.com/p/android/issues/detail?id=78977 - log_gl_string(GL_EXTENSIONS, "Extensions"); + if (!eglMakeCurrent(display, surface, surface, context)) { + LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); + } - loadExtensions(); + log_gl_string(GL_VENDOR, "Vendor"); + log_gl_string(GL_RENDERER, "Renderer"); + log_gl_string(GL_VERSION, "Version"); + log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); // In the emulator this returns NULL with error code 0? https://code.google.com/p/android/issues/detail?id=78977 + log_gl_string(GL_EXTENSIONS, "Extensions"); - if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT)) { - LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", - eglGetError()); + loadExtensions(); + + if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } } resume(); diff --git a/android/cpp/NativeMapView.hpp b/android/cpp/NativeMapView.hpp index 7a7ad1817..5e12084b3 100644 --- a/android/cpp/NativeMapView.hpp +++ b/android/cpp/NativeMapView.hpp @@ -91,6 +91,8 @@ private: std::string style_url; std::string api_key; + + bool first_time = false; }; class MBGLView: public mbgl::View { diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index d58bfa036..ebec3db83 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -374,6 +374,7 @@ public class MapView extends SurfaceView { mNativeMapView.initializeDisplay(); mNativeMapView.initializeContext(); + mNativeMapView.start(); } // Called when we need to save instance state @@ -393,6 +394,7 @@ public class MapView extends SurfaceView { // Must be called from Activity onDestroy public void onDestroy() { Log.v(TAG, "onDestroy"); + mNativeMapView.stop(); mNativeMapView.terminateContext(); mNativeMapView.terminateDisplay(); } @@ -401,14 +403,14 @@ public class MapView extends SurfaceView { // Must be called from Activity onStart public void onStart() { Log.v(TAG, "onStart"); - mNativeMapView.start(); + //mNativeMapView.start(); } // Called when we need to terminate the GL context // Must be called from Activity onPause public void onStop() { Log.v(TAG, "onStop"); - mNativeMapView.stop(); + //mNativeMapView.stop(); } // Called when we need to stop the render thread diff --git a/src/map/map.cpp b/src/map/map.cpp index ba298c00e..483ae276e 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -230,12 +230,16 @@ void Map::run() { void Map::check_for_pause() { mutex_run->lock(); while (pausing) { + view.make_inactive(); + mutex_pause->lock(); is_paused = true; cond_pause->broadcast(); mutex_pause->unlock(); cond_run->wait(*mutex_run); + + view.make_active(); } mutex_pause->lock(); From 6109102923469ab9db6374b270f0b07a2c6f1615 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 11 Nov 2014 22:30:55 +1100 Subject: [PATCH 102/415] Remove left over test logs --- src/map/map.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/map/map.cpp b/src/map/map.cpp index 483ae276e..5a18ac7e7 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -319,7 +319,6 @@ void Map::render(uv_async_t *async) { } void Map::terminate(uv_async_t *async) { - Log::Info(Event::General, "Map::terminate() start"); // Closes all open handles on the loop. This means that the loop will automatically terminate. Map *map = static_cast(async->data); assert(uv_thread_self() == map->map_thread); @@ -334,13 +333,10 @@ void Map::terminate(uv_async_t *async) { map->view.make_inactive(); map->terminating = true; - Log::Info(Event::General, "Map::terminate() set terminating = true"); uv_close((uv_handle_t *)map->async_cleanup.get(), nullptr); uv_close((uv_handle_t *)map->async_render.get(), nullptr); uv_close((uv_handle_t *)map->async_terminate.get(), nullptr); - - Log::Info(Event::General, "Map::terminate() done"); } #pragma mark - Setup From 6ee24f07d780fe1c16fb41a9e1de67c0a41b2420 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 11 Nov 2014 22:31:25 +1100 Subject: [PATCH 103/415] Be sure to terminate everything --- android/cpp/NativeMapView.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 3d9a3b89e..00fb11b2b 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -59,6 +59,8 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_) { NativeMapView::~NativeMapView() { LOG_VERBOSE("NativeMapView::~NativeMapView"); terminateContext(); + terminateSurface(); + terminateDisplay(); delete map; map = nullptr; From 682e9404c43ece4d38f35e199429f63a8c3c6c99 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 11 Nov 2014 22:32:43 +1100 Subject: [PATCH 104/415] We no longer need to call Map::terminate9 --- android/cpp/NativeMapView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 00fb11b2b..eadf40c3e 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -243,7 +243,7 @@ bool NativeMapView::initializeContext() { void NativeMapView::terminateContext() { LOG_VERBOSE("NativeMapView::terminateContext"); - map->terminate(); + //map->terminate(); if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, From e8139cbcfdef09e735a7f1931537930499aa01b7 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 11 Nov 2014 22:35:46 +1100 Subject: [PATCH 105/415] Fix function name --- android/cpp/NativeMapView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index eadf40c3e..6e6b4d6c1 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -59,7 +59,7 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_) { NativeMapView::~NativeMapView() { LOG_VERBOSE("NativeMapView::~NativeMapView"); terminateContext(); - terminateSurface(); + destroySurface(); terminateDisplay(); delete map; From de1f37fa9ee77f1dd7ee535415f1c6876ffe99d9 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 11 Nov 2014 22:50:37 +1100 Subject: [PATCH 106/415] Change Assetbridge extraction dir to data dir Only extract needed files --- android/cpp/JNI.cpp | 6 ++++-- android/cpp/NativeMapView.hpp | 1 + .../arieslabs/assetbridge/Assetbridge.java | 20 +++++++++++++++---- .../java/com/mapbox/mapboxgl/lib/MapView.java | 7 ++++--- .../mapbox/mapboxgl/lib/NativeMapView.java | 6 +++--- platform/default/http_request_baton_curl.cpp | 2 +- 6 files changed, 29 insertions(+), 13 deletions(-) diff --git a/android/cpp/JNI.cpp b/android/cpp/JNI.cpp index 3b1801c4c..708063f24 100644 --- a/android/cpp/JNI.cpp +++ b/android/cpp/JNI.cpp @@ -21,6 +21,7 @@ namespace mbgl { namespace android { std::string cache_path; +std::string data_path; jmethodID on_map_changed_id = nullptr; @@ -139,9 +140,10 @@ using namespace mbgl::android; // TODO: wrap C++ exceptions? // TODO: wrap other sorts of exceptions? eg coffee catch -jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_) { +jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_, jstring data_path_) { LOG_VERBOSE("nativeCreate"); cache_path = std_string_from_jstring(env, cache_path_); + data_path = std_string_from_jstring(env, data_path_); NativeMapView* native_map_view = new NativeMapView(env, obj); if (native_map_view == nullptr) { throw_error(env, "Unable to create NativeMapView."); @@ -754,7 +756,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { // NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the array correctly (too large) std::array methods = {{ // Can remove the extra brace in C++14 - { "nativeCreate", "(Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, + { "nativeCreate", "(Ljava/lang/String;Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, { "nativeDestroy", "(J)V", reinterpret_cast(&nativeDestroy) }, { "nativeInitializeDisplay", "(J)V", reinterpret_cast(&nativeInitializeDisplay) }, { "nativeTerminateDisplay", "(J)V", reinterpret_cast(&nativeTerminateDisplay) }, diff --git a/android/cpp/NativeMapView.hpp b/android/cpp/NativeMapView.hpp index 5e12084b3..75aba6985 100644 --- a/android/cpp/NativeMapView.hpp +++ b/android/cpp/NativeMapView.hpp @@ -15,6 +15,7 @@ namespace mbgl { namespace android { extern std::string cache_path; +extern std::string data_path; extern jmethodID on_map_changed_id; diff --git a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java index 8730cf4ca..f12468dce 100644 --- a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java +++ b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java @@ -9,8 +9,12 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; public class Assetbridge { + + private static final String TAG = "Assetbridge"; + /*static { System.loadLibrary("assetbridge"); }*/ @@ -19,16 +23,21 @@ public class Assetbridge { public static void unpack(Context c) { try { - // first let's get the temp directory - String tmpdir = c.getCacheDir().getPath(); + // first let's get the data directory + String datadir = c.getFilesDir().getAbsolutePath(); + Log.v(TAG, "Extracting assets to " + datadir); // now we need the assetmanager AssetManager am = c.getAssets(); - String[] assets = am.list(""); + + // only extract what we need + ArrayList assets = new ArrayList(); + assets.add("ca-bundle.crt"); + assets.add("styles"); // iterate on the files... for(String asset : assets) { - copyAssetFolder(am, asset, tmpdir + "/" + asset); + copyAssetFolder(am, asset, datadir + "/" + asset); } // last, set the ASSETDIR environment variable for the C @@ -44,6 +53,7 @@ public class Assetbridge { public static void copyAssetFolder(AssetManager am, String src, String dest) throws IOException { + Log.v(TAG, "Copying " + src); InputStream srcIS = null; File destfh; @@ -58,6 +68,8 @@ public class Assetbridge { isDir = true; } + Log.v(TAG, src + " was " + (isDir ? "a dir" : "a file")); + // either way, we'll use the dest as a File destfh = new File(dest); diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index ebec3db83..37db7d55f 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -116,18 +116,19 @@ public class MapView extends SurfaceView { } // Get the cache path - String cachePath = context.getCacheDir().getPath(); + String cachePath = context.getCacheDir().getAbsolutePath(); + String dataPath = context.getFilesDir().getAbsolutePath(); // Extract the asset files Assetbridge.unpack(context); // Load the map style and API key //mStyleUrl = "https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"; - mStyleUrl = "file://" + cachePath + "/styles/styles/bright-v6.json"; + mStyleUrl = "file://" + dataPath + "/styles/styles/bright-v6.json"; mAccessToken = "pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"; // Create the NativeMapView - mNativeMapView = new NativeMapView(this, cachePath); + mNativeMapView = new NativeMapView(this, cachePath, dataPath); mNativeMapView.setStyleURL(mStyleUrl); mNativeMapView.setAccessToken(mAccessToken); mNativeMapView.setDebug(true); diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index 974c408b6..e0e005f66 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -37,11 +37,11 @@ class NativeMapView { // Constructors // - public NativeMapView(MapView mapView, String cachePath) { + public NativeMapView(MapView mapView, String cachePath, String dataPath) { mMapView = mapView; // Create the NativeMapView - mNativeMapViewPtr = nativeCreate(cachePath); + mNativeMapViewPtr = nativeCreate(cachePath, dataPath); } // @@ -407,7 +407,7 @@ class NativeMapView { super.finalize(); } - private native long nativeCreate(String cachePath); + private native long nativeCreate(String cachePath, String dataPath); private native void nativeDestroy(long nativeMapViewPtr); diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index db057bc1b..5238283fd 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -423,7 +423,7 @@ void start_request(void *const ptr) { #ifndef __ANDROID__ std::string ca_path = "ca-bundle.crt"; #else - std::string ca_path = mbgl::android::cache_path + "/ca-bundle.crt"; + std::string ca_path = mbgl::android::data_path + "/ca-bundle.crt"; #endif // Carry on the shared pointer in the private information of the CURL handle. From beb12bada213bda24e183224ad8f115b3548fc03 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 11 Nov 2014 23:05:30 +1100 Subject: [PATCH 107/415] Missed other Map::terminate() call --- android/cpp/NativeMapView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/NativeMapView.cpp index 6e6b4d6c1..5644b284d 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/NativeMapView.cpp @@ -197,7 +197,7 @@ bool NativeMapView::initializeDisplay() { void NativeMapView::terminateDisplay() { LOG_VERBOSE("NativeMapView::terminateDisplay"); - map->terminate(); + //map->terminate(); if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, From 53016d65d431ca404524add268a22ff0d8e18416 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Tue, 11 Nov 2014 10:48:30 -0800 Subject: [PATCH 108/415] use remote style URL --- .../lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index ebec3db83..72dba4389 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -122,8 +122,8 @@ public class MapView extends SurfaceView { Assetbridge.unpack(context); // Load the map style and API key - //mStyleUrl = "https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"; - mStyleUrl = "file://" + cachePath + "/styles/styles/bright-v6.json"; + mStyleUrl = "https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"; + //mStyleUrl = "file://" + cachePath + "/styles/styles/bright-v6.json"; mAccessToken = "pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"; // Create the NativeMapView From dfd382c3e8019776666ad0fea228ac7ac9699b78 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Tue, 11 Nov 2014 10:48:38 -0800 Subject: [PATCH 109/415] update app icons --- .../main/res/drawable-hdpi/ic_launcher.png | Bin 7658 -> 2546 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 3777 -> 2015 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 12516 -> 3358 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 24777 -> 10233 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/android/java/app/src/main/res/drawable-hdpi/ic_launcher.png b/android/java/app/src/main/res/drawable-hdpi/ic_launcher.png index 288b66551d1efd1f13dd06f20a67534d2df57946..f9875161da4b31ec5c968910683886c619491366 100644 GIT binary patch literal 2546 zcmZuzc|6nqAOD#9UiHmUsx@7XnH-61VH9%BF@(%*vcoQ0#FA2(Bb5r3BhfJTkw#i6 z%+WzhzP>~ULglEW<2xPSZ@+%u_v7)tp0DTgb-h1-e2%*~?N*dqB?kb2qMfa^t7x}f znle(N)nlr9w`dcixbC(BxZN7#qKhoa)|&zV@++367?72t1^^&^oVyp*%hACsh=|t< z2qp%G=rQml5gP!^8D^p*K7<+oVc;VP6f*__`i)^GIxoR+DC8T3x*q}ca&&>9h~yB6 zv7V8hJ`^bjfk4d3!J%fZ*1LYlMK=UAoJu8`!Qs)-(R$H_dPH&bgS2Gk5Pa=mZ+ENpcCI&wQzk_TOQSy&6gm zEYv<^+ozG8C*x%yg{olbLd3;Tb+r)rHj9>YJgg050M-xok*{JYNGS9_O891`cc%M$ z*0~j&9^Gzb2(v}YA@RwY;rdXe2Gn{*keqxfywisJbmygG_4eV%SPo?uaKvz|Q{yYEYTXPa}q z0r_Kh&4xfU`|}{`LEn{X$^OjAbj0V9U?1nAQbMvvO}|P?+w;y+4dkiiN?_A6s>>?r z#O~>R12E-sJKco0ji%`W!An(Q`+bVZY5o%%UPopTE_e1wg2tcQw)7D9ZEgbG_DJm* zQ9I^d2BkgkBNV1;tI399XV{WQpd%@3CtAZy>8VvYs`X0zuKbxt>uzNn8*SGnT>CJ_ zR~+1Up-b?pqW7%WF)IOWDv){DA#JvzS4-luxRi zj%#Sq^}V8^nc2hdXdFd{AkdpEtlZ3=)%6xXEZIhnZj<+s4we%WD)w`RR{EpwWwZ7p z-nW<;%ic@DnFiROJDm5iuxxC6aWOdB-)hq`-uT7me4{%jr`nzRZ(NHHP~qr$e#se% zPg0gS7_2dV+x^o->UtrEGp8+`Y$_2wd6R7;kRLcuj2UA+-V!9cernvgrd98Q0OL}OfU;mSbNBU-12kz8feLfiK$97ilT~i|=W&66yImNNV$ zaQWd)@4%pK0VF&X%&l#NaX=L2WWbu(>BZi}Px=OVoFG8i9wdI57Mj%XbtJsH+RQKd z7*2Oq4bu^mlra-Nq}^X1R~v@K^yGiifUHHF&G2s6cx1Phr5%>Fz2h`tvdJl}r!7pC z0Q0&((_N9Hw|Iv;Kl9@7>R#%;EUq?R)zbAT9G=!rEKs-OcO+!C0Jzrih?0?I3v+zO zh5dn2ZoC3B&MdQ!P*Zu`uWootfsy0qLvTsyUan)8AnE1qiqsRB-U7i#rlJOTYNHb9 zPf{~Q<)vWx!JcvniEDD-1bI(#yy}CWiHF!4V^H0izp519N~We zsxc9pl3Fy-v~!nc%dLx9#oPQ~$623Ypm%mFD+aZAygk#E9L*c4P94Wff*70fFT&ue zb3=LJ57q?UB5ID!=wBqbPvCe(tGlrN^bd0t!F^8gOm#o!d%`O~pzg>(O0`!&e8OWZ zwZf>g?;Uo;9Y4gBlkXbif7A-sI5Te|3GT}+sT{rghCAtdAapDCRZA>B!U+lDN%)pv zAmx)8H?$nZtxY0sF{Oppuc26YQzKp6uzrO}L*OnKWwK!ezu(BhJrZ~1!mbu49i5RA z3oo@%)Lz+c1g42JgLjVS9CK_W;lnYAs{y zG)Qg(s~EYSI_%{%2)Jnvapm^jk`MrW9s@!h+|qTa)fch%-2liyAw@D+OkGB;s!UQH zSTIPAsxRBwh-eZQdR~}*=~3UVlZm#c#(aDd9)kqPmsgaSPZxc-pZ^{m`+Du^3PSy0 z;n7+p*=JYRA;)eNuGGG-58=EUO<}W#JJ0RhAe3{3f5}~pJ+QaQ(JwFVVp`?&xfM5~ zB6r~<7Lvh(daxozo$mOzQjgN6PCOjjsRGHxZE2uhtmZj?dakBC@L8o!pxGaBcYV#e zou%~w@_g3QgPwRGuJy2MTV#Fm@L67okHd67xO>GUQw$ln=5L zzxs3PSPGgUV%`=~preudEmhJFvDOdlO_vOYVB6|cewO{sJoy!Hx{(HZQkx)L`Vq6U LakA!GVH5raXlr5; literal 7658 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 diff --git a/android/java/app/src/main/res/drawable-xhdpi/ic_launcher.png b/android/java/app/src/main/res/drawable-xhdpi/ic_launcher.png index d4fb7cd9d868f1d7d9964f1686dcbc018ef9495a..0cbf5993624c94e3635f3348feafac1596e0859f 100644 GIT binary patch literal 3358 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 literal 12516 zcmVAl;2fhX_b_H$!(yr=WHoOzl)fEwqZl|>V1FuJHhd92cP;)+IZb%XWJHo{vS%6?KQsMx!WMLWsBBVA_0ZM@;`i4T>ABg5!-j8KH8bz&}^`6Yx zV|buri=K8jdLyw{MbH0k02-78Fw+6gA^lLF)DI0A{m{fmOL+z@kx!u6?Ey4s--PlJ z1kMW>2pjDnV9LAa8My3(_0_V)T2~_r72LzBBtFHb^6rT*@^C(1@1v}C4aLf(nycHl z=?}E;)ScJ9R`my=a5Y~JLY*E1jp@%|M(<-NI`jZ?&pm{k8xJ7+?tRF-e+!cCUx(Ox z*C77Zbx68+4bqNXgRH)5OhNe-L5bbb$GIa&+9443-Sbpld&yx>!&=>egldKIX<-sS zy>qGOUqJ|~GXFGZma1#b3av9Ux9C5sTB#NZ&|ufk)~$R3C08Fn=A(y@DR>O|%ws4P z5PJlLf_uOhT!QI>li(mY4t9bgU@Q0?Y?CBu^wvZDi3!_oMUC8W z)v2X(m(gHG3vaV;io7ZnAKp>#e%pFD&V!Gjn7Iq7EQ(J> ze+c>PJxGH)kPLUJr2u+AT!C;n4PI~pT;V7vkreoT7Qj4x7Q8>Z0~y@&_tKh9JodbQgAHKsKpOiH z3YdG41-Bs)?m;>{gK}V?8UM>{v`)M|6YfJ1oP%Ka13VFcD;xr6*v~k??g2a4@zff& zKD1yq+%jf1-mzd;TrgppH|QL!USVuMd%5}77=0hYyBq-|FJA%v-%)&vhfuoeKIA>U z0Ws`-HV1{x1I+E{<;n0ADu9J{6n!pQH`;t^gLvO<$VC95a1#9B7YpSooRDl3m(PlzFv>=Rnyss9m4jw`!T*Jql#mAn4AOzrt z0K8$pz#Vo!bAs)UCd1}?*0BDj1+2MZ0?W@E!s3(qF#mU5X7=t$=bAR^BsQ!u`bN~Y zzEk28I|s4$_n_$bZOCTMLnwO#GTYvcmmvYYKNoX-5vHGF>gnYB*D&{A#i$_X zD|kK+F>nU!z)Ac+$G{&Bvp%qY&=Yn)b%E^ei{=C|#uDO@_38H-*171((>4CzD5=g@pCD zpp-cUVc0TGMIqy;h(}>Mo`VoXGzuGv-Ay1CSn_o!1w#ZUb{g#z+8=n|afEgRA9GMJ z9rg`OgI$k3VfzDD*nGzk`mWo->dRKJ{G1tjKZ<`;2j(BvgRXr#(7s(08rG``%9kpi zEbLM=eCtyY@%$}d^VRQy@S2N|{p1Xr%M&YKxVL<>K==X;s{V8zZStz~l5QF!# zz9{Z=OfP|G!Bp&H2;&%@j}8fcm4zTvJEv!6m z1xx=hf!^cJu;TbMn18?=X6@33ncK9Wp--Ky>QxaGb}Ma2ZBv*)=YqEp0`V-eZnNi! zx8pG;?#yYHFE}_DKs%M4kFkG$ngq88cS1jN2ObQbgaeObu#mc7KKF(L0v}8*SYR2< z@k{`O?Co(F8c<$|8?2x~4+hE?ZnVd*Io=sOb%cb=Sr zrvrE3+^zMnc)tg+lhN~Sq4%^kO*?xomSXn9pUet?VFqFE z*%oNtpbhow)S;?Z8HyLGFxef7yOV0UlioT2#QPh7_wj~nPOdxe2b|seIE>kTZyNO7 zw8H+}6#A}BfyNEg(t(uTU# z>QMO`0ieugckmu1*Ko2D3dVi&R;3}b7`Eh$%`fXNyVP#E<=4OE)--nQ6Dz;^!97cu8SHn8juD_DHY0u~-Ih3-Sf(6!eHww)@1n-BhgOV>_9+tP3-U!sEP zM-{LLL-7I?0)R=J!PyW|JdyiWMFEL`rKhb`R-JR)v+lA7v-;v>w)dPd_T`4OH}5@* zcG4F5PDR4H)1fGyHT0gr-u;3NrV=~!>d8X8;y^uo%_X6&zUY8C9#hRZ;_-Iq@wW8; zE;()qi;kMZg2O2OK_e930A}yfgN|(mFmJmn%v|FNCG$0*VyP;6zZ!zjfRY882tb*M zuadbLULfV>$me|WRzv|!KYMr|FFI=B`P<3KkCvaA%q~4?f-RxG&`ud(ku*dzf<-3` zVbO8YtDC{nljan{^0QX3;+ze&mFH|}Y9V4TKST06Ni*o}CvC9Hv4+J|c=Y<;&0zi^ z6PUN(2<8y+ck5xxsRM0Wbf9SiNjnevbI(Tecm9!uvOQJL= zam;t!Z^KeGc@aIw?0;Br$Z|%{5ev5Gs42Vfh%OdJT{4kE&oTWWGk`_M458;J+A(7U zfsjs^qOj&eW`W*qiD#iLIblgXounBede0H!@h^n$+GhxzyA5F0P85F|5q}aiZ`6jy z^_oz(h5*3azf2v)SHt|T2KilTklDs#;wohrf4;;9&(w)y--A?rS~qK>_*zu_dKABACAOZusE9?YYrkB|&7hZ_!=5t=dHhvx%E_KIq=*rO~4Y~}_u~Umqq_cMun}hSS=PRN2D*(TZ3rTgd5LqI{c*Tng?0v)odH%eQNg8?~JXyzD z{wct!FUXlcwCVr<0^wC}*z@rXsQFU(VCGRo?VK;%_ zaRt(rpB2Q9lg@(dX+ctFGD(q;8*sa7i3q^m~;~R+Z>okOEhlr19XT}Pl z_g5@ahO#9j0+cZSD?-*R1xRg@XJgCd`@;)4e*|T7y4)fsD3JO^UdKs6rtNt)WzAK{ zxN;Nn9{j-uKHl}z_Q8f*mY0{DGF;iQOWV4wPw%rg0)hOJBf$UUI^@`21OCp-kaqt% z6bxKqvzgtGT?hNF+hD6{cXHld+o;()%)cfPKwd~nyTQnmYTfjNBI9fE1*U9Lg)XGk zsX^9E9u&=2p)FzEYAw1vK!hb;-LV6WM8Gbg&E73Ea^EYqgFIdcs11R&ZqXjf{n+*N zX(I$JEWDbS_cfq&p*rMsszQ3RGQ?FXK~$j{gl6bKK&&C_5v23T#+^IUda4A6Y+{B@JdylAv!p z9<9aMebEw899GkRA@=5e6V6Y7qtiB)|{(4W_(UzAu2u2uNIFE+|oLr+{^4MP-yNA8L*?}x6ke2KP`(HrC@CT4p{}v?VzX7p{U&E+zU&60qe~00t z{|-No{2YE9@frLu{8RY;=TG6gpFe@`e)wgbARzHK8>#tz%KN{Sk#K0#-971v@veVO~*vbCl z0p5ItZ{sTEcVL=g>(^>Bt(&mGZ_$UAjYN1&+DTy%XH)9s8K0C1%N&BnjC;KRFqO>i zb05{T#=Nt7g&I@6LYb|>!dA0V1FDzf6Iw<>TorO>E3w#7u>SmsryQqA{M5F=!Mb&m z{{01qP3RESd)5@zUbchvS0=;C3;6XJ3%uV779O&OuH6>UvDqA&R+~cgQX?pvuMb6Y z^dNWUB*<*ig7i8~NU7F<#7Z@YE9XH>nG%{JL?g`TQZB@nb0NM;9ulh+Ah}Kf(wY>> zfQ8RV8S=VFx>JVIMc4%`R-to1#ZuucP%xK=k5j}#EXM@rNHTW5W3#)I>L(6 zm=aD+#wpnrmK?Ljsn`;Fez(AgG{cG57`pZtV)vv^+fz~#8rEszEI?L3Rj~-8@bh>$ z11LgTqXGpNS;S$z6UVWZ9%8u!KxnS?{){#`wrsHqTh*&Z#V2P1uV_93=ul+iDhL3{ zHv?eSCjSw&y;iNo^HrF_ZUvTf5e3BNDWZod;u9iX$7Q3-q*$+5iL2($660$YxtkQt z)4E%>Sdpz{RErkVwyoN6fFKXd0`2h^?r z87;zSiN#0?&zEJr;>WV)?xWHPfRJp?@$?p17GdFYSEJ&S1v~_T9TdWASH$iK0YqU0 zxV znjWa3;4@IDDipnPnNWBlSjj?V>a|3C>MXM`JSU@jWI zpxjRKg5+_eCjg{bkLYn%4eSs=w})}gOwGFm^SC5sv89W7Ac8=mK?Fi%wX4;r)vQwc zhub(%tq9YU(h=@+aBKckD^+NH9=M&NLyU_b* zlV}wVV5yjX605O}lqf+c0`QC-$C|s16#~eXIg{EX%VNG~Y0X$h(vK?rnIwH>&QxTv z_Au_zl0A0b;^J>40z?4v5I}wx5nloF<|ttPSHMCh56Sg%EROW7d-S;LhV~;RE9Q6@ zXSHhH&7C91V!zCmEL5WFFcr&q)PrX%SD{9RC9+gN&qIULj1{EtsnIk-2dKrWFn2>B z96i4V`UjrCqbIjv*YPfBUg<;y78Y1l`Zy|l4-sDI`BZ!?ws~`ugx)`tez1#ygc zRjdFZIULp_X6$nSK^fAo0$3^pK!6gE8Ep#aY;qU@5}oGmBS*b<0Ns7T!)tqOnhU#C znY=mjEJC2-ll2tr2q2@COI{EgRl)&`fCr||W96&n`55tAH16hf%8{Bu3wP-v9+WRd z04OSfP=x@VTQ4!;f*FYv;>k6=hA@9~82o;A1w8A2NcVSN94y>_bPaYKodxY{yr8^C zn~E+n()keaNvD%Xyub6M^wTT^5KjOU$qNBQ69D5W0RME!GpUUTV7@Xb#I*Y%fL{Sn zBFDH!Oqla#0Bj2TspdDk8HMvynA|yB79jw!T=f3z4tdf6VFU;PxJQk9Xzn7WSlKnz zFr!KBZuTr$Oj(NfVif5zK-rQZ2)`i!v`T8ig|gB`iu8pxt+0XKUFqoYtKrGBd%&_R zi2gm7UvwYKz}<(JVfXR5FmFRRR4y_?k$BWWi06~rxt+pJjiw)LI|+c~I(dkr9ncE^ zmabz{rX@}KDIk5~)szMfIu4J0Il2e{5=j}Y3T$LC0We+&!0~mrp*urD4eIt z)x;)q&{{jRn zG6o(zz6qxRPev*Dj0Zwa@7Xql@LPQ}Kg0iGpw@3uwJUW;F2tWWS z^&FDENGBw$Ej_{+fTXhYW_cEeHP$U+LYt|F*oZd-zySfMF*&ojZ1yZJz9{1TTzpYn zNNbkEmJTDJ7$YEZEY^Tum1^dCtE4rkU(RTg#fZVy6}zUQ1;R8$Ae18noDuLvDZp== zD&XP6dsIpW1!RUi|F0ed0K+g80zr5A>N%+G42PtKiPZZu+ep27833sPN|=J=gaBN{ zg5&t22p9seP?GTuH%9ulTAnXrAkr&SU*^jc8K>EFB}3eTL>VmfWtb6jH@@X|K;b`4xoEY z!Z)=`?Q;o$>{)Uz0HF7i4oC<9Q&15BFpjnM6xS`EJ57lKXyM@FaUQL;|{y_ZGeZ5?t?%uNIm-Br6Q5n4-O8(t=rdO<+=sn zA7cVWP9vbWE(*?FI01W(ECYVC8VV;z#KV~d0n`WqB$Vkvc%BM+^H_|4alyfHdc%EF zC)`e|&M<=5$U+6yHB9Qw0jTetl1Bh!b;z+I@rn0mU;$2T zl*67{2*5pREMx5|u3Jq2)N5WzYv$nNg#gF^N1~v3Az4e3hx%R%ICp&)_4q--0BqT} z08%Tgz&8P-q#+Xa9NY+Z@7=}%D;V-__SN!B#TUT!8&_b%)@6`d-~sMovXEZx1UvSx zcn)UE-X2J*R-ulPhTczFP$2*%A%I*0Kpf1S#|1_5&BXmuCq7K7lR*Had*e~@ixvnM z??^qt2x6m(lv($1*_ug?!+(1H0QMk&6$F4flhrQAX12>A067F8N4+YgK^EextGBkp-TOC&9!mfW zz~!u7{j^4rlx_-GsCIT4DBmia#0c5la0VLNG0J3a&0qrIj zbH`D-rCnZ%X?2>H)0$-P@dyBmc7E4T8k$cQ^W*A@bE z*rr`Q5L3c~#9CRJez0Z6h!p~eCjfGI;1Vv*m^zO2j6neYX%qWN1Yo|W0E&hNJ2F$y zbuauGkp%=Ar%=!C+izbh0J^={$N#cnxl?jMm-^CVt(iDu z<}ykztI?X1R;O_;bqE0InnY1+PJiCkO3}#t~_1F zE||xKszruy>hhL98MI!2c>3%ioVu_VX7;3kSF{XxNAqCrvI;nV`6yi*qE|n&|2#Lm z3?Qmd5hq^`6(2h`k}@H-T$ut0$yNfFFpOLmapzD1ApKPUMErtz!Xb>z5-IfxSh$s0 zmk>_D8;gMF0I;v;x1sngF9EhWWwdcv>iq~Htx1*^;KUjZ#FWcGNS-8vDTuZ6mb9Y)YBWx# z5CE;DaK8XRma_A@xKP>ip8+5T>4Yv{Jq7vo&d|~m3HKk~q+tHH*?<(`_1nAAITYw~ zOstk00)SCap#rhRx)7YDKmnLIju!$*K>#%}bnB5Uz7-4sAZtowDI>X70SmVZ;~XlT z!ZjK8#cK!9cb5NI;|kZ{!Y<8wC_bAp!_Lfap?bAp5E$1zCHHx67`vluoPD zKAqek!(y!Vi9(da0*JE#Pv;?Fh~J(D8S1AFeTBY3<@Aeg>eev zq;gGuc@qHI5P*Ad!5nP_z+uyyIgr*Y^!{Yb|D?D@Q2-N$0EpXVH(5%j69Dx>0HkFl zb$~_zS<0Rx_m=@&LIBw{PSCs%0X+H(05%W+BuX(=R>=whloJ5z2tWq{`3N9{q#%hY zVJSxHI0-WZK$Zx=TvC7$z%05FiYZqCLUU9EPC+tBJnLWo{#q~SeGXuydtkvFt-B%s zsZAW}{iFcL&yX3400KaaHGc(wGzCCfRuO<)vXRp%2NgXAe*pkPBY*-}@K*t}qhP`a zh{NJb(oak|)`AL6h%TIj0JsEzz|>`&Wk{N##&i*Yb_H5^iTHVQcoaZ-i#*xNWJ9x5 z`<;V0krEQaUhiiQk*nhN>}mM8d4>B_jDVY%e%Le=Kc!I!fD|hN;CTdui?L?zV)pz7 z6REUXtrHjl49*Hd0PQc*5CyQ%;4i13?CSp;fDi;gBY+fNwCFOd0p*$yRiFg{8FC0f zY{1xgtVvLc9!9_f3LtX`Kpq94jL%((E@cn^A(^WEQvzhd-Uxs`==-Q?g_lRcoJrTo z)dHy~eoBLkF#X^YjjNJ@$YLo7%AUZugpXm(+{7klG+Ia^fFnr=Agx)BA%&aF56=P2 zmV@$zhH&!o=KpB*^Z%NHo;`aCD>n)wAf}9ixnGv1pO`WfvQi&ni}WFwuLMrP;uHXh z01*HZ0i^H}@pC)r)ex9^$wHnIMu7Sgw-Ci34rkaWZv;T|N>8`KIoj7S{jgO0dKn<8y)k z-J;ub02l#H|3L%@ThIX*80d#pn-D;*EP6eu13~~K0+OqYAf>_&OM zrRizH6hvCioY_2VKb7e@t6hn%B_+;KX2Y_y?|Ve3dZ9!gzGeVGFFUVU=4xHosdIsd znNrVTiTH^_-XBvg4dI0dAd>(PW6V4xZ1NiIB{S-E5da4P$T6gCrGQ!$q6IqA|KGlI9eUTyf#76)bPh@MdRYn} zh5#tVfvj8~5=tx}EK3Kb1Wp(*m?CEG8)f>vZ<6fc*a{BIZ&hTnJJ9=QtKf51!ROB- zTbT^j4nahg$uXZOJ)<}BrarPBRMzRJ%5R#qEwP#-K(A*KX2`HZ;!6yu#J z!PxnYIcMr1!6|L`lFVzf`aPvykwLFv$g+h90EvR^*}_N&D-;K}i1CnH=?{mFY<~_y zwCDSe5kLX_NdyS{ezGim4Yu!I3vrn)VC?)W*!uknp?T7;q@d^$A%MhEONh!b1E&B^ zzp3LmOY;5z-*`pOuso#;8I8sR1+y##1+y)fybd!qqfwuUFVh-`$v3$cm};2i>d*V} zO?~#z2Zb#*Uq$4phX-YH&f*j}7@nuX1ZQ$t|8!Z_CsB@ZkC0>Rd?#EqaTy(D9wzZ= zV^4@=)m*PbIc@qF0SeCnkd;&lfJDJ;vYagkVFeQ48Z{hz|6zf)e+$6&jD+n;ouQH9;T;Fgzy6C=P8Ip0=cd zODrKK*9srceQM+&VLA+Z`d~ii%fL((_vA{GrG+!y{wV95db6nA^G;f|&H1Q&y}s}) zo!Gz>jgfEqT{0g`OH%yFD_YjaJ67qp=?Rn0_{3=5_KHxwJ|p4_fEuz~N)NXb}~y z>0G>WN(f*9nS^iPbUa84?c>J}VCUZTP|=ux5H-kD3qDB_*p=aQo-0kgpX^shmvU$n zM3*9f5(2;wQxF2kK>*Xa_w?+?X_MDU1}pN>mf=Ne6H;rl{0q><@&mW$on`2C|KD0$x1sqj{2NDvA#b3M@>sZ z$JQabL;$dnnU;)$pWF$3hcTR&h5c9W1N(3KY@t8(2I4iv9kE}Ow)-aJwb%`&*7MlZ z2I2aVC>7C79kR3b5&*iv(;`Q&vLM@>DfM!6@hq%hBDjQ)1-}?=2+dN0gmNwLickkD z@6iZ|eADSFx7LfBUTwcVu1tL(u2PmE%ho9k9C$e$iA)4Q1c6*@l-9(30b+(c`12*O zPE3SIoVur{OOlIfshGsuspvJr%T35tZ{$pj+#fGmekM^-CWn!NlY5~$if_$Ep!a)5 z@Xp!>NLZ7%s1AE;KGGONwW=!Y)AVu&*Qk7*6F3MBkaiV?V3TTu+k(mULV=0UB1>&-P=-3|pQ<|GlgK?gHI9?* z6(u?H?fpbu0-?eG@8Ow~3;w4b9eM0{P0fr>Ajy{gP!pNNYXpZT}>7QTrBApgkrsTQ|rr ziP!HHD**xNT!_j?Pc1Wt^s32_S>s5JUo#oft8EdC^$;9OHlxmp$*8wxGaGE#tVTOF zyJ<4o6v%0Ig4||j$Zc_hycT!7?LjT4$pf+*T!CNf2&pq{Ag;t1BJy>vhUaO_56xBa z56e@NA#bOBsgW4=mO~JJltJ0dC{944?tL@2U%P+LSf4^C~EVC;`ZrKHY)%s=LAFL+z_;2sOSoWvQ9rJ zYM+V#oFKW<67#(d_$8^bUJ;tClRvL%T8yF$c`xug_bKY4{<(g3m`p^L{yiI?5n$r- zGplF+Bk0=x1p4;F!NhqKSWOj&$^H`H5-NpYFo5tJT?D9sLaV^kXhm=i;eeg*1Tc3O2g4~N!Dz}@*3LtnwQy9- z*Ki#5J(<+sxsR|H0jTYcn3zy%bjLk<3^)dlVr_kY1#^!PVB|Og^z24}j`gpgZTTx` zm<|VRvoWA>$1kZoi$0F&LI&nqL3^0-&KgVoX|%@g@I^aWE}OoOO>D zLlC3E)@L+Wc#H;9m(d7gG)4r9XfF;rR-**!dZYB7PvJwy!Gj0C@Ch?oq+#|wqiOyl zqhs?k7&wd=5?qXWzqz{@-W~%MZW64+Gz8!xpZ3lI5FJem!$xH_m|h6Yp8!GG60Bbu zS%4b{?onf5N{~3%Oe5lrK?tM3c*_@)>KokiG;%~lDhO>0-eiZ21{lpkM z4#!kBiUOQGeH_A+07o>300bK_k#!5y9JKL305~Lek;M}syl^6f zl7`p{Df00K5LF@#5ryQc3Tc?0 zhTTuBBshg&B=|~#sk8W>0tiYp?J#p2F0l0am6_~68eGE0f=A2*m>Mqy(-Nh@2aN#r zis7(RBPR_`_U63@FuN~)R9TnhMY5S6S1HBDR*;=4vSA_(Vfi=@WXoVWl0pxb0H+`+ zuyh|M(6f^GR{%bYOt`{1WDE-LJkY4bibCx z2<3n1f+&E5{K@T3A>#zjVPhDtxN+c{DhUC6jHGN?G&u;$l0^v8;Frv0{o-^6o&4Sd z7*@9`@aN)A%e{#;vg8wM+1N@1K%)R5lyKx#%$Az9mxC=v>X>4U*S%Gi33+i2}7`Qyto02L6^ zQ9`l3t#6X-UEdT*L13l~OT-T^An$%xf~aC1y(B#(N1h2tmlyaYC?9eO;Tr1c4f|-= zyZUDghYkH1!^CnOgUBL<{V}CHHlb3JjVo7SqDz#Rm{L_HrbLU0D$rqrGBg>FD7n*9 zrjK_KeU|4x{;a}6o3BDL6>7tCd5;sy44C99LpG(_kWH&IWi#q6*tA*;Hm+2k4bD`) z;+-Is=oK>Zhj&-}mnP!aV(u3KnR2#4nY@)T1qKh}i_F-#B2zZL#DYyIv1MZmteypD z=2#U8&jev7G12&Pn%(UB)8dNu(;iWEuZ6y=Evr>dOS<2FxwTD zqaN;`EIs1=pD=uJeBc;0>htMIQj-2D3R8nJ)N8}C^p;2EnXHP=H*E>aHVB%Ytf}Dd z&--65{~)WHk>v{CCRM1brPUe)X1Cf@6t+9J7Ie6jWi;7(C0Clr#uvW$bGh%2mmrX| zLO!$KoiFjVKcDww3}5d@{{o|L!onmz9ZEU>QLz*iWpXV~e2dms{5HF9Nv+`z{XKwp u_AlqXKi(hjkN3y>dEP(nupnNhv*aiG+YKfDYX) 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 bTX&eRr$q&LU6n8H`|;G2v=wXQt={|(UOb$! literal 24777 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 From a1274fbc800e96048baf71225ae2c8e23ffdbf32 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 12 Nov 2014 08:13:06 +1100 Subject: [PATCH 110/415] Lets no get ahead of ourselves --- android/java/app/src/main/AndroidManifest.xml | 2 +- android/java/lib/src/main/AndroidManifest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/java/app/src/main/AndroidManifest.xml b/android/java/app/src/main/AndroidManifest.xml index b3e55dd97..a227b81f5 100644 --- a/android/java/app/src/main/AndroidManifest.xml +++ b/android/java/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ + android:versionName="0.1" > diff --git a/android/java/lib/src/main/AndroidManifest.xml b/android/java/lib/src/main/AndroidManifest.xml index 4a8844179..16dabf837 100644 --- a/android/java/lib/src/main/AndroidManifest.xml +++ b/android/java/lib/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + android:versionName="0.1" > From 918f590d9b26a24b89672791e2c3bd629c96da89 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Tue, 11 Nov 2014 16:11:42 -0800 Subject: [PATCH 111/415] rename icons --- android/java/app/src/main/AndroidManifest.xml | 2 +- .../res/drawable-hdpi/{ic_launcher.png => icon.png} | Bin .../res/drawable-mdpi/{ic_launcher.png => icon.png} | Bin .../drawable-xhdpi/{ic_launcher.png => icon.png} | Bin .../drawable-xxhdpi/{ic_launcher.png => icon.png} | Bin 5 files changed, 1 insertion(+), 1 deletion(-) rename android/java/app/src/main/res/drawable-hdpi/{ic_launcher.png => icon.png} (100%) rename android/java/app/src/main/res/drawable-mdpi/{ic_launcher.png => icon.png} (100%) rename android/java/app/src/main/res/drawable-xhdpi/{ic_launcher.png => icon.png} (100%) rename android/java/app/src/main/res/drawable-xxhdpi/{ic_launcher.png => icon.png} (100%) diff --git a/android/java/app/src/main/AndroidManifest.xml b/android/java/app/src/main/AndroidManifest.xml index b3e55dd97..a0bb4c08a 100644 --- a/android/java/app/src/main/AndroidManifest.xml +++ b/android/java/app/src/main/AndroidManifest.xml @@ -10,7 +10,7 @@ Date: Tue, 11 Nov 2014 16:21:07 -0800 Subject: [PATCH 112/415] add dev icon burning --- android/java/app/src/main/AndroidManifest.xml | 2 +- .../main/res/drawable-hdpi/icon_burned.png | Bin 0 -> 4664 bytes .../main/res/drawable-mdpi/icon_burned.png | Bin 0 -> 2437 bytes .../main/res/drawable-xhdpi/icon_burned.png | Bin 0 -> 6769 bytes .../main/res/drawable-xxhdpi/icon_burned.png | Bin 0 -> 11952 bytes update_icons.sh | 23 ++++++++++++++++++ 6 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 android/java/app/src/main/res/drawable-hdpi/icon_burned.png create mode 100644 android/java/app/src/main/res/drawable-mdpi/icon_burned.png create mode 100644 android/java/app/src/main/res/drawable-xhdpi/icon_burned.png create mode 100644 android/java/app/src/main/res/drawable-xxhdpi/icon_burned.png create mode 100755 update_icons.sh diff --git a/android/java/app/src/main/AndroidManifest.xml b/android/java/app/src/main/AndroidManifest.xml index a0bb4c08a..ffc80dbc1 100644 --- a/android/java/app/src/main/AndroidManifest.xml +++ b/android/java/app/src/main/AndroidManifest.xml @@ -10,7 +10,7 @@ Fp~aRV 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 literal 0 HcmV?d00001 diff --git a/android/java/app/src/main/res/drawable-mdpi/icon_burned.png b/android/java/app/src/main/res/drawable-mdpi/icon_burned.png new file mode 100644 index 0000000000000000000000000000000000000000..115792242066a5c06f513987e627af1e64f34e38 GIT binary patch literal 2437 zcmV;033~R4P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv0RI600RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_00@UkL_t(o!|hmGY+P3r zUHjZSGoFXX?N2BJ_kP-Z=oHlo+SWCRhp5!s9Tx?1B`s$`7 z-Hlz7g|{bA1d*x|u|kTU@9FRS!gW1L?u;_;Kt+4;A(u8~j&3+uIK-&i4tb_Py6fB*xWt!zFrx!@>h zsQY_kL*G9>obd#SA3is?6vi3TcK)^lEZLdz zURqfH{iS8chazMOKms*9@{=8`02!(S2jXV(C8y4^Q9pA$@sqv zVA@|i?GcbAv%WXI9y~TRzx^8u@be3c6RWj+%Cqfg`Q0+4L?PupJ+pXX>8cS}f`k#d zSPh<-TFCm+cB$%zj6C$DCz3LfGA838W4x3R-ymhg7n3rQ5`6IRP+uW;(?IXcQsu8_ zr#!(Y$8M+wQCBuIvs5W|WOA8Qf3fi5H{PEcb6q!zIf%{k;A7i&A&FckV*M# zwJ@-1RjtwzmUK8+zws97%g94d8doxrv?m#3Ql5Aa&%hV)MLZ*S9o)Y!o9RgT&y7!; zriJ>JY?JYpymO;o>bIn%RmOxRV`U6lWvoyjR1H-Fn^e=1+m^7b(?|gc1*`zdmoLtF zBA&oV!wVa`k)3$Z44QSCEg35;piU2fk_lDZV#%*ZNxi5fL_whlD9(D7G*F0fE8=>i zZ2@xjC=Ec{7Kch$(kizI+bY$TirB27zh`2hvJpk=wXi$a zQOu;*f>3~^jhd2ug&cuGCRN!Cd-K^JzjV4q)!P^%5yn~#qrqZcAkUmRzY)fR#llmk z#xE?b8j)X~nmE+m^~{;cTq;FiuvmEFFFZG_lyy7m7cAv z-G6*!sH;$mZ7sHwOO;RGbp2WoTIKZ0+Wp6G7%CP1F+2Ct>|D&GI+4;6!`($o`o@g* z_taurF61T`EBB3EzaB(EtXAn%v0c+i+rx;sCijha1_JQ_5itURh`0@PB0|Td+upk^ zesh{>ee>+u>2H7F_>W#Zb@#!6Vn@0&ow~GE zUEZi2EOqY7WtOV7h3e*^?#_(ojW4W>_H_rbed_c$>bBD89&IXOeYm~VEK{)IU8=Bz zC9LAc^1jFZd9Iw#)nYqe-E<@AU{_};+i}-$e>I8+i}}@BxUf+>*1zx36XWGV?yiFa zKYQhDxsW3zR@kO&mUSOOs}GqbvXhyo%`+%*eLQ#m(${V~dZ1LO)WW5Wni8XZr62tL zL@kalEU&%x=JKN_&OLr|;#j%+`KilazkPJLR9LNrmbe;5^&#&7u>nQvTdU^7-y(>j zLO={j*B}tk$sGfoIR&4ex)j&j^~B|cuiQGaP_21Jl(_fEP%h;!Z3O+DITC+6e`&U| z{=nU1SHmb$HvWOBKeKr-fLom}BM&|4rcYCQCqt>R%W9n2RA9Dzf^Ane$D+O=*%`~m zYNYOvbx{)#f&u}_dQ#b$j$29jwt%GhEw3HhjHbC{jg?~Eaip2ZYaDO29BM0;jqGB7$bH!UzYIxsMR&{*~W001R)MObuX zVRU6WZEs|0W_bWIFflYOF)=MMF;p=&Ix;XiGdC?TI65#e@r~e000000NkvXXu0mjf DB9@qH literal 0 HcmV?d00001 diff --git a/android/java/app/src/main/res/drawable-xhdpi/icon_burned.png b/android/java/app/src/main/res/drawable-xhdpi/icon_burned.png new file mode 100644 index 0000000000000000000000000000000000000000..9d3972c7514b6e6ad0dd0dd19ec89f209ac5273b GIT binary patch literal 6769 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 TQ7ft_0YF7jQ=wAcJotYARR0BQ literal 0 HcmV?d00001 diff --git a/android/java/app/src/main/res/drawable-xxhdpi/icon_burned.png b/android/java/app/src/main/res/drawable-xxhdpi/icon_burned.png new file mode 100644 index 0000000000000000000000000000000000000000..f4d2a89ce46187103c2d5ae7c3d678ab0d66e714 GIT binary patch literal 11952 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 Date: Wed, 12 Nov 2014 21:17:24 +1100 Subject: [PATCH 113/415] Renamed source files using mapbox style Move headers to mbgl/android Tidy up headers and includes --- android/cpp/{JNI.cpp => jni.cpp} | 7 ++-- ...{NativeMapView.cpp => native_map_view.cpp} | 9 +++-- android/mapboxgl-app.gyp | 4 +- include/mbgl/android/jni.hpp | 37 +++++++++++++++++ .../cpp/log.h => include/mbgl/android/log.hpp | 6 +-- .../mbgl/android/native_map_view.hpp | 40 +++---------------- platform/android/cache_database_data.cpp | 2 +- platform/default/http_request_baton_curl.cpp | 4 +- 8 files changed, 61 insertions(+), 48 deletions(-) rename android/cpp/{JNI.cpp => jni.cpp} (99%) rename android/cpp/{NativeMapView.cpp => native_map_view.cpp} (99%) create mode 100644 include/mbgl/android/jni.hpp rename android/cpp/log.h => include/mbgl/android/log.hpp (94%) rename android/cpp/NativeMapView.hpp => include/mbgl/android/native_map_view.hpp (68%) diff --git a/android/cpp/JNI.cpp b/android/cpp/jni.cpp similarity index 99% rename from android/cpp/JNI.cpp rename to android/cpp/jni.cpp index 708063f24..45b8f7fd2 100644 --- a/android/cpp/JNI.cpp +++ b/android/cpp/jni.cpp @@ -11,9 +11,10 @@ #include -#include "log.h" - -#include "NativeMapView.hpp" +#include +#include +#include +#include #pragma clang diagnostic ignored "-Wunused-parameter" diff --git a/android/cpp/NativeMapView.cpp b/android/cpp/native_map_view.cpp similarity index 99% rename from android/cpp/NativeMapView.cpp rename to android/cpp/native_map_view.cpp index 5644b284d..ab06275f0 100644 --- a/android/cpp/NativeMapView.cpp +++ b/android/cpp/native_map_view.cpp @@ -5,12 +5,13 @@ #include +#include +#include + +#include #include #include - -#include "log.h" - -#include "NativeMapView.hpp" +#include namespace mbgl { namespace android { diff --git a/android/mapboxgl-app.gyp b/android/mapboxgl-app.gyp index ac8937fb3..118669542 100644 --- a/android/mapboxgl-app.gyp +++ b/android/mapboxgl-app.gyp @@ -8,8 +8,8 @@ 'product_name': 'mapbox-gl', 'type': 'shared_library', 'sources': [ - './cpp/NativeMapView.cpp', - './cpp/JNI.cpp', + './cpp/native_map_view.cpp', + './cpp/jni.cpp', ], 'cflags_cc': [ '-I<(boost_root)/include', diff --git a/include/mbgl/android/jni.hpp b/include/mbgl/android/jni.hpp new file mode 100644 index 000000000..c1af07c64 --- /dev/null +++ b/include/mbgl/android/jni.hpp @@ -0,0 +1,37 @@ +#ifndef MBGL_ANDROID_JNI +#define MBGL_ANDROID_JNI + +#include +#include + +namespace mbgl { +namespace android { + +extern std::string cache_path; +extern std::string data_path; + +extern jmethodID on_map_changed_id; + +extern jclass lon_lat_class; +extern jmethodID lon_lat_constructor_id; +extern jfieldID lon_lat_lon_id; +extern jfieldID lon_lat_lat_id; + +extern jclass lon_lat_zoom_class; +extern jmethodID lon_lat_zoom_constructor_id; +extern jfieldID lon_lat_zoom_lon_id; +extern jfieldID lon_lat_zoom_lat_id; +extern jfieldID lon_lat_zoom_zoom_id; + +extern jclass runtime_exception_class; + +extern jmethodID list_to_array_id; + +extern jclass array_list_class; +extern jmethodID array_list_constructor_id; +extern jmethodID array_list_add_id; + +} +} + +#endif diff --git a/android/cpp/log.h b/include/mbgl/android/log.hpp similarity index 94% rename from android/cpp/log.h rename to include/mbgl/android/log.hpp index 3cf203853..50b6bec1e 100644 --- a/android/cpp/log.h +++ b/include/mbgl/android/log.hpp @@ -1,5 +1,5 @@ -#ifndef LOG_H -#define LOG_H +#ifndef MBGL_ANDROID_LOG +#define MBGL_ANDROID_LOG #include @@ -22,4 +22,4 @@ #define LOG_WARN(fmt, args...) __android_log_print(ANDROID_LOG_WARN, __FILE__ ":" S__LINE__, fmt, ##args) #define LOG_ERROR(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, __FILE__ ":" S__LINE__, fmt, ##args) -#endif // LOG_H +#endif diff --git a/android/cpp/NativeMapView.hpp b/include/mbgl/android/native_map_view.hpp similarity index 68% rename from android/cpp/NativeMapView.hpp rename to include/mbgl/android/native_map_view.hpp index 75aba6985..04949abe1 100644 --- a/android/cpp/NativeMapView.hpp +++ b/include/mbgl/android/native_map_view.hpp @@ -1,43 +1,15 @@ -#ifndef MAP_VIEW_HPP -#define MAP_VIEW_HPP +#ifndef MBGL_ANDROID_NATIVE_MAP_VIEW +#define MBGL_ANDROID_NATIVE_MAP_VIEW +#include #include - #include - #include - #include -#include - namespace mbgl { namespace android { -extern std::string cache_path; -extern std::string data_path; - -extern jmethodID on_map_changed_id; - -extern jclass lon_lat_class; -extern jmethodID lon_lat_constructor_id; -extern jfieldID lon_lat_lon_id; -extern jfieldID lon_lat_lat_id; - -extern jclass lon_lat_zoom_class; -extern jmethodID lon_lat_zoom_constructor_id; -extern jfieldID lon_lat_zoom_lon_id; -extern jfieldID lon_lat_zoom_lat_id; -extern jfieldID lon_lat_zoom_zoom_id; - -extern jclass runtime_exception_class; - -extern jmethodID list_to_array_id; - -extern jclass array_list_class; -extern jmethodID array_list_constructor_id; -extern jmethodID array_list_add_id; - class MBGLView; class NativeMapView { @@ -116,7 +88,7 @@ private: NativeMapView* nativeView = nullptr; }; -} // namespace android -} // namespace mbgl +} +} -#endif // MAP_VIEW_HPP +#endif diff --git a/platform/android/cache_database_data.cpp b/platform/android/cache_database_data.cpp index 61c4efc15..5f13a44f6 100644 --- a/platform/android/cache_database_data.cpp +++ b/platform/android/cache_database_data.cpp @@ -1,5 +1,5 @@ #include -#include "../../android/cpp/NativeMapView.hpp" +#include namespace mbgl { namespace platform { diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index 5238283fd..a88b949e0 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -1,8 +1,10 @@ +#include #include #include #include + #ifdef __ANDROID__ - #include "../../android/cpp/NativeMapView.hpp" + #include #endif #include From 49da2c2fa5e6419df383b3358e63f2efcc939119 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 12 Nov 2014 22:00:44 +1100 Subject: [PATCH 114/415] Tidy code up more --- android/cpp/jni.cpp | 99 +++++++++--------- android/cpp/native_map_view.cpp | 127 ++++++++++------------- include/mbgl/android/native_map_view.hpp | 50 ++++----- 3 files changed, 131 insertions(+), 145 deletions(-) diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index 45b8f7fd2..8b3780811 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #pragma clang diagnostic ignored "-Wunused-parameter" @@ -242,49 +243,49 @@ void JNICALL nativeRun(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeRun"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->run(); + native_map_view->getMap().run(); } void JNICALL nativeRerender(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeRerender"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->rerender(); + native_map_view->getMap().rerender(); } void JNICALL nativeUpdate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeUpdate"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->update(); + native_map_view->getMap().update(); } void JNICALL nativeCleanup(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeCleanup"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->cleanup(); + native_map_view->getMap().cleanup(); } void JNICALL nativeTerminate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeTerminate"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->terminate(); + native_map_view->getMap().terminate(); } jboolean JNICALL nativeNeedsSwap(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeNeedsSwap"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap()->needsSwap(); + return native_map_view->getMap().needsSwap(); } void JNICALL nativeSwapped(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeSwapped"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->swapped(); + native_map_view->getMap().swapped(); } void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio) { @@ -295,7 +296,7 @@ void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, j LOG_ASSERT(width <= UINT16_MAX); LOG_ASSERT(height <= UINT16_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->resize(width, height, ratio); + native_map_view->getMap().resize(width, height, ratio); } void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio, jint fb_width, jint fb_height) { @@ -310,21 +311,21 @@ void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, j LOG_ASSERT(fb_width <= UINT16_MAX); LOG_ASSERT(fb_height <= UINT16_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->resize(width, height, ratio, fb_width, fb_height); + native_map_view->getMap().resize(width, height, ratio, fb_width, fb_height); } void JNICALL nativeSetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject classes) { LOG_VERBOSE("nativeSetAppliedClasses"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setAppliedClasses(std_vector_string_from_jobject(env, classes)); + native_map_view->getMap().setAppliedClasses(std_vector_string_from_jobject(env, classes)); } jobject JNICALL nativeGetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeGetAppliedClasses"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return std_vector_string_to_jobject(env, native_map_view->getMap()->getAppliedClasses()); + return std_vector_string_to_jobject(env, native_map_view->getMap().getAppliedClasses()); } void JNICALL nativeSetDefaultTransitionDuration(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jlong duration_milliseconds) { @@ -333,56 +334,56 @@ void JNICALL nativeSetDefaultTransitionDuration(JNIEnv* env, jobject obj, jlong LOG_ASSERT(duration_milliseconds >= 0); //LOG_ASSERT(duration_milliseconds <= UINT64_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setDefaultTransitionDuration(duration_milliseconds); + native_map_view->getMap().setDefaultTransitionDuration(duration_milliseconds); } void JNICALL nativeSetStyleURL(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring url) { LOG_VERBOSE("nativeSetStyleURL"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setStyleURL(std_string_from_jstring(env, url)); + native_map_view->getMap().setStyleURL(std_string_from_jstring(env, url)); } void JNICALL nativeSetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring new_style_json, jstring base) { LOG_VERBOSE("nativeSetStyleJSON"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setStyleJSON(std_string_from_jstring(env, new_style_json), std_string_from_jstring(env, base)); + native_map_view->getMap().setStyleJSON(std_string_from_jstring(env, new_style_json), std_string_from_jstring(env, base)); } jstring JNICALL nativeGetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeGetStyleJSON"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return std_string_to_jstring(env, native_map_view->getMap()->getStyleJSON()); + return std_string_to_jstring(env, native_map_view->getMap().getStyleJSON()); } void JNICALL nativeSetAccessToken(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring access_token) { LOG_VERBOSE("nativeSetAccessToken"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setAccessToken(std_string_from_jstring(env, access_token)); + native_map_view->getMap().setAccessToken(std_string_from_jstring(env, access_token)); } jstring JNICALL nativeGetAccessToken(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeGetAccessToken"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return std_string_to_jstring(env, native_map_view->getMap()->getAccessToken()); + return std_string_to_jstring(env, native_map_view->getMap().getAccessToken()); } void JNICALL nativeCancelTransitions(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeCancelTransitions"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->cancelTransitions(); + native_map_view->getMap().cancelTransitions(); } void JNICALL nativeMoveBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble dx, jdouble dy, jdouble duration) { LOG_VERBOSE("nativeMoveBy"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->moveBy(dx, dy, duration); + native_map_view->getMap().moveBy(dx, dy, duration); } void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat, jdouble duration) { @@ -404,7 +405,7 @@ void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr // TODO remove when fixed LOG_DEBUG("lon lat %f %f", lon, lat); - native_map_view->getMap()->setLonLat(lon, lat, duration); + native_map_view->getMap().setLonLat(lon, lat, duration); } jobject JNICALL nativeGetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { @@ -412,7 +413,7 @@ jobject JNICALL nativeGetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); double lon, lat; - native_map_view->getMap()->getLonLat(lon, lat); + native_map_view->getMap().getLonLat(lon, lat); jobject ret = env->NewObject(lon_lat_class, lon_lat_constructor_id, lon, lat); if (ret == nullptr) { @@ -427,56 +428,56 @@ void JNICALL nativeStartPanning(JNIEnv* env, jobject obj, jlong native_map_view_ LOG_VERBOSE("nativeStartPanning"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->startPanning(); + native_map_view->getMap().startPanning(); } void JNICALL nativeStopPanning(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeStopPanning"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->stopPanning(); + native_map_view->getMap().stopPanning(); } void JNICALL nativeResetPosition(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeResetPosition"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->resetPosition(); + native_map_view->getMap().resetPosition(); } void JNICALL nativeScaleBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble ds, jdouble cx, jdouble cy, jdouble duration) { LOG_VERBOSE("nativeScaleBy"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->scaleBy(ds, cx, cy, duration); + native_map_view->getMap().scaleBy(ds, cx, cy, duration); } void JNICALL nativeSetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble scale, jdouble cx, jdouble cy, jdouble duration) { LOG_VERBOSE("nativeSetScale"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setScale(scale, cx, cy, duration); + native_map_view->getMap().setScale(scale, cx, cy, duration); } jdouble JNICALL nativeGetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeGetScale"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap()->getScale(); + return native_map_view->getMap().getScale(); } void JNICALL nativeSetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble zoom, jdouble duration) { LOG_VERBOSE("nativeSetZoom"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setZoom(zoom, duration); + native_map_view->getMap().setZoom(zoom, duration); } jdouble JNICALL nativeGetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeGetZoom"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap()->getZoom(); + return native_map_view->getMap().getZoom(); } void JNICALL nativeSetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat_zoom, jdouble duration) { @@ -502,7 +503,7 @@ void JNICALL nativeSetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view return; } - native_map_view->getMap()->setLonLatZoom(lon, lat, zoom, duration); + native_map_view->getMap().setLonLatZoom(lon, lat, zoom, duration); } jobject JNICALL nativeGetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { @@ -510,7 +511,7 @@ jobject JNICALL nativeGetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_v LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); double lon, lat, zoom; - native_map_view->getMap()->getLonLatZoom(lon, lat, zoom); + native_map_view->getMap().getLonLatZoom(lon, lat, zoom); jobject ret = env->NewObject(lon_lat_zoom_class, lon_lat_zoom_constructor_id, lon, lat, zoom); if (ret == nullptr) { @@ -525,119 +526,119 @@ void JNICALL nativeResetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr LOG_VERBOSE("nativeResetZoom"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->resetZoom(); + native_map_view->getMap().resetZoom(); } void JNICALL nativeStartScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeStartScaling"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->startScaling(); + native_map_view->getMap().startScaling(); } void JNICALL nativeStopScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeStopScaling"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->stopScaling(); + native_map_view->getMap().stopScaling(); } jdouble JNICALL nativeGetMinZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeGetMinZoom"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap()->getMinZoom(); + return native_map_view->getMap().getMinZoom(); } jdouble JNICALL nativeGetMaxZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeGetMaxZoom"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap()->getMaxZoom(); + return native_map_view->getMap().getMaxZoom(); } void JNICALL nativeRotateBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble sx, jdouble sy, jdouble ex, jdouble ey, jdouble duration) { LOG_VERBOSE("nativeRotateBy"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->rotateBy(sx, sy, ex, ey, duration); + native_map_view->getMap().rotateBy(sx, sy, ex, ey, duration); } void JNICALL nativeSetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble degrees, jdouble duration) { LOG_VERBOSE("nativeSetBearing"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setBearing(degrees, duration); + native_map_view->getMap().setBearing(degrees, duration); } void JNICALL nativeSetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble degrees, jdouble cx, jdouble cy) { LOG_VERBOSE("nativeSetBearing"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setBearing(degrees, cx, cy); + native_map_view->getMap().setBearing(degrees, cx, cy); } jdouble JNICALL nativeGetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeGetBearing"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap()->getBearing(); + return native_map_view->getMap().getBearing(); } void JNICALL nativeResetNorth(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeResetNorth"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->resetNorth(); + native_map_view->getMap().resetNorth(); } void JNICALL nativeStartRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeStartRotating"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->startRotating(); + native_map_view->getMap().startRotating(); } void JNICALL nativeStopRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeStopRotating"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->stopRotating(); + native_map_view->getMap().stopRotating(); } jboolean JNICALL nativeCanRotate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeCanRotate"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap()->canRotate(); + return native_map_view->getMap().canRotate(); } void JNICALL nativeSetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jboolean debug) { LOG_VERBOSE("nativeSetDebug"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setDebug(debug); + native_map_view->getMap().setDebug(debug); } void JNICALL nativeToggleDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeToggleDebug"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->toggleDebug(); + native_map_view->getMap().toggleDebug(); } jboolean JNICALL nativeGetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { LOG_VERBOSE("nativeGetDebug"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap()->getDebug(); + return native_map_view->getMap().getDebug(); } void JNICALL nativeSetReachability(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jboolean status) { LOG_VERBOSE("nativeSetReachability"); LOG_ASSERT(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap()->setReachability(status); + native_map_view->getMap().setReachability(status); } } // namespace @@ -645,6 +646,8 @@ void JNICALL nativeSetReachability(JNIEnv* env, jobject obj, jlong native_map_vi extern "C" { extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { + mbgl::Log::Set(); + LOG_VERBOSE("JNI_OnLoad"); JNIEnv* env = nullptr; diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp index ab06275f0..f976d7d96 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -8,9 +8,7 @@ #include #include -#include #include -#include #include namespace mbgl { @@ -34,7 +32,55 @@ void log_gl_string(GLenum name, const char* label) { } } -NativeMapView::NativeMapView(JNIEnv* env, jobject obj_) { + +void MBGLView::make_active() { + LOG_VERBOSE("MBGLView::make_active"); + if ((nativeView.display != EGL_NO_DISPLAY) + && (nativeView.surface != EGL_NO_SURFACE) + && (nativeView.context != EGL_NO_CONTEXT)) { + if (!eglMakeCurrent(nativeView.display, nativeView.surface, + nativeView.surface, nativeView.context)) { + LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); + } + } else { + LOG_DEBUG("Not activating as we are not ready"); + } +} + +void MBGLView::make_inactive() { + LOG_VERBOSE("MBGLView::make_inactive"); + if (!eglMakeCurrent(nativeView.display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) { + LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + eglGetError()); + } +} + +void MBGLView::swap() { + LOG_VERBOSE("MBGLView::swap"); + + if (map->needsSwap() && (nativeView.display != EGL_NO_DISPLAY) + && (nativeView.surface != EGL_NO_SURFACE)) { + if (!eglSwapBuffers(nativeView.display, nativeView.surface)) { + LOG_ERROR("eglSwapBuffers() returned error %d", eglGetError()); + } + map->swapped(); + } else { + LOG_DEBUG("Not swapping as we are not ready"); + } +} + +void MBGLView::notify() { + LOG_VERBOSE("MBGLView::notify()"); + // noop +} + +void MBGLView::notify_map_change(mbgl::MapChange /* change */, mbgl::timestamp /* delay */) { + LOG_VERBOSE("MBGLView::notify_map_change()"); + nativeView.notifyMapChange(); +} + +NativeMapView::NativeMapView(JNIEnv* env, jobject obj_) : map(view), view(*this) { LOG_VERBOSE("NativeMapView::NativeMapView"); LOG_ASSERT(env != nullptr); @@ -50,11 +96,6 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_) { env->ExceptionDescribe(); return; } - - mbgl::Log::Set(); - - view = new MBGLView(this); - map = new mbgl::Map(*view); } NativeMapView::~NativeMapView() { @@ -63,12 +104,6 @@ NativeMapView::~NativeMapView() { destroySurface(); terminateDisplay(); - delete map; - map = nullptr; - - delete view; - view = nullptr; - LOG_ASSERT(vm != nullptr); LOG_ASSERT(obj != nullptr); @@ -168,8 +203,8 @@ bool NativeMapView::initializeDisplay() { } } - // TODO make_unique missing in Android NDK? -// const std::unique_ptr configs = std::make_unique(new EGLConfig[num_configs]); + // TODO: make_unique is missing in Android NDK? + // const std::unique_ptr configs = std::make_unique(new EGLConfig[num_configs]); const std::unique_ptr configs(new EGLConfig[num_configs]); if (!eglChooseConfig(display, config_attribs, configs.get(), num_configs, &num_configs)) { @@ -198,8 +233,6 @@ bool NativeMapView::initializeDisplay() { void NativeMapView::terminateDisplay() { LOG_VERBOSE("NativeMapView::terminateDisplay"); - //map->terminate(); - if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { @@ -244,8 +277,6 @@ bool NativeMapView::initializeContext() { void NativeMapView::terminateContext() { LOG_VERBOSE("NativeMapView::terminateContext"); - //map->terminate(); - if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { @@ -480,7 +511,7 @@ void NativeMapView::start() { LOG_ASSERT(display != EGL_NO_DISPLAY); LOG_ASSERT(context != EGL_NO_CONTEXT); - map->start(true); + map.start(true); } void NativeMapView::loadExtensions() { @@ -515,7 +546,7 @@ void NativeMapView::stop() { LOG_ASSERT(display != EGL_NO_DISPLAY); LOG_ASSERT(context != EGL_NO_CONTEXT); - map->stop(); + map.stop(); } @@ -525,7 +556,7 @@ void NativeMapView::pause(bool wait_for_pause) { LOG_ASSERT(display != EGL_NO_DISPLAY); LOG_ASSERT(context != EGL_NO_CONTEXT); - map->pause(wait_for_pause); + map.pause(wait_for_pause); } void NativeMapView::resume() { @@ -535,7 +566,7 @@ void NativeMapView::resume() { LOG_ASSERT(context != EGL_NO_CONTEXT); if (surface != EGL_NO_SURFACE) { - map->resume(); + map.resume(); } else { LOG_DEBUG("Not resuming because we are not ready"); } @@ -553,7 +584,6 @@ void NativeMapView::notifyMapChange() { NULL }; - // TODO do this only once per thread? jint ret; JNIEnv* env = nullptr; bool detach = false; @@ -584,52 +614,5 @@ void NativeMapView::notifyMapChange() { env = nullptr; } -void MBGLView::make_active() { - LOG_VERBOSE("MBGLView::make_active"); - if ((nativeView->display != EGL_NO_DISPLAY) - && (nativeView->surface != EGL_NO_SURFACE) - && (nativeView->context != EGL_NO_CONTEXT)) { - if (!eglMakeCurrent(nativeView->display, nativeView->surface, - nativeView->surface, nativeView->context)) { - LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); - } - } else { - LOG_DEBUG("Not activating as we are not ready"); - } } - -void MBGLView::make_inactive() { - LOG_VERBOSE("MBGLView::make_inactive"); - if (!eglMakeCurrent(nativeView->display, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT)) { - LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", - eglGetError()); - } } - -void MBGLView::swap() { - LOG_VERBOSE("MBGLView::swap"); - - if (map->needsSwap() && (nativeView->display != EGL_NO_DISPLAY) - && (nativeView->surface != EGL_NO_SURFACE)) { - if (!eglSwapBuffers(nativeView->display, nativeView->surface)) { - LOG_ERROR("eglSwapBuffers() returned error %d", eglGetError()); - } - map->swapped(); - } else { - LOG_DEBUG("Not swapping as we are not ready"); - } -} - -void MBGLView::notify() { - LOG_VERBOSE("MBGLView::notify()"); - // noop -} - -void MBGLView::notify_map_change(mbgl::MapChange /* change */, mbgl::timestamp /* delay */) { - LOG_VERBOSE("MBGLView::notify_map_change()"); - nativeView->notifyMapChange(); -} - -} // namespace android -} // namespace mbgl diff --git a/include/mbgl/android/native_map_view.hpp b/include/mbgl/android/native_map_view.hpp index 04949abe1..e2daab939 100644 --- a/include/mbgl/android/native_map_view.hpp +++ b/include/mbgl/android/native_map_view.hpp @@ -1,7 +1,9 @@ #ifndef MBGL_ANDROID_NATIVE_MAP_VIEW #define MBGL_ANDROID_NATIVE_MAP_VIEW +#include #include +#include #include #include #include @@ -10,16 +12,34 @@ namespace mbgl { namespace android { -class MBGLView; +class NativeMapView; -class NativeMapView { +class MBGLView: public mbgl::View, private mbgl::util::noncopyable { +public: + explicit MBGLView(NativeMapView& nativeView_) : + nativeView(nativeView_) { + } + + void make_active() override; + void make_inactive() override; + + void swap() override; + + void notify() override; + void notify_map_change(mbgl::MapChange change, mbgl::timestamp delay = 0) override; + +private: + NativeMapView& nativeView; +}; + +class NativeMapView : private mbgl::util::noncopyable { friend class MBGLView; public: NativeMapView(JNIEnv* env, jobject obj); ~NativeMapView(); - mbgl::Map* getMap() const { + mbgl::Map& getMap() { return map; } @@ -52,8 +72,8 @@ private: ANativeWindow* window = nullptr; - mbgl::Map* map = nullptr; - MBGLView* view = nullptr; + mbgl::Map map; + MBGLView view; EGLDisplay display = EGL_NO_DISPLAY; EGLSurface surface = EGL_NO_SURFACE; @@ -68,26 +88,6 @@ private: bool first_time = false; }; -class MBGLView: public mbgl::View { -public: - MBGLView(NativeMapView* nativeView_) : - nativeView(nativeView_) { - } - virtual ~MBGLView() { - } - - void make_active() override; - void make_inactive() override; - - void swap() override; - - void notify() override; - void notify_map_change(mbgl::MapChange change, mbgl::timestamp delay = 0) override; - -private: - NativeMapView* nativeView = nullptr; -}; - } } From 11061ee5da02650e36abbc4f558446db793d3df2 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 12 Nov 2014 22:01:32 +1100 Subject: [PATCH 115/415] Add noncopyable --- include/mbgl/platform/default/glfw_view.hpp | 3 ++- include/mbgl/platform/default/headless_display.hpp | 3 ++- include/mbgl/platform/default/headless_view.hpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/mbgl/platform/default/glfw_view.hpp b/include/mbgl/platform/default/glfw_view.hpp index 6e91c1125..c63b205a4 100644 --- a/include/mbgl/platform/default/glfw_view.hpp +++ b/include/mbgl/platform/default/glfw_view.hpp @@ -3,13 +3,14 @@ #include #include +#include #ifdef NVIDIA #define GLFW_INCLUDE_ES2 #endif #include -class GLFWView : public mbgl::View { +class GLFWView : public mbgl::View, private mbgl::util::noncopyable { public: GLFWView(bool fullscreen = false); ~GLFWView(); diff --git a/include/mbgl/platform/default/headless_display.hpp b/include/mbgl/platform/default/headless_display.hpp index 5b33fd699..cd6cb4f75 100644 --- a/include/mbgl/platform/default/headless_display.hpp +++ b/include/mbgl/platform/default/headless_display.hpp @@ -1,11 +1,12 @@ #ifndef MBGL_COMMON_HEADLESS_DISPLAY #define MBGL_COMMON_HEADLESS_DISPLAY +#include #include "headless_view.hpp" namespace mbgl { -class HeadlessDisplay { +class HeadlessDisplay, private mbgl::util::noncopyable { public: HeadlessDisplay(); ~HeadlessDisplay(); diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp index f599dde27..0a058552e 100644 --- a/include/mbgl/platform/default/headless_view.hpp +++ b/include/mbgl/platform/default/headless_view.hpp @@ -9,6 +9,7 @@ #define MBGL_USE_GLX 1 #endif +#include #include #include @@ -18,7 +19,7 @@ namespace mbgl { class HeadlessDisplay; -class HeadlessView : public View { +class HeadlessView : public View, private util::noncopyable { public: HeadlessView(); HeadlessView(std::shared_ptr display); From 82908c99f779c4d5771b4eb315b2283db4c1f046 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 12 Nov 2014 22:03:27 +1100 Subject: [PATCH 116/415] More tidy up --- android/cpp/jni.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index 8b3780811..82b78f817 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -132,16 +132,13 @@ jobject std_vector_string_to_jobject(JNIEnv* env, std::vector vecto return jlist; } -} // namespace android -} // namespace mbgl +} +} namespace { using namespace mbgl::android; -// TODO: wrap C++ exceptions? -// TODO: wrap other sorts of exceptions? eg coffee catch - jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_, jstring data_path_) { LOG_VERBOSE("nativeCreate"); cache_path = std_string_from_jstring(env, cache_path_); @@ -332,7 +329,6 @@ void JNICALL nativeSetDefaultTransitionDuration(JNIEnv* env, jobject obj, jlong LOG_VERBOSE("nativeSetDefaultTransitionDuration"); LOG_ASSERT(native_map_view_ptr != 0); LOG_ASSERT(duration_milliseconds >= 0); - //LOG_ASSERT(duration_milliseconds <= UINT64_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setDefaultTransitionDuration(duration_milliseconds); } @@ -403,8 +399,6 @@ void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr return; } - // TODO remove when fixed - LOG_DEBUG("lon lat %f %f", lon, lat); native_map_view->getMap().setLonLat(lon, lat, duration); } @@ -641,7 +635,7 @@ void JNICALL nativeSetReachability(JNIEnv* env, jobject obj, jlong native_map_vi native_map_view->getMap().setReachability(status); } -} // namespace +} extern "C" { @@ -895,4 +889,4 @@ extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { array_list_add_id = nullptr; } -} // extern "C" +} From 7877d0505e25dd4d2f636b3dba8605a8519c9e9e Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 12 Nov 2014 23:17:07 +1100 Subject: [PATCH 117/415] Fix crash --- android/cpp/native_map_view.cpp | 2 +- include/mbgl/android/native_map_view.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp index f976d7d96..ab3034a18 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -80,7 +80,7 @@ void MBGLView::notify_map_change(mbgl::MapChange /* change */, mbgl::timestamp / nativeView.notifyMapChange(); } -NativeMapView::NativeMapView(JNIEnv* env, jobject obj_) : map(view), view(*this) { +NativeMapView::NativeMapView(JNIEnv* env, jobject obj_) : view(*this), map(view) { LOG_VERBOSE("NativeMapView::NativeMapView"); LOG_ASSERT(env != nullptr); diff --git a/include/mbgl/android/native_map_view.hpp b/include/mbgl/android/native_map_view.hpp index e2daab939..dee6a2226 100644 --- a/include/mbgl/android/native_map_view.hpp +++ b/include/mbgl/android/native_map_view.hpp @@ -72,8 +72,8 @@ private: ANativeWindow* window = nullptr; + MBGLView view; // Must be before map mbgl::Map map; - MBGLView view; EGLDisplay display = EGL_NO_DISPLAY; EGLSurface surface = EGL_NO_SURFACE; From 43833851393956544f5c9cdfd047d081485d1ab3 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 12 Nov 2014 10:22:45 -0800 Subject: [PATCH 118/415] add default, unburned icons to satisfy Studio --- .../main/res/drawable-hdpi/icon_burned.png | Bin 4664 -> 2546 bytes .../main/res/drawable-mdpi/icon_burned.png | Bin 2437 -> 2015 bytes .../main/res/drawable-xhdpi/icon_burned.png | Bin 6769 -> 3358 bytes .../main/res/drawable-xxhdpi/icon_burned.png | Bin 11952 -> 10233 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/android/java/app/src/main/res/drawable-hdpi/icon_burned.png b/android/java/app/src/main/res/drawable-hdpi/icon_burned.png index 6e8bf4aa2243f796b9b0035414d754d98a0c3f26..f9875161da4b31ec5c968910683886c619491366 100644 GIT binary patch literal 2546 zcmZuzc|6nqAOD#9UiHmUsx@7XnH-61VH9%BF@(%*vcoQ0#FA2(Bb5r3BhfJTkw#i6 z%+WzhzP>~ULglEW<2xPSZ@+%u_v7)tp0DTgb-h1-e2%*~?N*dqB?kb2qMfa^t7x}f znle(N)nlr9w`dcixbC(BxZN7#qKhoa)|&zV@++367?72t1^^&^oVyp*%hACsh=|t< z2qp%G=rQml5gP!^8D^p*K7<+oVc;VP6f*__`i)^GIxoR+DC8T3x*q}ca&&>9h~yB6 zv7V8hJ`^bjfk4d3!J%fZ*1LYlMK=UAoJu8`!Qs)-(R$H_dPH&bgS2Gk5Pa=mZ+ENpcCI&wQzk_TOQSy&6gm zEYv<^+ozG8C*x%yg{olbLd3;Tb+r)rHj9>YJgg050M-xok*{JYNGS9_O891`cc%M$ z*0~j&9^Gzb2(v}YA@RwY;rdXe2Gn{*keqxfywisJbmygG_4eV%SPo?uaKvz|Q{yYEYTXPa}q z0r_Kh&4xfU`|}{`LEn{X$^OjAbj0V9U?1nAQbMvvO}|P?+w;y+4dkiiN?_A6s>>?r z#O~>R12E-sJKco0ji%`W!An(Q`+bVZY5o%%UPopTE_e1wg2tcQw)7D9ZEgbG_DJm* zQ9I^d2BkgkBNV1;tI399XV{WQpd%@3CtAZy>8VvYs`X0zuKbxt>uzNn8*SGnT>CJ_ zR~+1Up-b?pqW7%WF)IOWDv){DA#JvzS4-luxRi zj%#Sq^}V8^nc2hdXdFd{AkdpEtlZ3=)%6xXEZIhnZj<+s4we%WD)w`RR{EpwWwZ7p z-nW<;%ic@DnFiROJDm5iuxxC6aWOdB-)hq`-uT7me4{%jr`nzRZ(NHHP~qr$e#se% zPg0gS7_2dV+x^o->UtrEGp8+`Y$_2wd6R7;kRLcuj2UA+-V!9cernvgrd98Q0OL}OfU;mSbNBU-12kz8feLfiK$97ilT~i|=W&66yImNNV$ zaQWd)@4%pK0VF&X%&l#NaX=L2WWbu(>BZi}Px=OVoFG8i9wdI57Mj%XbtJsH+RQKd z7*2Oq4bu^mlra-Nq}^X1R~v@K^yGiifUHHF&G2s6cx1Phr5%>Fz2h`tvdJl}r!7pC z0Q0&((_N9Hw|Iv;Kl9@7>R#%;EUq?R)zbAT9G=!rEKs-OcO+!C0Jzrih?0?I3v+zO zh5dn2ZoC3B&MdQ!P*Zu`uWootfsy0qLvTsyUan)8AnE1qiqsRB-U7i#rlJOTYNHb9 zPf{~Q<)vWx!JcvniEDD-1bI(#yy}CWiHF!4V^H0izp519N~We zsxc9pl3Fy-v~!nc%dLx9#oPQ~$623Ypm%mFD+aZAygk#E9L*c4P94Wff*70fFT&ue zb3=LJ57q?UB5ID!=wBqbPvCe(tGlrN^bd0t!F^8gOm#o!d%`O~pzg>(O0`!&e8OWZ zwZf>g?;Uo;9Y4gBlkXbif7A-sI5Te|3GT}+sT{rghCAtdAapDCRZA>B!U+lDN%)pv zAmx)8H?$nZtxY0sF{Oppuc26YQzKp6uzrO}L*OnKWwK!ezu(BhJrZ~1!mbu49i5RA z3oo@%)Lz+c1g42JgLjVS9CK_W;lnYAs{y zG)Qg(s~EYSI_%{%2)Jnvapm^jk`MrW9s@!h+|qTa)fch%-2liyAw@D+OkGB;s!UQH zSTIPAsxRBwh-eZQdR~}*=~3UVlZm#c#(aDd9)kqPmsgaSPZxc-pZ^{m`+Du^3PSy0 z;n7+p*=JYRA;)eNuGGG-58=EUO<}W#JJ0RhAe3{3f5}~pJ+QaQ(JwFVVp`?&xfM5~ zB6r~<7Lvh(daxozo$mOzQjgN6PCOjjsRGHxZE2uhtmZj?dakBC@L8o!pxGaBcYV#e zou%~w@_g3QgPwRGuJy2MTV#Fm@L67okHd67xO>GUQw$ln=5L zzxs3PSPGgUV%`=~preudEmhJFvDOdlO_vOYVB6|cewO{sJoy!Hx{(HZQkx)L`Vq6U LakA!GVH5raXlr5; literal 4664 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 diff --git a/android/java/app/src/main/res/drawable-mdpi/icon_burned.png b/android/java/app/src/main/res/drawable-mdpi/icon_burned.png index 115792242066a5c06f513987e627af1e64f34e38..3c0cc701a058cd8cce9ba01f8d440a880d1abeb2 100644 GIT binary patch literal 2015 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 diff --git a/android/java/app/src/main/res/drawable-xhdpi/icon_burned.png b/android/java/app/src/main/res/drawable-xhdpi/icon_burned.png index 9d3972c7514b6e6ad0dd0dd19ec89f209ac5273b..0cbf5993624c94e3635f3348feafac1596e0859f 100644 GIT binary patch literal 3358 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 delta 6713 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 ZAAsS1AxdEP(nupnNhv*aiG+YKfDYX) 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 bTX&eRr$q&LU6n8H`|;G2v=wXQt={|(UOb$! literal 11952 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 Date: Thu, 13 Nov 2014 06:57:14 +1100 Subject: [PATCH 119/415] Scale map style to match screen DPI --- .../java/com/mapbox/mapboxgl/lib/MapView.java | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 37db7d55f..9dc5d5e3f 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -10,6 +10,7 @@ import android.graphics.PointF; import android.os.Bundle; import android.support.annotation.NonNull; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.util.Log; import android.view.GestureDetector; import android.view.InputDevice; @@ -59,6 +60,9 @@ public class MapView extends SurfaceView { // Used to load map tiles private String mAccessToken; + // Used to handle DPI scaling + private float mScreenDensity = 1.0f; + // Touch gesture detectors private GestureDetector mGestureDetector; private ScaleGestureDetector mScaleGestureDetector; @@ -115,6 +119,9 @@ public class MapView extends SurfaceView { return; } + // Get the screen's density + mScreenDensity = context.getResources().getDisplayMetrics().density; + // Get the cache path String cachePath = context.getCacheDir().getAbsolutePath(); String dataPath = context.getFilesDir().getAbsolutePath(); @@ -465,7 +472,7 @@ public class MapView extends SurfaceView { int height) { Log.v(TAG, "surfaceChanged"); Log.i(TAG, "resize " + format + " " + width + " " + height); - mNativeMapView.resize(width, height); + mNativeMapView.resize((int) (width / mScreenDensity), (int) (height / mScreenDensity), mScreenDensity, width, height); } } @@ -516,10 +523,10 @@ public class MapView extends SurfaceView { mNativeMapView.cancelTransitions(); if (zoomIn) { - mNativeMapView.scaleBy(2.0, x, y, 0.3); + mNativeMapView.scaleBy(2.0, x / mScreenDensity, y / mScreenDensity, 0.3); } else { // TODO two finger tap zoom out - mNativeMapView.scaleBy(0.5, x, y, 0.3); + mNativeMapView.scaleBy(0.5, x / mScreenDensity, y / mScreenDensity, 0.3); } } @@ -654,8 +661,8 @@ public class MapView extends SurfaceView { * * // Cancel any animation mNativeMapView.cancelTransitions(); * - * mNativeMapView.moveBy(velocityX * duration / 2.0, velocityY * - * duration / 2.0, duration); + * mNativeMapView.moveBy(velocityX * duration / 2.0 / mScreenDensity, velocityY * + * duration / 2.0 / mScreenDensity, duration); * * return true; */ @@ -675,7 +682,7 @@ public class MapView extends SurfaceView { // transitions with touch // Scroll the map - mNativeMapView.moveBy(-distanceX, -distanceY); + mNativeMapView.moveBy(-distanceX / mScreenDensity, -distanceY / mScreenDensity); return true; } } @@ -740,7 +747,7 @@ public class MapView extends SurfaceView { // Scale the map mNativeMapView.scaleBy(detector.getScaleFactor(), - detector.getFocusX(), detector.getFocusY()); + detector.getFocusX() / mScreenDensity, detector.getFocusY() / mScreenDensity); return true; } @@ -818,8 +825,8 @@ public class MapView extends SurfaceView { double bearing = mNativeMapView.getBearing(); bearing += detector.getRotationDegreesDelta() * Math.PI / 180.0; Log.d("rotate", "rotate to " + bearing); - mNativeMapView.setBearing(bearing, detector.getFocusX(), - detector.getFocusY()); + mNativeMapView.setBearing(bearing, detector.getFocusX() / mScreenDensity, + detector.getFocusY() / mScreenDensity); return true; } @@ -874,7 +881,7 @@ public class MapView extends SurfaceView { mNativeMapView.cancelTransitions(); // Move left - mNativeMapView.moveBy(scrollDist, 0.0); + mNativeMapView.moveBy(scrollDist / mScreenDensity, 0.0 / mScreenDensity); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: @@ -886,7 +893,7 @@ public class MapView extends SurfaceView { mNativeMapView.cancelTransitions(); // Move right - mNativeMapView.moveBy(-scrollDist, 0.0); + mNativeMapView.moveBy(-scrollDist / mScreenDensity, 0.0 / mScreenDensity); return true; case KeyEvent.KEYCODE_DPAD_UP: @@ -898,7 +905,7 @@ public class MapView extends SurfaceView { mNativeMapView.cancelTransitions(); // Move up - mNativeMapView.moveBy(0.0, scrollDist); + mNativeMapView.moveBy(0.0 / mScreenDensity, scrollDist / mScreenDensity); return true; case KeyEvent.KEYCODE_DPAD_DOWN: @@ -910,7 +917,7 @@ public class MapView extends SurfaceView { mNativeMapView.cancelTransitions(); // Move down - mNativeMapView.moveBy(0.0, -scrollDist); + mNativeMapView.moveBy(0.0 / mScreenDensity, -scrollDist / mScreenDensity); return true; default: @@ -988,7 +995,7 @@ public class MapView extends SurfaceView { mNativeMapView.cancelTransitions(); // Scroll the map - mNativeMapView.moveBy(-10.0 * event.getX(), -10.0 * event.getY()); + mNativeMapView.moveBy(-10.0 * event.getX() / mScreenDensity, -10.0 * event.getY() / mScreenDensity); return true; // Trackball was pushed in so start tracking and tell system we are @@ -1085,8 +1092,8 @@ public class MapView extends SurfaceView { float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL); // Scale the map by the appropriate power of two factor - mNativeMapView.scaleBy(Math.pow(2.0, scrollDist), event.getX(), - event.getY()); + mNativeMapView.scaleBy(Math.pow(2.0, scrollDist), event.getX() / mScreenDensity, + event.getY() / mScreenDensity); return true; From 64f390943320dfb9846ab91ba568a5366c55e0ae Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 12 Nov 2014 12:24:58 -0800 Subject: [PATCH 120/415] formatting tweaks --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3b66a7d6f..9b2980e4d 100644 --- a/README.md +++ b/README.md @@ -105,15 +105,15 @@ Or proceed to building the debug application with: ## Android (on Linux) -Follow Linux steps until ./configure +Follow Linux steps until `./configure`. -Install Oracle JDK 7 +Install Oracle JDK 7: wget http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-linux-x64.tar.gz tar -xzf jdk-7u71-linux-x64.tar.gz export JAVA_HOME="/dir/to/jdk1.7.0_71" -Install Android NDK +Install Android NDK: wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin chmod +x android-ndk-r10c-linux-x86_64.bin @@ -121,20 +121,20 @@ Install Android NDK export ANDROID_NDK_PATH="/dir/to/android-ndk-r10c" export NDK_PATH=$(ANDROID_NDK_PATH) -Install Android SDK +Install Android SDK: wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz tar -xzf ./android-sdk_r23.0.2-linux.tgz ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository export ANDROID_HOME="/dir/to/android-sdk-linux" -Run +Run: make android -APKs for testing are output to android/java/app/build/outputs/apk/app-debug.apk +APKs for testing are output to `android/java/app/build/outputs/apk/app-debug.apk` -You can also open android/java in Android Studio. +You can also open `android/java` in Android Studio. # Troubleshooting From 79179c96b6d52169daf4d005b2c7a170a82d0bb1 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 12 Nov 2014 12:37:06 -0800 Subject: [PATCH 121/415] indicate submodule setup in common header of install info --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9b2980e4d..19d332879 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ implemented in C++11, currently targeting iOS, OS X, and Ubuntu Linux. We try to link to as many system-provided libraries as possible. When these are unavailable or too outdated, we run a thin build-script layer called [Mason](https://github.com/mapbox/mason) to automate builds, and load precompiled binary packages when possible. +Be sure to pull down all submodules first: + + git submodule init + git submodule update + ## OS X On OS X, we are using `zlib`, `SQLite3`, `libcurl` and `libpng` provided by the operating system. In addition to that, you need to have the Boost headers installed. To install all prerequisites, use [Homebrew](http://brew.sh/) and type `brew install pkg-config boost`. @@ -43,12 +48,7 @@ iOS makes use of a Cocoa-specific API called [`mapbox-gl-cocoa`](https://github. If you intend to develop here, `mapbox-gl-cocoa` is included as a submodule of the overall build setup. -To pull down the submodule(s), run: - - git submodule init - git submodule update - -Then, you can run `make iproj` to create and open an Xcode project with an iOS-specific view controller housing. This will automatically install required dependencies as well. +You can run `make iproj` to create and open an Xcode project with an iOS-specific view controller housing. This will automatically install required dependencies as well. Note that if you are doing OS X development as well, to toggle from OS X back to iOS, you will need to `make iproj` again. From 2071247689d94416be91fbaa1e4b09aa48b6d8f3 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 12 Nov 2014 12:37:38 -0800 Subject: [PATCH 122/415] tabs -> spaces --- README.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 19d332879..66d62a044 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,9 @@ We are using Ubuntu for development. While the software should work on other dis Install GCC 4.8+ if you are running Ubuntu 13.10 or older. Alternatively, you can also use Clang 3.4+. - sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test - sudo apt-get update - sudo apt-get install gcc-4.8 g++-4.8 + sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install gcc-4.8 g++-4.8 Ensure you have git and other build essentials: @@ -81,17 +81,17 @@ Install glfw3 dependencies: Finally, install Boost. If you're running Ubuntu 12.04 or older, you need to install a backport PPA since the version provided by APT doesn't contain Boost Geometry: - sudo add-apt-repository --yes ppa:boost-latest/ppa - sudo apt-get update - sudo apt-get install libboost1.55-dev + sudo add-apt-repository --yes ppa:boost-latest/ppa + sudo apt-get update + sudo apt-get install libboost1.55-dev Otherwise, you can just install - sudo apt-get install libboost-dev + sudo apt-get install libboost-dev Once you're done installing the build dependencies, you can get started by running - ./configure + ./configure which downloads all other dependencies that we need to build manually with [Mason](https://github.com/mapbox/mason). There's a good chance that there already are binary files and that you don't need to compile anything. @@ -101,7 +101,7 @@ Then, you can then proceed to build the library like: Or proceed to building the debug application with: - make linux + make linux ## Android (on Linux) @@ -109,28 +109,28 @@ Follow Linux steps until `./configure`. Install Oracle JDK 7: - wget http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-linux-x64.tar.gz - tar -xzf jdk-7u71-linux-x64.tar.gz - export JAVA_HOME="/dir/to/jdk1.7.0_71" + wget http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-linux-x64.tar.gz + tar -xzf jdk-7u71-linux-x64.tar.gz + export JAVA_HOME="/dir/to/jdk1.7.0_71" Install Android NDK: - wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin - chmod +x android-ndk-r10c-linux-x86_64.bin - ./android-ndk-r10c-linux-x86_64.bin - export ANDROID_NDK_PATH="/dir/to/android-ndk-r10c" - export NDK_PATH=$(ANDROID_NDK_PATH) + wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin + chmod +x android-ndk-r10c-linux-x86_64.bin + ./android-ndk-r10c-linux-x86_64.bin + export ANDROID_NDK_PATH="/dir/to/android-ndk-r10c" + export NDK_PATH=$(ANDROID_NDK_PATH) Install Android SDK: - wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz - tar -xzf ./android-sdk_r23.0.2-linux.tgz + wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz + tar -xzf ./android-sdk_r23.0.2-linux.tgz ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository export ANDROID_HOME="/dir/to/android-sdk-linux" Run: - make android + make android APKs for testing are output to `android/java/app/build/outputs/apk/app-debug.apk` From a5ac35d392d6fb49f7c63fc3f49029ab9c74f8de Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 12 Nov 2014 12:38:22 -0800 Subject: [PATCH 123/415] refs #556: OS X Android build instructions --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 66d62a044..4d5090c48 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,22 @@ APKs for testing are output to `android/java/app/build/outputs/apk/app-debug.apk You can also open `android/java` in Android Studio. +## Android (on OS X) + +Install the [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) for 64-bit OS X. + + export ANDROID_NDK_PATH="/dir/to/android-ndk-r10c" + +Install the Android SDK. We recommend doing this by way of [Android Studio](https://developer.android.com/sdk/installing/studio.html). The latest versions recommend that place the SDK someplace like `/usr/local/android-sdk-macosx`. + + export ANDROID_HOME="/dir/to/android-sdk-macosx" + +Run: + + make android + +You can then open `android/java` in Android Studio. + # Troubleshooting To trigger a complete rebuild, run `make clean` and then start over generating the Xcode projects or Makefiles as described above. From 6743cb6140f7785cd06b2196549b4621bba66d5b Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 12 Nov 2014 12:40:04 -0800 Subject: [PATCH 124/415] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d5090c48..3c914cbc0 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ Install the [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html export ANDROID_NDK_PATH="/dir/to/android-ndk-r10c" -Install the Android SDK. We recommend doing this by way of [Android Studio](https://developer.android.com/sdk/installing/studio.html). The latest versions recommend that place the SDK someplace like `/usr/local/android-sdk-macosx`. +Install the Android SDK. We recommend doing this by way of [Android Studio](https://developer.android.com/sdk/installing/studio.html). The latest versions recommend that you place the SDK someplace like `/usr/local/android-sdk-macosx`. export ANDROID_HOME="/dir/to/android-sdk-macosx" From 14714a1088583ef7f28f6b80e54be7f3031549bc Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Thu, 13 Nov 2014 18:50:47 +1100 Subject: [PATCH 125/415] Ensure error when the styles submodule is not loaded --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 1d03340c8..d48d21f38 100644 --- a/Makefile +++ b/Makefile @@ -90,6 +90,7 @@ android: cp build/android/out/$(BUILDTYPE)/lib.target/libmapbox-gl.so android/java/lib/src/main/jniLibs/armeabi-v7a/libmapbox-gl.so && \ mkdir -p android/java/lib/src/main/assets && \ cp build/android/out/$(BUILDTYPE)/ca-bundle.crt android/java/lib/src/main/assets/ca-bundle.crt && \ + cp build/android/out/$(BUILDTYPE)/styles/package.json android/java/lib/src/main/assets/styles/package.json && \ # Ensures error when no styles submodule loaded cp -r build/android/out/$(BUILDTYPE)/styles android/java/lib/src/main/assets/styles && \ cd android/java && \ ./gradlew build From 0c2e3be5af419cc69fe0ae704ab28d2027118b8a Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 14 Nov 2014 01:15:09 +1100 Subject: [PATCH 126/415] Implement connectivity monitoring Enable map debug mode in debug builds Go back to MainActivity --- android/java/app/src/main/AndroidManifest.xml | 14 ++-- .../com/mapbox/mapboxgl/app/MainActivity.java | 8 +++ android/java/lib/src/main/AndroidManifest.xml | 9 ++- .../java/com/mapbox/mapboxgl/lib/MapView.java | 72 ++++++++++++++++++- 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/android/java/app/src/main/AndroidManifest.xml b/android/java/app/src/main/AndroidManifest.xml index a48f83caf..567529187 100644 --- a/android/java/app/src/main/AndroidManifest.xml +++ b/android/java/app/src/main/AndroidManifest.xml @@ -3,10 +3,6 @@ package="com.mapbox.mapboxgl.app" android:versionCode="1" android:versionName="0.1" > - - - - - - + + diff --git a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java index 798e05a84..acc899500 100644 --- a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java +++ b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java @@ -1,6 +1,10 @@ package com.mapbox.mapboxgl.app; import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; import android.os.Bundle; import android.support.annotation.NonNull; import android.util.Log; @@ -16,6 +20,10 @@ public class MainActivity extends Activity { // Tag used for logging private static final String TAG = "MainActivity"; + // + // Instance members + // + // The map private MapView mMapView; diff --git a/android/java/lib/src/main/AndroidManifest.xml b/android/java/lib/src/main/AndroidManifest.xml index 16dabf837..5a6cdc0f9 100644 --- a/android/java/lib/src/main/AndroidManifest.xml +++ b/android/java/lib/src/main/AndroidManifest.xml @@ -5,6 +5,13 @@ - + + + + + + + + diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 9dc5d5e3f..08208657b 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -3,10 +3,16 @@ package com.mapbox.mapboxgl.lib; import org.json.JSONException; import org.json.JSONObject; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.graphics.PointF; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.os.Bundle; import android.support.annotation.NonNull; import android.util.AttributeSet; @@ -75,6 +81,13 @@ public class MapView extends SurfaceView { // Used to track trackball long presses private TrackballLongPressTimeOut mCurrentTrackballLongPressTimeOut; + // Used to check connection status + private ConnectivityManager mConnectivityManager; + private ConnectivityReceiver mConnectivityReceiver; + + // Holds the context + private Context mContext; + // // Properties // @@ -113,9 +126,12 @@ public class MapView extends SurfaceView { private void initialize(Context context, AttributeSet attrs) { Log.v(TAG, "initialize"); + // Save the context + mContext = context; + // Check if we are in Eclipse UI editor if (isInEditMode()) { - // TODO editor does not load properly because we dont implement this + // TODO editor does not load properly because we don't implement this return; } @@ -134,11 +150,15 @@ public class MapView extends SurfaceView { mStyleUrl = "file://" + dataPath + "/styles/styles/bright-v6.json"; mAccessToken = "pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"; + // Are we a debug build? + boolean isDebuggable = (context.getApplicationInfo().flags & + ApplicationInfo.FLAG_DEBUGGABLE) == ApplicationInfo.FLAG_DEBUGGABLE; + // Create the NativeMapView mNativeMapView = new NativeMapView(this, cachePath, dataPath); mNativeMapView.setStyleURL(mStyleUrl); mNativeMapView.setAccessToken(mAccessToken); - mNativeMapView.setDebug(true); + mNativeMapView.setDebug(isDebuggable); // Load the attributes TypedArray typedArray = context.obtainStyledAttributes(attrs, @@ -194,6 +214,14 @@ public class MapView extends SurfaceView { mZoomButtonsController.setZoomSpeed(300); mZoomButtonsController.setOnZoomListener(new OnZoomListener()); } + + // Check current connection status + mConnectivityManager = (ConnectivityManager)context.getSystemService( + Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetwork = mConnectivityManager.getActiveNetworkInfo(); + boolean isConnected = (activeNetwork != null) && + activeNetwork.isConnectedOrConnecting(); + onConnectivityChanged(isConnected); } } @@ -425,6 +453,11 @@ public class MapView extends SurfaceView { // Must be called from Activity onPause public void onPause() { Log.v(TAG, "onPause"); + + // Register for connectivity changes + getContext().unregisterReceiver(mConnectivityReceiver); + mConnectivityReceiver = null; + mNativeMapView.pause(); } @@ -433,6 +466,12 @@ public class MapView extends SurfaceView { public void onResume() { Log.v(TAG, "onResume"); + + // Register for connectivity changes + mConnectivityReceiver = new ConnectivityReceiver(); + mContext.registerReceiver(mConnectivityReceiver, + new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + mNativeMapView.resume(); } @@ -472,7 +511,8 @@ public class MapView extends SurfaceView { int height) { Log.v(TAG, "surfaceChanged"); Log.i(TAG, "resize " + format + " " + width + " " + height); - mNativeMapView.resize((int) (width / mScreenDensity), (int) (height / mScreenDensity), mScreenDensity, width, height); + mNativeMapView.resize((int) (width / mScreenDensity), (int) (height / mScreenDensity), + mScreenDensity, width, height); } } @@ -1132,6 +1172,32 @@ public class MapView extends SurfaceView { } } + // + // Action events + // + + // This class handles connectivity changes + private class ConnectivityReceiver extends BroadcastReceiver { + + // Called when an action we are listening to in the manifest has been sent + @Override + public void onReceive(Context context, Intent intent) { + Log.v(TAG, "ConnectivityReceiver.onReceive: action = " + intent.getAction()); + if (intent.getAction() == ConnectivityManager.CONNECTIVITY_ACTION) { + boolean noConnectivity = intent.getBooleanExtra( + ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); + onConnectivityChanged(!noConnectivity); + } + } + } + + + // Called when our Internet connectivity has changed + private void onConnectivityChanged(boolean isConnected) { + Log.v(TAG, "onConnectivityChanged: " + isConnected); + mNativeMapView.setReachability(isConnected); + } + // // Accessibility events // From 990be6743f07411e6bc89f010f9dc64587b44e97 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 14 Nov 2014 08:07:56 +1100 Subject: [PATCH 127/415] Fix Makefile --- Makefile | 2 +- .../java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d48d21f38..c303999a1 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ android: cp build/android/out/$(BUILDTYPE)/lib.target/libmapbox-gl.so android/java/lib/src/main/jniLibs/armeabi-v7a/libmapbox-gl.so && \ mkdir -p android/java/lib/src/main/assets && \ cp build/android/out/$(BUILDTYPE)/ca-bundle.crt android/java/lib/src/main/assets/ca-bundle.crt && \ - cp build/android/out/$(BUILDTYPE)/styles/package.json android/java/lib/src/main/assets/styles/package.json && \ # Ensures error when no styles submodule loaded + cp build/android/out/$(BUILDTYPE)/styles/package.json android/java/lib/src/main/assets/styles/package.json && \ cp -r build/android/out/$(BUILDTYPE)/styles android/java/lib/src/main/assets/styles && \ cd android/java && \ ./gradlew build diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 08208657b..e1359de1b 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -511,6 +511,8 @@ public class MapView extends SurfaceView { int height) { Log.v(TAG, "surfaceChanged"); Log.i(TAG, "resize " + format + " " + width + " " + height); + Log.v(TAG, "calling map resize with " + (int) (width / mScreenDensity) + "," + (int) (height / mScreenDensity) + "," + + mScreenDensity + "," + width + "," + height); mNativeMapView.resize((int) (width / mScreenDensity), (int) (height / mScreenDensity), mScreenDensity, width, height); } From dce0f0b6d947a3273f4f5181e83975048818f048 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 13 Nov 2014 18:15:29 -0800 Subject: [PATCH 128/415] Revert "Fix Makefile" This reverts commit 990be6743f07411e6bc89f010f9dc64587b44e97. --- Makefile | 2 +- .../java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Makefile b/Makefile index c303999a1..d48d21f38 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ android: cp build/android/out/$(BUILDTYPE)/lib.target/libmapbox-gl.so android/java/lib/src/main/jniLibs/armeabi-v7a/libmapbox-gl.so && \ mkdir -p android/java/lib/src/main/assets && \ cp build/android/out/$(BUILDTYPE)/ca-bundle.crt android/java/lib/src/main/assets/ca-bundle.crt && \ - cp build/android/out/$(BUILDTYPE)/styles/package.json android/java/lib/src/main/assets/styles/package.json && \ + cp build/android/out/$(BUILDTYPE)/styles/package.json android/java/lib/src/main/assets/styles/package.json && \ # Ensures error when no styles submodule loaded cp -r build/android/out/$(BUILDTYPE)/styles android/java/lib/src/main/assets/styles && \ cd android/java && \ ./gradlew build diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index e1359de1b..08208657b 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -511,8 +511,6 @@ public class MapView extends SurfaceView { int height) { Log.v(TAG, "surfaceChanged"); Log.i(TAG, "resize " + format + " " + width + " " + height); - Log.v(TAG, "calling map resize with " + (int) (width / mScreenDensity) + "," + (int) (height / mScreenDensity) + "," + - mScreenDensity + "," + width + "," + height); mNativeMapView.resize((int) (width / mScreenDensity), (int) (height / mScreenDensity), mScreenDensity, width, height); } From 30bea1dde4095f007c31076f57212d9bca07c4c0 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 13 Nov 2014 18:15:34 -0800 Subject: [PATCH 129/415] Revert "Ensure error when the styles submodule is not loaded" This reverts commit 14714a1088583ef7f28f6b80e54be7f3031549bc. --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index d48d21f38..1d03340c8 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,6 @@ android: cp build/android/out/$(BUILDTYPE)/lib.target/libmapbox-gl.so android/java/lib/src/main/jniLibs/armeabi-v7a/libmapbox-gl.so && \ mkdir -p android/java/lib/src/main/assets && \ cp build/android/out/$(BUILDTYPE)/ca-bundle.crt android/java/lib/src/main/assets/ca-bundle.crt && \ - cp build/android/out/$(BUILDTYPE)/styles/package.json android/java/lib/src/main/assets/styles/package.json && \ # Ensures error when no styles submodule loaded cp -r build/android/out/$(BUILDTYPE)/styles android/java/lib/src/main/assets/styles && \ cd android/java && \ ./gradlew build From d1389cc7ec5a687021be3bed13a14d46886b8c74 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Fri, 14 Nov 2014 18:35:00 +1100 Subject: [PATCH 130/415] Travis needs submodules too --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 1d03340c8..e766c9db4 100644 --- a/Makefile +++ b/Makefile @@ -71,6 +71,7 @@ build/linux/mapboxgl-app.xcodeproj: linux/mapboxgl-app.gyp config.gypi .PHONY: android android: + git submodule update --init --recursive && \ ./scripts/local_mason.sh && \ MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env PATH && \ export CXX="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ From 1512617ede4b659c724a0025c83ca05ebc0247a0 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 10:24:19 +1100 Subject: [PATCH 131/415] Add missing apt-get to Linux instructions --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3c914cbc0..6327fcc90 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ Install Android NDK: Install Android SDK: + sudo apt-get install lib32stdc++6 lib32z1 wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz tar -xzf ./android-sdk_r23.0.2-linux.tgz ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository From 7d66632209dc5a6eb447eba44f33ac2ab42e0894 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 11:14:37 +1100 Subject: [PATCH 132/415] Change to proper Log functions --- android/cpp/jni.cpp | 285 ++++++++++++++++---------------- android/cpp/native_map_view.cpp | 246 +++++++++++++-------------- include/mbgl/android/log.hpp | 25 --- include/mbgl/platform/event.hpp | 4 + 4 files changed, 271 insertions(+), 289 deletions(-) delete mode 100644 include/mbgl/android/log.hpp diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index 82b78f817..b8e11961a 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -11,11 +11,12 @@ #include -#include #include -#include #include +#include #include +#include +#include #pragma clang diagnostic ignored "-Wunused-parameter" @@ -140,7 +141,7 @@ namespace { using namespace mbgl::android; jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_, jstring data_path_) { - LOG_VERBOSE("nativeCreate"); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreate"); cache_path = std_string_from_jstring(env, cache_path_); data_path = std_string_from_jstring(env, data_path_); NativeMapView* native_map_view = new NativeMapView(env, obj); @@ -153,16 +154,16 @@ jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_, jstrin } void JNICALL nativeDestroy(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeDestroy"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeDestroy"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); delete native_map_view; native_map_view = nullptr; } void JNICALL nativeInitializeDisplay(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeInitializeDisplay"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeDisplay"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); if (!native_map_view->initializeDisplay()) { throw_error(env, "Unable to initialize GL display."); @@ -170,15 +171,15 @@ void JNICALL nativeInitializeDisplay(JNIEnv* env, jobject obj, jlong native_map_ } void JNICALL nativeTerminateDisplay(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeTerminateDisplay"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminateDisplay"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->terminateDisplay(); } void JNICALL nativeInitializeContext(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeInitializeContext"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeContext"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); if (!native_map_view->initializeContext()) { throw_error(env, "Unable to initialize GL context."); @@ -186,15 +187,15 @@ void JNICALL nativeInitializeContext(JNIEnv* env, jobject obj, jlong native_map_ } void JNICALL nativeTerminateContext(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeTerminateContext"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminateContext"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->terminateContext(); } void JNICALL nativeCreateSurface(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject surface) { - LOG_VERBOSE("nativeCreateSurface"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreateSurface"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); if (!native_map_view->createSurface(ANativeWindow_fromSurface(env, surface))) { throw_error(env, "Unable to create GL surface."); @@ -202,189 +203,189 @@ void JNICALL nativeCreateSurface(JNIEnv* env, jobject obj, jlong native_map_view } void JNICALL nativeDestroySurface(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeDestroySurface"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeDestroySurface"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->destroySurface(); } void JNICALL nativeStart(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeStart"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeStart"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->start(); } void JNICALL nativeStop(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeStop"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeStop"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->stop(); } void JNICALL nativePause(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativePause"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativePause"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->pause(); } void JNICALL nativeResume(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeResume"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeResume"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->resume(); } void JNICALL nativeRun(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeRun"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeRun"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().run(); } void JNICALL nativeRerender(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeRerender"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeRerender"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().rerender(); } void JNICALL nativeUpdate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeUpdate"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdate"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().update(); } void JNICALL nativeCleanup(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeCleanup"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeCleanup"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().cleanup(); } void JNICALL nativeTerminate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeTerminate"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminate"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().terminate(); } jboolean JNICALL nativeNeedsSwap(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeNeedsSwap"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeNeedsSwap"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap().needsSwap(); } void JNICALL nativeSwapped(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeSwapped"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSwapped"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().swapped(); } void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio) { - LOG_VERBOSE("nativeResize"); - LOG_ASSERT(native_map_view_ptr != 0); - LOG_ASSERT(width >= 0); - LOG_ASSERT(height >= 0); - LOG_ASSERT(width <= UINT16_MAX); - LOG_ASSERT(height <= UINT16_MAX); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeResize"); + assert(native_map_view_ptr != 0); + assert(width >= 0); + assert(height >= 0); + assert(width <= UINT16_MAX); + assert(height <= UINT16_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().resize(width, height, ratio); } void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio, jint fb_width, jint fb_height) { - LOG_VERBOSE("nativeResize"); - LOG_ASSERT(native_map_view_ptr != 0); - LOG_ASSERT(width >= 0); - LOG_ASSERT(height >= 0); - LOG_ASSERT(width <= UINT16_MAX); - LOG_ASSERT(height <= UINT16_MAX); - LOG_ASSERT(fb_width >= 0); - LOG_ASSERT(fb_height >= 0); - LOG_ASSERT(fb_width <= UINT16_MAX); - LOG_ASSERT(fb_height <= UINT16_MAX); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeResize"); + assert(native_map_view_ptr != 0); + assert(width >= 0); + assert(height >= 0); + assert(width <= UINT16_MAX); + assert(height <= UINT16_MAX); + assert(fb_width >= 0); + assert(fb_height >= 0); + assert(fb_width <= UINT16_MAX); + assert(fb_height <= UINT16_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().resize(width, height, ratio, fb_width, fb_height); } void JNICALL nativeSetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject classes) { - LOG_VERBOSE("nativeSetAppliedClasses"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetAppliedClasses"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setAppliedClasses(std_vector_string_from_jobject(env, classes)); } jobject JNICALL nativeGetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetAppliedClasses"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetAppliedClasses"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return std_vector_string_to_jobject(env, native_map_view->getMap().getAppliedClasses()); } void JNICALL nativeSetDefaultTransitionDuration(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jlong duration_milliseconds) { - LOG_VERBOSE("nativeSetDefaultTransitionDuration"); - LOG_ASSERT(native_map_view_ptr != 0); - LOG_ASSERT(duration_milliseconds >= 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetDefaultTransitionDuration"); + assert(native_map_view_ptr != 0); + assert(duration_milliseconds >= 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setDefaultTransitionDuration(duration_milliseconds); } void JNICALL nativeSetStyleURL(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring url) { - LOG_VERBOSE("nativeSetStyleURL"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetStyleURL"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setStyleURL(std_string_from_jstring(env, url)); } void JNICALL nativeSetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring new_style_json, jstring base) { - LOG_VERBOSE("nativeSetStyleJSON"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetStyleJSON"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setStyleJSON(std_string_from_jstring(env, new_style_json), std_string_from_jstring(env, base)); } jstring JNICALL nativeGetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetStyleJSON"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetStyleJSON"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return std_string_to_jstring(env, native_map_view->getMap().getStyleJSON()); } void JNICALL nativeSetAccessToken(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring access_token) { - LOG_VERBOSE("nativeSetAccessToken"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetAccessToken"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setAccessToken(std_string_from_jstring(env, access_token)); } jstring JNICALL nativeGetAccessToken(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetAccessToken"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetAccessToken"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return std_string_to_jstring(env, native_map_view->getMap().getAccessToken()); } void JNICALL nativeCancelTransitions(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeCancelTransitions"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeCancelTransitions"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().cancelTransitions(); } void JNICALL nativeMoveBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble dx, jdouble dy, jdouble duration) { - LOG_VERBOSE("nativeMoveBy"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeMoveBy"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().moveBy(dx, dy, duration); } void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat, jdouble duration) { - LOG_VERBOSE("nativeSetLonLat"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetLonLat"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); double lon = env->GetDoubleField(lon_lat, lon_lat_lon_id); @@ -403,8 +404,8 @@ void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr } jobject JNICALL nativeGetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetLonLat"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetLonLat"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); double lon, lat; native_map_view->getMap().getLonLat(lon, lat); @@ -419,64 +420,64 @@ jobject JNICALL nativeGetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ } void JNICALL nativeStartPanning(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeStartPanning"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeStartPanning"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().startPanning(); } void JNICALL nativeStopPanning(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeStopPanning"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeStopPanning"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().stopPanning(); } void JNICALL nativeResetPosition(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeResetPosition"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeResetPosition"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().resetPosition(); } void JNICALL nativeScaleBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble ds, jdouble cx, jdouble cy, jdouble duration) { - LOG_VERBOSE("nativeScaleBy"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeScaleBy"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().scaleBy(ds, cx, cy, duration); } void JNICALL nativeSetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble scale, jdouble cx, jdouble cy, jdouble duration) { - LOG_VERBOSE("nativeSetScale"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetScale"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setScale(scale, cx, cy, duration); } jdouble JNICALL nativeGetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetScale"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetScale"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap().getScale(); } void JNICALL nativeSetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble zoom, jdouble duration) { - LOG_VERBOSE("nativeSetZoom"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetZoom"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setZoom(zoom, duration); } jdouble JNICALL nativeGetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetZoom"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetZoom"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap().getZoom(); } void JNICALL nativeSetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat_zoom, jdouble duration) { - LOG_VERBOSE("nativeSetLonLatZoom"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetLonLatZoom"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); double lon = env->GetDoubleField(lon_lat_zoom, lon_lat_zoom_lon_id); @@ -501,8 +502,8 @@ void JNICALL nativeSetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view } jobject JNICALL nativeGetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetLonLatZoom"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetLonLatZoom"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); double lon, lat, zoom; native_map_view->getMap().getLonLatZoom(lon, lat, zoom); @@ -517,120 +518,120 @@ jobject JNICALL nativeGetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_v } void JNICALL nativeResetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeResetZoom"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeResetZoom"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().resetZoom(); } void JNICALL nativeStartScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeStartScaling"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeStartScaling"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().startScaling(); } void JNICALL nativeStopScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeStopScaling"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeStopScaling"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().stopScaling(); } jdouble JNICALL nativeGetMinZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetMinZoom"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetMinZoom"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap().getMinZoom(); } jdouble JNICALL nativeGetMaxZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetMaxZoom"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetMaxZoom"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap().getMaxZoom(); } void JNICALL nativeRotateBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble sx, jdouble sy, jdouble ex, jdouble ey, jdouble duration) { - LOG_VERBOSE("nativeRotateBy"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeRotateBy"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().rotateBy(sx, sy, ex, ey, duration); } void JNICALL nativeSetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble degrees, jdouble duration) { - LOG_VERBOSE("nativeSetBearing"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetBearing"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setBearing(degrees, duration); } void JNICALL nativeSetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble degrees, jdouble cx, jdouble cy) { - LOG_VERBOSE("nativeSetBearing"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetBearing"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setBearing(degrees, cx, cy); } jdouble JNICALL nativeGetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetBearing"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetBearing"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap().getBearing(); } void JNICALL nativeResetNorth(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeResetNorth"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeResetNorth"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().resetNorth(); } void JNICALL nativeStartRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeStartRotating"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeStartRotating"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().startRotating(); } void JNICALL nativeStopRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeStopRotating"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeStopRotating"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().stopRotating(); } jboolean JNICALL nativeCanRotate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeCanRotate"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeCanRotate"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap().canRotate(); } void JNICALL nativeSetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jboolean debug) { - LOG_VERBOSE("nativeSetDebug"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetDebug"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setDebug(debug); } void JNICALL nativeToggleDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeToggleDebug"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeToggleDebug"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().toggleDebug(); } jboolean JNICALL nativeGetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { - LOG_VERBOSE("nativeGetDebug"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetDebug"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); return native_map_view->getMap().getDebug(); } void JNICALL nativeSetReachability(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jboolean status) { - LOG_VERBOSE("nativeSetReachability"); - LOG_ASSERT(native_map_view_ptr != 0); + mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetReachability"); + assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); native_map_view->getMap().setReachability(status); } @@ -642,11 +643,12 @@ extern "C" { extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { mbgl::Log::Set(); - LOG_VERBOSE("JNI_OnLoad"); + mbgl::Log::Debug(mbgl::Event::JNI, "JNI_OnLoad"); - JNIEnv* env = nullptr; - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { - LOG_ERROR("GetEnv() failed"); + JNIEnv* env = nullptr; + jint ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + if (ret != JNI_OK) { + mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret); return JNI_ERR; } @@ -855,11 +857,12 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { } extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { - LOG_VERBOSE("JNI_OnUnload"); + mbgl::Log::Debug(mbgl::Event::JNI, "JNI_OnUnload"); JNIEnv* env = nullptr; - if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { - LOG_ERROR("GetEnv() failed"); + jint ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + if (ret != JNI_OK) { + mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret); return; } diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp index ab3034a18..c9cb96d5a 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -1,15 +1,17 @@ #include #include +#include + #include #include -#include #include - -#include #include +#include +#include +#include namespace mbgl { namespace android { @@ -17,74 +19,74 @@ namespace android { void log_egl_string(EGLDisplay display, EGLint name, const char* label) { const char* str = eglQueryString(display, name); if (str == nullptr) { - LOG_ERROR("eglQueryString(%d) returned error %d", name, eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglQueryString(%d) returned error %d", name, eglGetError()); } else { - LOG_INFO("EGL %s: %s", label, str); + mbgl::Log::Info(mbgl::Event::OpenGL, "EGL %s: %s", label, str); } } void log_gl_string(GLenum name, const char* label) { const GLubyte* str = glGetString(name); if (str == nullptr) { - LOG_ERROR("glGetString(%d) returned error %d", name, glGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "glGetString(%d) returned error %d", name, glGetError()); } else { - LOG_INFO("GL %s: %s", label, str); + mbgl::Log::Info(mbgl::Event::OpenGL, "GL %s: %s", label, str); } } void MBGLView::make_active() { - LOG_VERBOSE("MBGLView::make_active"); + mbgl::Log::Debug(mbgl::Event::Android, "MBGLView::make_active"); if ((nativeView.display != EGL_NO_DISPLAY) && (nativeView.surface != EGL_NO_SURFACE) && (nativeView.context != EGL_NO_CONTEXT)) { if (!eglMakeCurrent(nativeView.display, nativeView.surface, nativeView.surface, nativeView.context)) { - LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", eglGetError()); } } else { - LOG_DEBUG("Not activating as we are not ready"); + mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready"); } } void MBGLView::make_inactive() { - LOG_VERBOSE("MBGLView::make_inactive"); + mbgl::Log::Debug(mbgl::Event::Android, "MBGLView::make_inactive"); if (!eglMakeCurrent(nativeView.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); } } void MBGLView::swap() { - LOG_VERBOSE("MBGLView::swap"); + mbgl::Log::Debug(mbgl::Event::Android, "MBGLView::swap"); if (map->needsSwap() && (nativeView.display != EGL_NO_DISPLAY) && (nativeView.surface != EGL_NO_SURFACE)) { if (!eglSwapBuffers(nativeView.display, nativeView.surface)) { - LOG_ERROR("eglSwapBuffers() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d", eglGetError()); } map->swapped(); } else { - LOG_DEBUG("Not swapping as we are not ready"); + mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready"); } } void MBGLView::notify() { - LOG_VERBOSE("MBGLView::notify()"); + mbgl::Log::Debug(mbgl::Event::Android, "MBGLView::notify()"); // noop } void MBGLView::notify_map_change(mbgl::MapChange /* change */, mbgl::timestamp /* delay */) { - LOG_VERBOSE("MBGLView::notify_map_change()"); + mbgl::Log::Debug(mbgl::Event::Android, "MBGLView::notify_map_change()"); nativeView.notifyMapChange(); } NativeMapView::NativeMapView(JNIEnv* env, jobject obj_) : view(*this), map(view) { - LOG_VERBOSE("NativeMapView::NativeMapView"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::NativeMapView"); - LOG_ASSERT(env != nullptr); - LOG_ASSERT(obj_ != nullptr); + assert(env != nullptr); + assert(obj_ != nullptr); if (env->GetJavaVM(&vm) < 0) { env->ExceptionDescribe(); @@ -99,47 +101,48 @@ NativeMapView::NativeMapView(JNIEnv* env, jobject obj_) : view(*this), map(view) } NativeMapView::~NativeMapView() { - LOG_VERBOSE("NativeMapView::~NativeMapView"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::~NativeMapView"); terminateContext(); destroySurface(); terminateDisplay(); - LOG_ASSERT(vm != nullptr); - LOG_ASSERT(obj != nullptr); + assert(vm != nullptr); + assert(obj != nullptr); jint ret; JNIEnv* env = nullptr; - if ((ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6)) == JNI_OK) { + ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + if (ret == JNI_OK) { env->DeleteGlobalRef(obj); } else { - LOG_ERROR("GetEnv() failed with %i", ret); + mbgl::Log::Debug(mbgl::Event::JNI, "GetEnv() failed with %i", ret); } obj = nullptr; vm = nullptr; } bool NativeMapView::initializeDisplay() { - LOG_VERBOSE("NativeMapView::initializeDisplay"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::initializeDisplay"); - LOG_ASSERT(display == EGL_NO_DISPLAY); - LOG_ASSERT(config == nullptr); - LOG_ASSERT(format < 0); + assert(display == EGL_NO_DISPLAY); + assert(config == nullptr); + assert(format < 0); display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (display == EGL_NO_DISPLAY) { - LOG_ERROR("eglGetDisplay() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetDisplay() returned error %d", eglGetError()); terminateDisplay(); return false; } EGLint major, minor; if (!eglInitialize(display, &major, &minor)) { - LOG_ERROR("eglInitialize() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglInitialize() returned error %d", eglGetError()); terminateDisplay(); return false; } if ((major <= 1) && (minor < 3)) { - LOG_ERROR("EGL version is too low, need 1.3, got %d.%d", major, minor); + mbgl::Log::Error(mbgl::Event::OpenGL, "EGL version is too low, need 1.3, got %d.%d", major, minor); terminateDisplay(); return false; } @@ -153,9 +156,8 @@ bool NativeMapView::initializeDisplay() { char prop[PROP_VALUE_MAX]; __system_property_get("ro.kernel.qemu", prop); bool in_emulator = strtol(prop, nullptr, 0) == 1; - LOG_INFO("ro.kernel.qemu = %s", prop); if (in_emulator) { - LOG_INFO("In emulator! Enabling hacks :-("); + mbgl::Log::Warning(mbgl::Event::Android, "In emulator! Enabling hacks :-("); } // Try 565 first (faster) @@ -178,12 +180,12 @@ bool NativeMapView::initializeDisplay() { }; EGLint num_configs; if (!eglChooseConfig(display, config_attribs, nullptr, 0, &num_configs)) { - LOG_ERROR("eglChooseConfig(NULL) returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d", eglGetError()); terminateDisplay(); return false; } if (num_configs < 1) { - LOG_WARN("eglChooseConfig() returned no configs for 565"); + mbgl::Log::Warning(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs for 565"); // Now try 8888 use565 = false; @@ -192,56 +194,54 @@ bool NativeMapView::initializeDisplay() { config_attribs[9] = 8; // EGL_GREEN_SIZE config_attribs[11] = 8; // EGL_BLUE_SIZE if (!eglChooseConfig(display, config_attribs, nullptr, 0, &num_configs)) { - LOG_ERROR("eglChooseConfig(NULL) returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d", eglGetError()); terminateDisplay(); return false; } if (num_configs < 1) { - LOG_ERROR("eglChooseConfig() returned no configs for 8888"); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs for 8888"); terminateDisplay(); return false; } } - // TODO: make_unique is missing in Android NDK? - // const std::unique_ptr configs = std::make_unique(new EGLConfig[num_configs]); - const std::unique_ptr configs(new EGLConfig[num_configs]); + const std::unique_ptr configs = boost::make_unique(num_configs); // switch to std::make_unique in C++14 if (!eglChooseConfig(display, config_attribs, configs.get(), num_configs, &num_configs)) { - LOG_ERROR("eglChooseConfig() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError()); terminateDisplay(); return false; } config = chooseConfig(configs.get(), num_configs, use565); if (config == nullptr) { - LOG_ERROR("No config chosen"); + mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen"); terminateDisplay(); return false; } if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) { - LOG_ERROR("eglGetConfigAttrib() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib() returned error %d", eglGetError()); terminateDisplay(); return false; } - LOG_DEBUG("Chosen window format is %d", format); + mbgl::Log::Info(mbgl::Event::OpenGL, "Chosen window format is %d", format); return true; } void NativeMapView::terminateDisplay() { - LOG_VERBOSE("NativeMapView::terminateDisplay"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::terminateDisplay"); if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); } if (!eglTerminate(display)) { - LOG_ERROR("eglTerminate() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d", eglGetError()); } } @@ -251,11 +251,11 @@ void NativeMapView::terminateDisplay() { } bool NativeMapView::initializeContext() { - LOG_VERBOSE("NativeMapView::initializeContext"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::initializeContext"); - LOG_ASSERT(display != EGL_NO_DISPLAY); - LOG_ASSERT(context == EGL_NO_CONTEXT); - LOG_ASSERT(config != nullptr); + assert(display != EGL_NO_DISPLAY); + assert(context == EGL_NO_CONTEXT); + assert(config != nullptr); const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, @@ -264,29 +264,27 @@ bool NativeMapView::initializeContext() { context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attribs); if (context == EGL_NO_CONTEXT) { - LOG_ERROR("eglCreateContext() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error %d", eglGetError()); terminateContext(); return false; } - LOG_INFO("Context initialized"); - return true; } void NativeMapView::terminateContext() { - LOG_VERBOSE("NativeMapView::terminateContext"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::terminateContext"); if (display != EGL_NO_DISPLAY) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); } if (context != EGL_NO_CONTEXT) { if (!eglDestroyContext(display, context)) { - LOG_ERROR("eglDestroyContext() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroyContext() returned error %d", eglGetError()); } } } @@ -295,16 +293,16 @@ void NativeMapView::terminateContext() { } bool NativeMapView::createSurface(ANativeWindow* window_) { - LOG_VERBOSE("NativeMapView::createSurface"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::createSurface"); - LOG_ASSERT(window == nullptr); - LOG_ASSERT(window_ != nullptr); + assert(window == nullptr); + assert(window_ != nullptr); window = window_; - LOG_ASSERT(display != EGL_NO_DISPLAY); - LOG_ASSERT(surface == EGL_NO_SURFACE); - LOG_ASSERT(config != nullptr); - LOG_ASSERT(format >= 0); + assert(display != EGL_NO_DISPLAY); + assert(surface == EGL_NO_SURFACE); + assert(config != nullptr); + assert(format >= 0); ANativeWindow_setBuffersGeometry(window, 0, 0, format); @@ -313,7 +311,7 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { }; surface = eglCreateWindowSurface(display, config, window, surface_attribs); if (surface == EGL_NO_SURFACE) { - LOG_ERROR("eglCreateWindowSurface() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d", eglGetError()); destroySurface(); return false; } @@ -322,7 +320,7 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { first_time = true; if (!eglMakeCurrent(display, surface, surface, context)) { - LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d", eglGetError()); } log_gl_string(GL_VENDOR, "Vendor"); @@ -335,7 +333,7 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { - LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError()); } } @@ -346,13 +344,13 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { } void NativeMapView::destroySurface() { - LOG_VERBOSE("NativeMapView::destroySurface"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::destroySurface"); pause(true); if (surface != EGL_NO_SURFACE) { if (!eglDestroySurface(display, surface)) { - LOG_ERROR("eglDestroySurface() returned error %d", eglGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d", eglGetError()); } } @@ -366,104 +364,104 @@ void NativeMapView::destroySurface() { EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint num_configs, bool use565) const { - LOG_INFO("Found %d configs", num_configs); + mbgl::Log::Info(mbgl::Event::OpenGL, "Found %d configs", num_configs); int chosen_config = -1; bool is_caveat = false; bool is_conformant = false; for (int i = 0; i < num_configs; i++) { - LOG_INFO("Config %d:", i); + mbgl::Log::Info(mbgl::Event::OpenGL, "Config %d:", i); EGLint caveat, conformant, bits, red, green, blue, alpha, alpha_mask, depth, stencil, sample_buffers, samples; if (!eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)) { - LOG_ERROR("eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) { - LOG_ERROR("eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) { - LOG_ERROR("eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) { - LOG_ERROR("eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) { - LOG_ERROR("eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) { - LOG_ERROR("eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) { - LOG_ERROR("eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alpha_mask)) { - LOG_ERROR("eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) { - LOG_ERROR("eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) { - LOG_ERROR("eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sample_buffers)) { - LOG_ERROR("eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d", eglGetError()); return nullptr; } if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) { - LOG_ERROR("eglGetConfigAttrib(EGL_SAMPLES) returned error %d", + mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError()); return nullptr; } - LOG_INFO("Caveat: %d", caveat); - LOG_INFO("Conformant: %d", conformant); - LOG_INFO("Color: %d", bits); - LOG_INFO("Red: %d", red); - LOG_INFO("Green: %d", green); - LOG_INFO("Blue: %d", blue); - LOG_INFO("Alpha: %d", alpha); - LOG_INFO("Alpha mask: %d", alpha_mask); - LOG_INFO("Depth: %d", depth); - LOG_INFO("Stencil: %d", stencil); - LOG_INFO("Sample buffers: %d", sample_buffers); - LOG_INFO("Samples: %d", samples); + mbgl::Log::Info(mbgl::Event::OpenGL, "Caveat: %d", caveat); + mbgl::Log::Info(mbgl::Event::OpenGL, "Conformant: %d", conformant); + mbgl::Log::Info(mbgl::Event::OpenGL, "Color: %d", bits); + mbgl::Log::Info(mbgl::Event::OpenGL, "Red: %d", red); + mbgl::Log::Info(mbgl::Event::OpenGL, "Green: %d", green); + mbgl::Log::Info(mbgl::Event::OpenGL, "Blue: %d", blue); + mbgl::Log::Info(mbgl::Event::OpenGL, "Alpha: %d", alpha); + mbgl::Log::Info(mbgl::Event::OpenGL, "Alpha mask: %d", alpha_mask); + mbgl::Log::Info(mbgl::Event::OpenGL, "Depth: %d", depth); + mbgl::Log::Info(mbgl::Event::OpenGL, "Stencil: %d", stencil); + mbgl::Log::Info(mbgl::Event::OpenGL, "Sample buffers: %d", sample_buffers); + mbgl::Log::Info(mbgl::Event::OpenGL, "Samples: %d", samples); bool config_ok = true; config_ok &= bits == (use565 ? 16 : 32); @@ -483,22 +481,22 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], } } - LOG_DEBUG("Chosen config is %d", chosen_config); + mbgl::Log::Debug(mbgl::Event::OpenGL, "Chosen config is %d", chosen_config); if (chosen_config >= 0) { return configs[chosen_config]; } else { return nullptr; } if (!is_caveat) { - LOG_WARN("Chosen config has a caveat."); + mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config has a caveat."); } if (!is_conformant) { - LOG_WARN("Chosen config is not conformant."); + mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config is not conformant."); } } void NativeMapView::start() { - LOG_VERBOSE("NativeMapView::start"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::start"); if (display == EGL_NO_DISPLAY) { initializeDisplay(); @@ -508,8 +506,8 @@ void NativeMapView::start() { initializeContext(); } - LOG_ASSERT(display != EGL_NO_DISPLAY); - LOG_ASSERT(context != EGL_NO_CONTEXT); + assert(display != EGL_NO_DISPLAY); + assert(context != EGL_NO_CONTEXT); map.start(true); } @@ -517,21 +515,21 @@ void NativeMapView::start() { void NativeMapView::loadExtensions() { const GLubyte* str = glGetString(GL_EXTENSIONS); if (str == nullptr) { - LOG_ERROR("glGetString(GL_EXTENSIONS) returned error %d", glGetError()); + mbgl::Log::Error(mbgl::Event::OpenGL, "glGetString(GL_EXTENSIONS) returned error %d", glGetError()); return; } std::string extensions(reinterpret_cast(str)); if (extensions.find("GL_OES_vertex_array_object") != std::string::npos) { - LOG_INFO("Using GL_OES_vertex_array_object."); + mbgl::Log::Info(mbgl::Event::OpenGL, "Using GL_OES_vertex_array_object."); gl::BindVertexArray = (gl::PFNGLBINDVERTEXARRAYPROC)eglGetProcAddress("glBindVertexArrayOES"); gl::DeleteVertexArrays = (gl::PFNGLDELETEVERTEXARRAYSPROC)eglGetProcAddress("glDeleteVertexArraysOES"); gl::GenVertexArrays = (gl::PFNGLGENVERTEXARRAYSPROC)eglGetProcAddress("glGenVertexArraysOES"); gl::IsVertexArray = (gl::PFNGLISVERTEXARRAYPROC)eglGetProcAddress("glIsVertexArrayOES"); if ((gl::BindVertexArray == nullptr) || (gl::DeleteVertexArrays == nullptr) || (gl::GenVertexArrays == nullptr) || (gl::IsVertexArray == nullptr)) { - LOG_ERROR("Could not load GL_OES_vertex_array_object functions."); + mbgl::Log::Error(mbgl::Event::OpenGL, "Could not load GL_OES_vertex_array_object functions."); gl::BindVertexArray = nullptr; gl::DeleteVertexArrays = nullptr; gl::GenVertexArrays = nullptr; @@ -541,42 +539,42 @@ void NativeMapView::loadExtensions() { } void NativeMapView::stop() { - LOG_VERBOSE("NativeMapView::stop"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::stop"); - LOG_ASSERT(display != EGL_NO_DISPLAY); - LOG_ASSERT(context != EGL_NO_CONTEXT); + assert(display != EGL_NO_DISPLAY); + assert(context != EGL_NO_CONTEXT); map.stop(); } void NativeMapView::pause(bool wait_for_pause) { - LOG_VERBOSE("NativeMapView::pause %s", (wait_for_pause) ? "true" : "false"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::pause %s", (wait_for_pause) ? "true" : "false"); - LOG_ASSERT(display != EGL_NO_DISPLAY); - LOG_ASSERT(context != EGL_NO_CONTEXT); + assert(display != EGL_NO_DISPLAY); + assert(context != EGL_NO_CONTEXT); map.pause(wait_for_pause); } void NativeMapView::resume() { - LOG_VERBOSE("NativeMapView::resume"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::resume"); - LOG_ASSERT(display != EGL_NO_DISPLAY); - LOG_ASSERT(context != EGL_NO_CONTEXT); + assert(display != EGL_NO_DISPLAY); + assert(context != EGL_NO_CONTEXT); if (surface != EGL_NO_SURFACE) { map.resume(); } else { - LOG_DEBUG("Not resuming because we are not ready"); + mbgl::Log::Debug(mbgl::Event::Android, "Not resuming because we are not ready"); } } void NativeMapView::notifyMapChange() { - LOG_VERBOSE("NativeMapView::notify_map_change()"); + mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notify_map_change()"); - LOG_ASSERT(vm != nullptr); - LOG_ASSERT(obj != nullptr); + assert(vm != nullptr); + assert(obj != nullptr); JavaVMAttachArgs args = { JNI_VERSION_1_2, @@ -587,13 +585,15 @@ void NativeMapView::notifyMapChange() { jint ret; JNIEnv* env = nullptr; bool detach = false; - if ((ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6)) != JNI_OK) { + ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + if (ret != JNI_OK) { if (ret != JNI_EDETACHED) { - LOG_ERROR("GetEnv() failed with %i", ret); + mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret); return; } else { - if ((ret = vm->AttachCurrentThread(&env, &args)) != JNI_OK) { - LOG_ERROR("AttachCurrentThread() failed with %i", ret); + ret = vm->AttachCurrentThread(&env, &args); + if (ret != JNI_OK) { + mbgl::Log::Error(mbgl::Event::JNI, "AttachCurrentThread() failed with %i", ret); return; } detach = true; @@ -607,7 +607,7 @@ void NativeMapView::notifyMapChange() { if (detach) { if ((ret = vm->DetachCurrentThread()) != JNI_OK) { - LOG_ERROR("DetachCurrentThread() failed with %i", ret); + mbgl::Log::Error(mbgl::Event::JNI, "DetachCurrentThread() failed with %i", ret); return; } } diff --git a/include/mbgl/android/log.hpp b/include/mbgl/android/log.hpp deleted file mode 100644 index 50b6bec1e..000000000 --- a/include/mbgl/android/log.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef MBGL_ANDROID_LOG -#define MBGL_ANDROID_LOG - -#include - -#define S(x) #x -#define S_(x) S(x) -#define S__LINE__ S_(__LINE__) - -#ifdef DEBUG -#define LOG_VERBOSE(fmt, args...) __android_log_print(ANDROID_LOG_VERBOSE, __FILE__ ":" S__LINE__, fmt, ##args) -#define LOG_DEBUG(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__ ":" S__LINE__, fmt, ##args) -#define LOG_ASSERT(cond, ...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, NULL)) -#define LOG_ASSERTMSG(cond, fmt, args...) ((cond)?(void)0:__android_log_assert(#cond, __FILE__ ":" S__LINE__, fmt, ##args)) -#else -#define LOG_VERBOSE(fmt, args...) -#define LOG_DEBUG(fmt, args...) -#define LOG_ASSERT(cond, ...) -#define LOG_ASSERTMSG(cond, fmt, args...) -#endif -#define LOG_INFO(fmt, args...) __android_log_print(ANDROID_LOG_INFO, __FILE__ ":" S__LINE__, fmt, ##args) -#define LOG_WARN(fmt, args...) __android_log_print(ANDROID_LOG_WARN, __FILE__ ":" S__LINE__, fmt, ##args) -#define LOG_ERROR(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, __FILE__ ":" S__LINE__, fmt, ##args) - -#endif diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp index 1b3d55eb1..77c45fb0f 100644 --- a/include/mbgl/platform/event.hpp +++ b/include/mbgl/platform/event.hpp @@ -34,6 +34,8 @@ enum class Event : uint8_t { Sprite, Image, OpenGL, + JNI, + Android }; MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { @@ -48,6 +50,8 @@ MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { { Event::Sprite, "Sprite" }, { Event::Image, "Image" }, { Event::OpenGL, "OpenGL" }, + { Event::JNI, "JNI" }, + { Event::Android, "Android" }, { Event(-1), "Unknown" }, }); From 7f2d876fc496b26a52ca06f1d55df1a869e6d096 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 11:29:11 +1100 Subject: [PATCH 133/415] Fix some lint errors --- android/java/lib/src/main/AndroidManifest.xml | 6 ------ .../lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java | 2 +- android/java/lib/src/main/res/raw/style.min.js | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 android/java/lib/src/main/res/raw/style.min.js diff --git a/android/java/lib/src/main/AndroidManifest.xml b/android/java/lib/src/main/AndroidManifest.xml index 5a6cdc0f9..d23d386f9 100644 --- a/android/java/lib/src/main/AndroidManifest.xml +++ b/android/java/lib/src/main/AndroidManifest.xml @@ -8,10 +8,4 @@ - - - - - - diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 08208657b..f664f2cf0 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -1177,7 +1177,7 @@ public class MapView extends SurfaceView { // // This class handles connectivity changes - private class ConnectivityReceiver extends BroadcastReceiver { + public class ConnectivityReceiver extends BroadcastReceiver { // Called when an action we are listening to in the manifest has been sent @Override diff --git a/android/java/lib/src/main/res/raw/style.min.js b/android/java/lib/src/main/res/raw/style.min.js deleted file mode 100644 index 906bec14f..000000000 --- a/android/java/lib/src/main/res/raw/style.min.js +++ /dev/null @@ -1 +0,0 @@ -{"buckets":{"admin_maritime":{"source":"outdoors","layer":"admin","field":"maritime","value":1,"join":"round","cap":"round","type":"line"},"admin_l2":{"source":"outdoors","layer":"admin","filter":["and",{"field":"admin_level","value":2},{"field":"maritime","operator":"not","value":1}],"join":"round","cap":"round","type":"line"},"admin_l3":{"source":"outdoors","layer":"admin","filter":["and",{"field":"admin_level","value":[3,4,5]},{"field":"maritime","operator":"not","value":1}],"join":"round","type":"line"},"landcover_wood":{"source":"outdoors","layer":"landcover","field":"class","value":"wood","type":"fill"},"landcover_scrub":{"source":"outdoors","layer":"landcover","field":"class","value":"scrub","type":"fill"},"landcover_grass":{"source":"outdoors","layer":"landcover","field":"class","value":"grass","type":"fill"},"landcover_crop":{"source":"outdoors","layer":"landcover","field":"class","value":"crop","type":"fill"},"landcover_snow":{"source":"outdoors","layer":"landcover","field":"class","value":"snow","type":"fill"},"water":{"source":"outdoors","layer":"water","type":"fill"},"waterway_other":{"source":"outdoors","layer":"waterway","field":"type","value":["ditch","drain"],"cap":"round","type":"line"},"waterway_river_canal":{"source":"outdoors","layer":"waterway","field":"type","value":["river","canal"],"cap":"round","type":"line"},"waterway_stream":{"source":"outdoors","layer":"waterway","field":"type","value":"stream","cap":"round","type":"line"},"landuse_park":{"source":"outdoors","layer":"landuse","field":"class","value":"park","type":"fill"},"landuse_pitch":{"source":"outdoors","layer":"landuse","field":"class","value":"pitch","type":"fill"},"landuse_cemetery":{"source":"outdoors","layer":"landuse","field":"class","value":"cemetery","type":"fill"},"landuse_hospital":{"source":"outdoors","layer":"landuse","field":"class","value":"hospital","type":"fill"},"landuse_industrial":{"source":"outdoors","layer":"landuse","type":"fill","field":"class","value":"industrial"},"landuse_school":{"source":"outdoors","layer":"landuse","field":"class","value":"school","type":"fill"},"landuse_wood":{"source":"outdoors","layer":"landuse","field":"class","value":"wood","type":"fill"},"landuse_scrub":{"source":"outdoors","layer":"landuse","field":"class","value":"scrub","type":"fill"},"landuse_grass":{"source":"outdoors","layer":"landuse","field":"class","value":"grass","type":"fill"},"landuse_crop":{"source":"outdoors","layer":"landuse","field":"class","value":"crop","type":"fill"},"landuse_sand":{"source":"outdoors","layer":"landuse","field":"class","value":"sand","type":"fill"},"landuse_rock":{"source":"outdoors","layer":"landuse","field":"class","value":"rock","type":"fill"},"landuse_snow":{"source":"outdoors","layer":"landuse","field":"class","value":"snow","type":"fill"},"overlay_wetland":{"source":"outdoors","layer":"landuse_overlay","field":"class","value":["wetland","wetland_noveg"],"type":"fill"},"overlay_breakwater_pier":{"source":"outdoors","layer":"landuse_overlay","field":"class","value":["breakwater","pier"],"type":"fill"},"hillshade_full_shadow":{"source":"outdoors","layer":"hillshade","field":"class","value":"full_shadow","type":"fill"},"hillshade_medium_shadow":{"source":"outdoors","layer":"hillshade","field":"class","value":"medium_shadow","type":"fill"},"hillshade_medium_highlight":{"source":"outdoors","layer":"hillshade","field":"class","value":"medium_highlight","type":"fill"},"hillshade_full_highlight":{"source":"outdoors","layer":"hillshade","field":"class","value":"full_highlight","type":"fill"},"contour_line_5":{"source":"outdoors","layer":"contour","field":"index","join":"round","value":5,"type":"line"},"contour_line_10":{"source":"outdoors","layer":"contour","field":"index","join":"round","value":10,"type":"line"},"contour_line_other":{"source":"outdoors","layer":"contour","join":"round","type":"line"},"contour_label":{"source":"outdoors","layer":"contour","filter":["and",{"field":"index","value":[5,10]},{"field":"ele","operator":"not","value":0}],"path":"curve","text_field":"{{ele}} m","font":"Open Sans Regular, Arial Unicode MS Regular","fontSize":10,"feature_type":"line","type":"text","maxAngleDelta":0.5},"building":{"source":"outdoors","layer":"building","type":"fill"},"barrier_line_gate":{"source":"outdoors","layer":"barrier_line","field":"class","value":"gate","type":"line"},"barrier_line_fence":{"source":"outdoors","layer":"barrier_line","field":"class","value":"fence","type":"line"},"barrier_line_hedge":{"source":"outdoors","layer":"barrier_line","field":"class","value":"hedge","type":"line"},"barrier_line_land":{"source":"outdoors","layer":"barrier_line","field":"class","value":"land","type":"line"},"barrier_line_land_fill":{"source":"outdoors","layer":"barrier_line","field":"class","value":"land","type":"fill"},"barrier_line_cliff":{"source":"outdoors","layer":"barrier_line","field":"class","value":"cliff","type":"line"},"aeroway_fill":{"source":"outdoors","layer":"aeroway","type":"fill","enabled":12},"aeroway_runway":{"source":"outdoors","layer":"aeroway","field":"type","value":"runway","type":"line","enabled":12},"aeroway_taxiway":{"source":"outdoors","layer":"aeroway","field":"type","value":"taxiway","type":"line","enabled":12},"motorway":{"source":"outdoors","layer":"road","field":"class","value":"motorway","join":"round","cap":"round","type":"line"},"motorway_link":{"source":"outdoors","layer":"road","field":"class","value":"motorway_link","join":"round","cap":"round","type":"line"},"main":{"source":"outdoors","layer":"road","field":"class","value":"main","join":"round","cap":"round","type":"line"},"street":{"source":"outdoors","layer":"road","field":"class","value":["street","street_limited"],"join":"round","cap":"round","type":"line","enabled":12},"service":{"source":"outdoors","layer":"road","field":"class","value":"service","join":"round","cap":"round","type":"line","enabled":15},"path":{"source":"outdoors","layer":"road","field":"class","value":"path","type":"line"},"path_footway":{"source":"outdoors","layer":"road","field":"type","value":"footway","type":"line"},"path_path":{"source":"outdoors","layer":"road","field":"type","value":"path","type":"line"},"path_cycleway":{"source":"outdoors","layer":"road","field":"type","value":"cycleway","type":"line"},"path_mtb":{"source":"outdoors","layer":"road","field":"type","value":"mtb","type":"line"},"path_steps":{"source":"outdoors","layer":"road","field":"type","value":"steps","type":"line"},"path_piste":{"source":"outdoors","layer":"road","field":"type","value":"piste","type":"line"},"major_rail":{"source":"outdoors","layer":"road","field":"class","value":"major_rail","type":"line"},"tunnel_motorway":{"source":"outdoors","layer":"tunnel","field":"class","value":"motorway","type":"line"},"tunnel_motorway_link":{"source":"outdoors","layer":"tunnel","field":"class","value":"motorway_link","type":"line"},"tunnel_main":{"source":"outdoors","layer":"tunnel","field":"class","value":"main","type":"line"},"tunnel_street":{"source":"outdoors","layer":"tunnel","field":"class","value":["street","street_limited"],"type":"line","enabled":12},"tunnel_service":{"source":"outdoors","layer":"tunnel","field":"class","value":"service","type":"line","enabled":15},"tunnel_path":{"source":"outdoors","layer":"tunnel","field":"class","value":"path","type":"line"},"tunnel_path_footway":{"source":"outdoors","layer":"tunnel","field":"type","value":"footway","type":"line"},"tunnel_path_path":{"source":"outdoors","layer":"tunnel","field":"type","value":"path","type":"line"},"tunnel_path_cycleway":{"source":"outdoors","layer":"tunnel","field":"type","value":"cycleway","type":"line"},"tunnel_path_mtb":{"source":"outdoors","layer":"tunnel","field":"type","value":"mtb","type":"line"},"tunnel_path_steps":{"source":"outdoors","layer":"tunnel","field":"type","value":"steps","type":"line"},"tunnel_path_piste":{"source":"outdoors","layer":"tunnel","field":"type","value":"piste","type":"line"},"tunnel_major_rail":{"source":"outdoors","layer":"tunnel","field":"class","value":"major_rail","type":"line"},"bridge_motorway":{"source":"outdoors","layer":"bridge","field":"class","value":"motorway","type":"line"},"bridge_motorway_link":{"source":"outdoors","layer":"bridge","field":"class","value":"motorway_link","type":"line"},"bridge_main":{"source":"outdoors","layer":"bridge","field":"class","value":"main","type":"line"},"bridge_street":{"source":"outdoors","layer":"bridge","field":"class","value":["street","street_limited"],"type":"line","enabled":12},"bridge_service":{"source":"outdoors","layer":"bridge","field":"class","value":"service","type":"line","enabled":15},"bridge_path":{"source":"outdoors","layer":"bridge","type":"line"},"bridge_path_footway":{"source":"outdoors","layer":"bridge","field":"type","value":"footway","type":"line"},"bridge_path_path":{"source":"outdoors","layer":"bridge","field":"type","value":"path","type":"line"},"bridge_path_cycleway":{"source":"outdoors","layer":"bridge","field":"type","value":"cycleway","type":"line"},"bridge_path_mtb":{"source":"outdoors","layer":"bridge","field":"type","value":"mtb","type":"line"},"bridge_path_steps":{"source":"outdoors","layer":"bridge","field":"type","value":"steps","type":"line"},"bridge_path_piste":{"source":"outdoors","layer":"bridge","field":"type","value":"piste","type":"line"},"bridge_major_rail":{"source":"outdoors","layer":"bridge","field":"class","value":"major_rail","type":"line"},"bridge_aerialway":{"source":"outdoors","layer":"bridge","field":"class","value":"aerialway","type":"line"},"country_label":{"source":"outdoors","layer":"country_label","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":24,"feature_type":"point","type":"text","maxWidth":5},"country_label_line":{"source":"outdoors","layer":"country_label_line","type":"line"},"marine_label_line_1":{"source":"outdoors","layer":"marine_label","feature_type":"line","type":"text","field":"labelrank","value":1,"text_field":"{{name_en}}","path":"curve","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":30,"letterSpacing":0.4,"maxAngleDelta":0.5},"marine_label_line_2":{"source":"outdoors","layer":"marine_label","feature_type":"line","field":"labelrank","value":2,"type":"text","text_field":"{{name_en}}","path":"curve","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":24,"letterSpacing":0.2,"maxAngleDelta":0.5},"marine_label_line_3":{"source":"outdoors","layer":"marine_label","feature_type":"line","type":"text","field":"labelrank","value":3,"text_field":"{{name_en}}","path":"curve","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":18,"letterSpacing":0.1,"maxAngleDelta":0.5},"marine_label_line_other":{"source":"outdoors","layer":"marine_label","feature_type":"line","type":"text","field":"labelrank","value":[4,5,6],"text_field":"{{name_en}}","path":"curve","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":16,"letterSpacing":0.1,"maxAngleDelta":0.5},"marine_label_point_1":{"source":"outdoors","layer":"marine_label","feature_type":"point","type":"text","text_field":"{{name_en}}","path":"horizontal","field":"labelrank","value":1,"font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":30,"maxWidth":8,"letterSpacing":0.4,"lineHeight":2},"marine_label_point_2":{"source":"outdoors","layer":"marine_label","feature_type":"point","type":"text","text_field":"{{name_en}}","path":"horizontal","field":"labelrank","value":2,"font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":24,"maxWidth":8,"letterSpacing":0.2,"lineHeight":1.5},"marine_label_point_3":{"source":"outdoors","layer":"marine_label","feature_type":"point","type":"text","text_field":"{{name_en}}","path":"horizontal","field":"labelrank","value":3,"font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":18,"maxWidth":8,"letterSpacing":0.1,"lineHeight":1.3},"marine_label_point_other":{"source":"outdoors","layer":"marine_label","feature_type":"point","type":"text","text_field":"{{name_en}}","path":"horizontal","field":"labelrank","value":[4,5,6],"font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":16,"maxWidth":8,"letterSpacing":0.1,"lineHeight":1.2},"state_label":{"source":"outdoors","layer":"state_label","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Regular, Arial Unicode MS Regular","fontSize":16,"feature_type":"point","type":"text","enabled":4,"maxWidth":8},"place_label_city":{"source":"outdoors","layer":"place_label","field":"type","value":"city","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":20,"feature_type":"point","type":"text","maxWidth":8},"place_label_town":{"source":"outdoors","layer":"place_label","field":"type","value":"town","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":24,"feature_type":"point","type":"text","maxWidth":8},"place_label_village":{"source":"outdoors","layer":"place_label","field":"type","value":"village","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":22,"feature_type":"point","type":"text","maxWidth":8},"place_label_other":{"source":"outdoors","layer":"place_label","field":"type","value":["hamlet","suburb","neighbourhood"],"text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":18,"maxWidth":6,"feature_type":"point","type":"text"},"road_label_1":{"source":"outdoors","layer":"road_label","field":"class","value":["motorway","main"],"text_field":"{{name_en}}","path":"curve","padding":2,"font":"Open Sans Regular, Arial Unicode MS Regular","fontSize":18,"feature_type":"line","type":"text","maxAngleDelta":0.5},"road_label_2":{"source":"outdoors","layer":"road_label","field":"class","value":["street","street_limited"],"text_field":"{{name_en}}","path":"curve","padding":2,"font":"Open Sans Regular, Arial Unicode MS Regular","fontSize":16,"feature_type":"line","type":"text","maxAngleDelta":0.5},"road_label_3":{"source":"outdoors","layer":"road_label","field":"class","value":["service","driveway","path"],"text_field":"{{name_en}}","path":"curve","padding":2,"font":"Open Sans Regular, Arial Unicode MS Regular","fontSize":14,"feature_type":"line","type":"text","maxAngleDelta":0.5},"water_label":{"source":"outdoors","layer":"water_label","text_field":"{{name_en}}","path":"horizontal","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":12,"feature_type":"point","type":"text","maxWidth":8},"waterway_label":{"source":"outdoors","layer":"waterway_label","text_field":"{{name_en}}","path":"curve","font":"Open Sans Semibold Italic, Arial Unicode MS Bold","fontSize":12,"feature_type":"line","type":"text","maxAngleDelta":0.5},"poi":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","field":"scalerank","value":[1,2],"size":12,"type":"point"},"poi_3":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","field":"scalerank","value":3,"size":12,"type":"point"},"poi_4_4":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","filter":["and",{"field":"scalerank","value":4},{"field":"localrank","operator":"<=","value":4}],"size":12,"type":"point"},"poi_4_16":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","filter":["and",{"field":"scalerank","value":4},{"field":"localrank","operator":"<=","value":16},{"field":"localrank","operator":">","value":4}],"size":12,"type":"point"},"poi_4_all":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","field":"scalerank","value":4,"size":12,"type":"point"},"poi_aerodrome":{"source":"outdoors","layer":"poi_label","icon":"{{maki}}","field":"maki","value":"airport","size":24,"type":"point"},"poi_label_1-2":{"source":"outdoors","layer":"poi_label","field":"scalerank","value":[1,2],"text_field":"{{name_en}}","path":"horizontal","padding":2,"maxWidth":10,"verticalAlignment":"top","translate":[0,-1],"font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":12,"feature_type":"point","type":"text"},"poi_label_3":{"source":"outdoors","layer":"poi_label","field":"scalerank","value":3,"text_field":"{{name_en}}","path":"horizontal","padding":2,"maxWidth":8,"verticalAlignment":"top","translate":[0,-1],"font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":11,"feature_type":"point","type":"text"},"poi_label_4_4":{"source":"outdoors","layer":"poi_label","filter":["and",{"field":"scalerank","value":4},{"field":"localrank","operator":"<=","value":4}],"text_field":"{{name_en}}","path":"horizontal","padding":2,"maxWidth":8,"verticalAlignment":"top","translate":[0,-1],"font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":10,"feature_type":"point","type":"text"},"poi_label_4_16":{"source":"outdoors","layer":"poi_label","filter":["and",{"field":"scalerank","value":4},{"field":"localrank","operator":"<=","value":16}],"text_field":"{{name_en}}","path":"horizontal","padding":2,"maxWidth":8,"verticalAlignment":"top","translate":[0,-1],"font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":10,"feature_type":"point","type":"text"},"poi_label_4_all":{"source":"outdoors","layer":"poi_label","field":"scalerank","value":4,"text_field":"{{name_en}}","path":"horizontal","padding":2,"maxWidth":8,"verticalAlignment":"top","translate":[0,-1],"font":"Open Sans Semibold, Arial Unicode MS Bold","fontSize":10,"feature_type":"point","type":"text"}},"structure":[{"name":"background","bucket":"background"},{"name":"landcover_snow","bucket":"landcover_snow"},{"name":"landcover_crop","bucket":"landcover_crop"},{"name":"landcover_grass","bucket":"landcover_grass"},{"name":"landcover_scrub","bucket":"landcover_scrub"},{"name":"landcover_wood","bucket":"landcover_wood"},{"name":"landuse_wood","bucket":"landuse_wood"},{"name":"landuse_school","bucket":"landuse_school"},{"name":"landuse_sand","bucket":"landuse_sand"},{"name":"landuse_pitch","bucket":"landuse_pitch"},{"name":"landuse_park","bucket":"landuse_park"},{"name":"landuse_industrial","bucket":"landuse_industrial"},{"name":"landuse_scrub","bucket":"landuse_scrub"},{"name":"landuse_grass","bucket":"landuse_grass"},{"name":"landuse_crop","bucket":"landuse_crop"},{"name":"landuse_rock","bucket":"landuse_rock"},{"name":"landuse_snow","bucket":"landuse_snow"},{"name":"landuse_hospital","bucket":"landuse_hospital"},{"name":"landuse_cemetery","bucket":"landuse_cemetery"},{"name":"overlay_wetland","bucket":"overlay_wetland"},{"name":"overlay_breakwater_pier","bucket":"overlay_breakwater_pier"},{"name":"waterway_river_canal","bucket":"waterway_river_canal"},{"name":"waterway_stream","bucket":"waterway_stream"},{"name":"building_shadow","bucket":"building"},{"name":"building","bucket":"building"},{"name":"building_wall","bucket":"building"},{"name":"hillshade_full_highlight","bucket":"hillshade_full_highlight"},{"name":"hillshade_medium_highlight","bucket":"hillshade_medium_highlight"},{"name":"hillshade_medium_shadow","bucket":"hillshade_medium_shadow"},{"name":"hillshade_full_shadow","bucket":"hillshade_full_shadow"},{"name":"contour_line_loud","bucket":"contour_line_10"},{"name":"contour_line_loud","bucket":"contour_line_5"},{"name":"contour_line_regular","bucket":"contour_line_other"},{"name":"barrier_line_gate","bucket":"barrier_line_gate"},{"name":"barrier_line_fence","bucket":"barrier_line_fence"},{"name":"barrier_line_hedge","bucket":"barrier_line_hedge"},{"name":"barrier_line_land","bucket":"barrier_line_land"},{"name":"barrier_line_land_fill","bucket":"barrier_line_land_fill"},{"name":"barrier_line_cliff","bucket":"barrier_line_cliff"},{"name":"water","bucket":"water"},{"name":"aeroway_fill","bucket":"aeroway_fill"},{"name":"aeroway_runway","bucket":"aeroway_runway"},{"name":"aeroway_taxiway","bucket":"aeroway_taxiway"},{"name":"tunnel_motorway_link_casing","bucket":"tunnel_motorway_link"},{"name":"tunnel_service_casing","bucket":"tunnel_service"},{"name":"tunnel_main_casing","bucket":"tunnel_main"},{"name":"tunnel_street_casing","bucket":"tunnel_street"},{"name":"tunnel_motorway_link","bucket":"tunnel_motorway_link"},{"name":"tunnel_service","bucket":"tunnel_service"},{"name":"tunnel_street","bucket":"tunnel_street"},{"name":"tunnel_main","bucket":"tunnel_main"},{"name":"tunnel_motorway_casing","bucket":"tunnel_motorway"},{"name":"tunnel_motorway","bucket":"tunnel_motorway"},{"name":"road_path_case","bucket":"tunnel_path"},{"name":"road_path_footway","bucket":"tunnel_path_footway"},{"name":"road_path_path","bucket":"tunnel_path_path"},{"name":"road_path_cycleway","bucket":"tunnel_path_cycleway"},{"name":"road_path_mtb","bucket":"tunnel_path_mtb"},{"name":"road_path_piste","bucket":"tunnel_path_piste"},{"name":"road_path_steps","bucket":"tunnel_path_steps"},{"name":"road_major_rail","bucket":"tunnel_major_rail"},{"name":"road_major_rail_hatching","bucket":"tunnel_major_rail"},{"name":"road_motorway_link_casing","bucket":"motorway_link"},{"name":"road_service_casing","bucket":"service"},{"name":"road_main_casing","bucket":"main"},{"name":"road_street_casing","bucket":"street"},{"name":"road_motorway_casing","bucket":"motorway"},{"name":"road_motorway_link","bucket":"motorway_link"},{"name":"road_service","bucket":"service"},{"name":"road_street","bucket":"street"},{"name":"road_main","bucket":"main"},{"name":"road_motorway","bucket":"motorway"},{"name":"road_path_case","bucket":"path"},{"name":"road_path_footway","bucket":"path_footway"},{"name":"road_path_path","bucket":"path_path"},{"name":"road_path_cycleway","bucket":"path_cycleway"},{"name":"road_path_mtb","bucket":"path_mtb"},{"name":"road_path_piste","bucket":"path_piste"},{"name":"road_path_steps","bucket":"path_steps"},{"name":"road_major_rail","bucket":"major_rail"},{"name":"road_major_rail_hatching","bucket":"major_rail"},{"name":"bridge_motorway_link_casing","bucket":"bridge_motorway_link"},{"name":"bridge_service_casing","bucket":"bridge_service"},{"name":"bridge_main_casing","bucket":"bridge_main"},{"name":"bridge_street_casing","bucket":"bridge_street"},{"name":"bridge_motorway_link","bucket":"bridge_motorway_link"},{"name":"bridge_service","bucket":"bridge_service"},{"name":"bridge_street","bucket":"bridge_street"},{"name":"bridge_main","bucket":"bridge_main"},{"name":"bridge_motorway_casing","bucket":"bridge_motorway"},{"name":"bridge_motorway","bucket":"bridge_motorway"},{"name":"road_path_footway","bucket":"bridge_path_footway"},{"name":"road_path_path","bucket":"bridge_path_path"},{"name":"road_path_cycleway","bucket":"bridge_path_cycleway"},{"name":"road_path_mtb","bucket":"bridge_path_mtb"},{"name":"road_path_piste","bucket":"bridge_path_piste"},{"name":"road_path_steps","bucket":"bridge_path_steps"},{"name":"bridge_aerialway_casing","bucket":"bridge_aerialway"},{"name":"bridge_aerialway","bucket":"bridge_aerialway"},{"name":"road_major_rail","bucket":"bridge_major_rail"},{"name":"road_major_rail_hatching","bucket":"bridge_major_rail"},{"name":"admin_l3","bucket":"admin_l3"},{"name":"admin_l2","bucket":"admin_l2"},{"name":"admin_maritime_cover","bucket":"admin_maritime"},{"name":"admin_maritime","bucket":"admin_maritime"},{"name":"country_label_line","bucket":"country_label_line"},{"name":"country_label","bucket":"country_label"},{"name":"marine_label_line_1","bucket":"marine_label_line_1"},{"name":"marine_label_line_2","bucket":"marine_label_line_2"},{"name":"marine_label_line_3","bucket":"marine_label_line_3"},{"name":"marine_label_line_other","bucket":"marine_label_line_other"},{"name":"marine_label_point_1","bucket":"marine_label_point_1"},{"name":"marine_label_point_2","bucket":"marine_label_point_2"},{"name":"marine_label_point_3","bucket":"marine_label_point_3"},{"name":"marine_label_point_other","bucket":"marine_label_point_other"},{"name":"state_label","bucket":"state_label"},{"name":"place_label_city","bucket":"place_label_city"},{"name":"place_label_town","bucket":"place_label_town"},{"name":"place_label_village","bucket":"place_label_village"},{"name":"place_label_other","bucket":"place_label_other"},{"name":"water_label","bucket":"water_label"},{"name":"poi_aerodrome","bucket":"poi_aerodrome"},{"name":"poi","bucket":"poi"},{"name":"poi_label_1-2","bucket":"poi_label_1-2"},{"name":"road_label_1","bucket":"road_label_1"},{"name":"poi_3","bucket":"poi_3"},{"name":"poi_label_3","bucket":"poi_label_3"},{"name":"road_label_2","bucket":"road_label_2"},{"name":"road_label_3","bucket":"road_label_3"},{"name":"contour_label","bucket":"contour_label"},{"name":"waterway_label","bucket":"waterway_label"},{"name":"poi_4_4","bucket":"poi_4_4"},{"name":"poi_label_4_4","bucket":"poi_label_4_4"},{"name":"poi_4_16","bucket":"poi_4_16"},{"name":"poi_label_4_16","bucket":"poi_label_4_16"},{"name":"poi_4_all","bucket":"poi_4_all"},{"name":"poi_label_4_all","bucket":"poi_label_4_all"}],"constants":{"land":"rgb(244,239,225)","water":"#cdd","water_dark":"#185869","crop":"#eeeed4","grass":"#e6e6cc","scrub":"#dfe5c8","wood":"#cee2bd","snow":"#f4f8ff","rock":"#ddd","sand":"#ffd","cemetery":"#edf4ed","pitch":"#fff","park":"#d4e4bc","piste":"blue","school":"#e8dfe0","hospital":"#f8eee0","builtup":"#f6faff","case":"#fff","motorway":"#cda0a0","main":"#ddc0b9","street":"#fff","text":"#666","text_stroke":"rgba(255,255,255,0.8)","country_text":"#222","marine_text":"#a0bdc0","water_text":"#185869","land_night":"#017293","water_night":"#103","water_dark_night":"#003366","crop_night":"#178d96","grass_night":"#23948a","scrub_night":"#31a186","wood_night":"#45b581","park_night":"#51bd8b","snow_night":"#5ad9fe","rock_night":"#999","sand_night":"#437162","cemetery_night":"#218c96","pitch_night":"rgba(255,255,255,0.2)","school_night":"#01536a","hospital_night":"#015e7a","builtup_night":"#014b60","admin_night":"#ffb680","text_night":"#fff","text_water_night":"#2a5b8a","text_stroke_night":"#103","text2_stroke_night":"rgba(1,69,89,0.8)","case_night":"#015e7a","street_case_night":"#015b76","motorway_night":"#bbdde7","main_night":"#64b2c9","street_night":"#0186ac","contour_night":"#ffff80","river_canal_width":["stops",{"z":11,"val":0.5},{"z":12,"val":1},{"z":14,"val":2},{"z":16,"val":3}],"stream_width":["stops",{"z":13,"val":0.25},{"z":14,"val":0.5},{"z":16,"val":1.5},{"z":18,"val":2}],"motorway_width":["stops",{"z":5,"val":0},{"z":6,"val":0.5},{"z":8,"val":0.8},{"z":10,"val":1},{"z":11,"val":1.2},{"z":12,"val":2},{"z":13,"val":3},{"z":14,"val":4},{"z":15,"val":6},{"z":16,"val":9},{"z":17,"val":12},{"z":18,"val":14}],"motorway_casing_width":["stops",{"z":7.5,"val":0.6},{"z":8,"val":0.8},{"z":10,"val":2.8},{"z":11,"val":3},{"z":12,"val":4},{"z":13,"val":5},{"z":14,"val":6.5},{"z":15,"val":9},{"z":16,"val":12},{"z":17,"val":15},{"z":18,"val":17}],"motorway_link_width":["stops",{"z":12,"val":1.2},{"z":14,"val":2},{"z":16,"val":3},{"z":18,"val":4}],"motorway_link_casing_width":["stops",{"z":12,"val":2.8},{"z":14,"val":3.5},{"z":16,"val":5},{"z":18,"val":6}],"main_width":["stops",{"z":5,"val":1},{"z":12,"val":1},{"z":13,"val":1.5},{"z":14,"val":2},{"z":15,"val":3},{"z":16,"val":6},{"z":17,"val":10},{"z":18,"val":12}],"main_casing_width":["stops",{"z":9,"val":2.9},{"z":12,"val":2.9},{"z":13,"val":3.5},{"z":14,"val":4},{"z":15,"val":5.5},{"z":16,"val":9},{"z":17,"val":12},{"z":18,"val":14}],"street_width":["stops",{"z":14.5,"val":0},{"z":15,"val":1.5},{"z":16,"val":3},{"z":17,"val":8}],"street_casing_width":["stops",{"z":13,"val":0.4},{"z":14,"val":1},{"z":15,"val":2.5},{"z":16,"val":4},{"z":17,"val":10}],"street_casing_opacity":["stops",{"z":14,"val":0},{"z":14.5,"val":1}],"service_casing_width":["stops",{"z":14,"val":0.5},{"z":15,"val":3},{"z":16,"val":3.5},{"z":17,"val":4},{"z":18,"val":5},{"z":19,"val":6}],"runway_width":["stops",{"z":10,"val":1},{"z":11,"val":2},{"z":12,"val":3},{"z":13,"val":5},{"z":14,"val":7},{"z":15,"val":11},{"z":16,"val":15},{"z":17,"val":19},{"z":18,"val":23}],"taxiway_width":["stops",{"z":10,"val":0.2},{"z":12,"val":0.2},{"z":13,"val":1},{"z":14,"val":1.5},{"z":15,"val":2},{"z":16,"val":3},{"z":17,"val":4},{"z":18,"val":5}],"aerialway_width":["stops",{"z":13.5,"val":0.8},{"z":14,"val":1.4},{"z":15,"val":1.6},{"z":16,"val":2},{"z":17,"val":2.4},{"z":18,"val":3}],"aerialway_casing_width":["stops",{"z":13.5,"val":2},{"z":14,"val":2.5},{"z":15,"val":3},{"z":16,"val":3.5},{"z":17,"val":4},{"z":22,"val":5}],"path_width":["stops",{"z":14,"val":1.2},{"z":15,"val":1.5},{"z":16,"val":1.8}],"admin_l2_width":["stops",{"z":2,"val":0.5},{"z":3,"val":0.7},{"z":4,"val":0.7},{"z":5,"val":0.8},{"z":6,"val":1},{"z":8,"val":2},{"z":10,"val":3}],"admin_l3_width":["stops",{"z":6,"val":0.6},{"z":8,"val":1},{"z":12,"val":2}],"road_label_1_size":["stops",{"z":13,"val":11},{"z":14,"val":12},{"z":15,"val":13},{"z":16,"val":14},{"z":17,"val":16},{"z":18,"val":18}],"road_label_2_size":["stops",{"z":13,"val":11},{"z":14,"val":12},{"z":16,"val":14},{"z":18,"val":16}],"road_label_3_size":["stops",{"z":15,"val":10},{"z":16,"val":12},{"z":18,"val":14}],"fence_width":["stops",{"z":17,"val":0.6},{"z":19,"val":1}],"hedge_width":["stops",{"z":16,"val":0.6},{"z":17,"val":1.2},{"z":19,"val":1.6}],"barrier_line_land_width":["stops",{"z":14,"val":0.4},{"z":15,"val":0.75},{"z":16,"val":1.5},{"z":17,"val":3},{"z":18,"val":6},{"z":19,"val":12},{"z":20,"val":24},{"z":21,"val":48}],"country_label_size":["stops",{"z":1,"val":14},{"z":12,"val":24}],"poi_label_1-2_size":["stops",{"z":15,"val":10},{"z":16,"val":11},{"z":17,"val":12}],"poi_label_3_size":["stops",{"z":16,"val":10},{"z":17,"val":11}],"hillshade_prerender":["stops",{"z":11,"val":0},{"z":12,"val":1}],"hillshade_prerender_size":["stops",{"z":11,"val":1056},{"z":12,"val":512},{"z":13,"val":256}]},"classes":[{"name":"default","layers":{"background":{"color":"land"},"admin_maritime_cover":{"color":"water","width":5},"admin_maritime":{"color":"#c0d6d6","width":["stops",{"z":6,"val":1},{"z":8,"val":2},{"z":12,"val":3}]},"admin_l2":{"color":"#88a","width":"admin_l2_width"},"admin_l3":{"color":"#88a","dasharray":[60,20],"opacity":["stops",{"z":4,"val":0},{"z":6,"val":1}],"width":"admin_l3_width"},"waterway_river_canal":{"color":"#87abaf","width":"river_canal_width"},"waterway_stream":{"color":"#87abaf","width":"stream_width"},"barrier_line_gate":{"width":2.5,"color":"#aab"},"barrier_line_fence":{"color":"#aeada3","width":"fence_width"},"barrier_line_hedge":{"color":"#8de99b","width":"hedge_width"},"barrier_line_land":{"color":"land","width":"barrier_line_land_width"},"barrier_line_land_fill":{"color":"land"},"barrier_line_cliff":{"color":"#987","width":4},"landcover_wood":{"color":"wood","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_scrub":{"color":"scrub","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_grass":{"color":"grass","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_crop":{"color":"crop"},"landcover_snow":{"color":"snow"},"landuse_wood":{"color":"wood"},"landuse_scrub":{"color":"scrub"},"landuse_grass":{"color":"grass"},"landuse_crop":{"color":"crop"},"landuse_snow":{"color":"snow"},"landuse_rock":{"color":"rock"},"landuse_sand":{"color":"sand"},"landuse_park":{"color":"park"},"landuse_cemetery":{"color":"cemetery"},"landuse_hospital":{"color":"hospital"},"landuse_school":{"color":"school"},"landuse_pitch":{"color":"rgba(255,255,255,0.5)","stroke":"pitch"},"landuse_industrial":{"color":"rgba(246,250,255,0.5)"},"overlay_wetland":{"color":"rgba(210,225,225,0.2)","image":"wetland_noveg_64"},"overlay_breakwater_pier":{"color":"land"},"hillshade_full_shadow":{"color":"#103","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":15,"val":0.08},{"z":16,"val":0.075},{"z":17,"val":0.05},{"z":18,"val":0.05},{"z":19,"val":0.025}]},"hillshade_medium_shadow":{"color":"#206","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":15,"val":0.08},{"z":16,"val":0.075},{"z":17,"val":0.05},{"z":18,"val":0.05},{"z":19,"val":0.025}]},"hillshade_full_highlight":{"color":"#fffff3","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":15,"val":0.3},{"z":16,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.2},{"z":19,"val":0.1}]},"hillshade_medium_highlight":{"color":"#ffd","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":15,"val":0.3},{"z":16,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.2},{"z":19,"val":0.1}]},"contour_line_loud":{"color":"#008","width":0.9,"opacity":["stops",{"z":12,"val":0.05},{"z":13,"val":0.11}]},"contour_line_regular":{"color":"#008","width":0.5,"opacity":["stops",{"z":12,"val":0.05},{"z":13,"val":0.11}]},"contour_label":{"color":"text","stroke":"land","strokeWidth":0.3,"strokeBlur":3,"size":10},"water":{"color":"water","stroke":"#a2bdc0"},"aeroway_fill":{"color":"#ddd"},"aeroway_runway":{"color":"#ddd","width":"runway_width"},"aeroway_taxiway":{"color":"#ddd","width":"taxiway_width"},"building":{"color":"#ebe7db"},"building_wall":{"color":"#ebe7db","stroke":"#d5d1c6","opacity":["stops",{"z":16.5,"val":0},{"z":17,"val":0.7}]},"building_shadow":{"color":"#d5d1c6","stroke":"#d5d1c6","translate":[1,1],"opacity":["stops",{"z":16.5,"val":0},{"z":17,"val":1}]},"tunnel_motorway_casing":{"color":"case","dasharray":[6,6],"width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"tunnel_motorway":{"color":"#e6cec7","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"tunnel_main_casing":{"color":"case","dasharray":[6,6],"width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"tunnel_main":{"color":"#e6cec7","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"tunnel_motorway_link_casing":{"color":"case","dasharray":[6,6],"width":"motorway_link_casing_width"},"tunnel_motorway_link":{"color":"#e6cec7","width":"motorway_link_width"},"tunnel_street_casing":{"color":"#d9d5c6","width":"street_casing_width","opacity":"street_casing_opacity"},"tunnel_street":{"color":"#d9d5c6","width":"street_width"},"tunnel_service_casing":{"color":"#000","opacity":0.04,"dasharray":[6,6],"width":"service_casing_width"},"tunnel_service":{"color":"#e6cec7","width":2},"road_motorway_casing":{"color":"case","width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"road_motorway":{"color":"motorway","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"road_main_casing":{"color":"case","width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"road_main":{"color":"main","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"road_motorway_link_casing":{"color":"case","width":"motorway_link_casing_width"},"road_motorway_link":{"color":"motorway","width":"motorway_link_width"},"road_street_casing":{"color":"#d9d5c6","width":"street_casing_width","opacity":"street_casing_opacity"},"road_street":{"color":"street","width":"street_width"},"road_service_casing":{"color":"#000","opacity":0.04,"width":"service_casing_width"},"road_service":{"color":"street","width":2},"road_path_case":{"color":"#ffd","opacity":0.4,"width":["stops",{"z":15,"val":3},{"z":16,"val":4}]},"road_path_footway":{"color":"#bba","dasharray":[10,4],"width":"path_width"},"road_path_path":{"color":"#987","dasharray":[10,4],"opacity":0.8,"width":["stops",{"z":14,"val":0.8},{"z":15,"val":0.9},{"z":16,"val":1.2}]},"road_path_cycleway":{"color":"#488","dasharray":[10,4],"width":"path_width"},"road_path_mtb":{"color":"#488","dasharray":[12,4],"width":"path_width"},"road_path_piste":{"color":"#87b","dasharray":[8,4],"width":"path_width"},"road_path_steps":{"color":"#bba","dasharray":[10,4],"width":4},"road_major_rail":{"color":"#c8c4c0","width":0.8},"road_major_rail_hatching":{"color":"#c8c4c0","dasharray":[2,31],"width":5},"bridge_motorway_casing":{"color":"case","width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"bridge_motorway":{"color":"motorway","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"bridge_main_casing":{"color":"case","width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"bridge_main":{"color":"main","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"bridge_motorway_link_casing":{"color":"case","width":"motorway_link_casing_width"},"bridge_motorway_link":{"color":"motorway","width":"motorway_link_width"},"bridge_street_casing":{"color":"#d9d5c6","width":"street_casing_width","opacity":"street_casing_opacity"},"bridge_street":{"color":"street","width":"street_width"},"bridge_service_casing":{"color":"#000","opacity":0.04,"width":"service_casing_width"},"bridge_service":{"color":"street","width":2},"bridge_aerialway_casing":{"color":"white","opacity":0.5,"width":"aerialway_casing_width"},"bridge_aerialway":{"color":"#876","opacity":0.5,"width":"aerialway_width"},"country_label":{"color":"country_text","stroke":"rgba(255,255,255,0.5)","strokeWidth":0.5,"size":"country_label_size"},"country_label_line":{"color":"country_text","width":0.5,"opacity":0.5},"marine_label_line_1":{"color":"marine_text","size":["stops",{"z":3,"val":20},{"z":4,"val":25},{"z":5,"val":30},{"z":22,"val":30}],"stroke":"water"},"marine_label_line_2":{"color":"marine_text","size":["stops",{"z":3,"val":13},{"z":4,"val":14},{"z":5,"val":20},{"z":6,"val":24},{"z":22,"val":24}],"stroke":"water"},"marine_label_line_3":{"color":"marine_text","size":["stops",{"z":3,"val":12},{"z":4,"val":13},{"z":5,"val":15},{"z":6,"val":18},{"z":22,"val":18}],"stroke":"water"},"marine_label_line_other":{"color":"marine_text","size":["stops",{"z":4,"val":12},{"z":5,"val":14},{"z":6,"val":16},{"z":22,"val":16}],"stroke":"water"},"marine_label_point_1":{"color":"marine_text","size":["stops",{"z":3,"val":20},{"z":4,"val":25},{"z":5,"val":30},{"z":22,"val":30}],"stroke":"water"},"marine_label_point_2":{"color":"marine_text","size":["stops",{"z":3,"val":13},{"z":4,"val":14},{"z":5,"val":20},{"z":6,"val":24},{"z":22,"val":24}],"stroke":"water"},"marine_label_point_3":{"color":"marine_text","size":["stops",{"z":3,"val":12},{"z":4,"val":13},{"z":5,"val":15},{"z":6,"val":18},{"z":22,"val":18}],"stroke":"water"},"marine_label_point_other":{"color":"marine_text","size":["stops",{"z":4,"val":12},{"z":5,"val":14},{"z":6,"val":16},{"z":22,"val":16}],"stroke":"water"},"state_label":{"color":"#333","strokeWidth":0.4,"strokeBlur":1,"stroke":"rgba(244,239,225,0.8)","size":["stops",{"z":3.99,"val":0},{"z":4,"val":10},{"z":9.99,"val":16},{"z":10,"val":0}]},"place_label_city":{"color":"#444","strokeWidth":0.4,"stroke":"text_stroke","size":["stops",{"z":3.99,"val":0},{"z":4,"val":10},{"z":7,"val":14},{"z":14.99,"val":20},{"z":15,"val":0}]},"place_label_town":{"color":"#716656","strokeWidth":0.3,"strokeBlur":2,"stroke":"text_stroke","size":["stops",{"z":9,"val":10},{"z":12,"val":13},{"z":14,"val":17},{"z":16,"val":22}]},"place_label_village":{"color":"#635644","strokeWidth":0.3,"strokeBlur":2,"stroke":"text_stroke","size":["stops",{"z":9,"val":8},{"z":12,"val":10},{"z":14,"val":14},{"z":16,"val":16},{"z":17,"val":20}]},"place_label_other":{"color":"#7d6c55","stroke":"text_stroke","size":["stops",{"z":13,"val":11},{"z":14,"val":12},{"z":16,"val":14},{"z":18,"val":18}]},"road_label_1":{"color":"#585042","stroke":"land","strokeWidth":0.6,"strokeBlur":2,"size":"road_label_1_size"},"road_label_2":{"color":"#585042","stroke":"land","strokeWidth":0.6,"strokeBlur":2,"size":"road_label_2_size"},"road_label_3":{"color":"#585042","stroke":"land","strokeWidth":0.6,"strokeBlur":2,"size":"road_label_3_size"},"water_label":{"color":"water_dark","stroke":"rgba(255,255,255,0.75)"},"waterway_label":{"color":"water_dark","strokeWidth":0.4,"strokeBlur":2,"stroke":"text_stroke"},"poi":{"antialias":false},"poi_3":{"antialias":false,"opacity":["stops",{"z":17.5,"val":0},{"z":17.75,"val":1}]},"poi_4_4":{"antialias":false,"opacity":["stops",{"z":18,"val":0},{"z":18.25,"val":1}]},"poi_4_16":{"antialias":false,"opacity":["stops",{"z":18.5,"val":0},{"z":18.75,"val":1}]},"poi_4_all":{"antialias":false,"opacity":["stops",{"z":18.75,"val":0},{"z":19,"val":1}]},"poi_label_1-2":{"color":"#444","size":"poi_label_1-2_size","stroke":"land","strokeWidth":0.3,"strokeBlur":1},"poi_label_3":{"color":"#444","size":"poi_label_3_size","stroke":"land","strokeWidth":0.3,"strokeBlur":1,"opacity":["stops",{"z":17.5,"val":0},{"z":17.75,"val":1}]},"poi_label_4_4":{"color":"#444","size":10,"opacity":["stops",{"z":18,"val":0},{"z":17.25,"val":1}],"stroke":"land","strokeWidth":0.3,"strokeBlur":1},"poi_label_4_16":{"color":"#444","size":10,"opacity":["stops",{"z":18.25,"val":0},{"z":18.75,"val":1}],"stroke":"land","strokeWidth":0.3,"strokeBlur":1},"poi_label_4_all":{"color":"#444","size":10,"opacity":["stops",{"z":18.75,"val":0},{"z":19,"val":1}],"stroke":"land","strokeWidth":0.3,"strokeBlur":1},"poi_aerodrome":{"opacity":["stops",{"z":13,"val":0},{"z":13.25,"val":1}],"antialias":false}}},{"name":"night","layers":{"background":{"color":"land_night"},"admin_maritime_cover":{"color":"water_night","width":5},"admin_maritime":{"color":"#0a1347","width":["stops",{"z":6,"val":1},{"z":8,"val":2},{"z":12,"val":3}]},"admin_l2":{"color":"admin_night","width":"admin_l2_width"},"admin_l3":{"color":"admin_night","dasharray":[60,20],"opacity":["stops",{"z":4,"val":0},{"z":6,"val":1}],"width":"admin_l3_width"},"waterway_river_canal":{"color":"rgb(10,20,71)","width":"river_canal_width"},"waterway_stream":{"color":"rgb(10,20,71)","width":"stream_width"},"barrier_line_gate":{"width":2.5,"color":"#59596f"},"barrier_line_fence":{"color":"#014b61","width":"fence_width"},"barrier_line_hedge":{"color":"#2e7a57","width":"hedge_width"},"barrier_line_land":{"color":"land_night","width":"barrier_line_land_width"},"barrier_line_land_fill":{"color":"land_night"},"barrier_line_cliff":{"color":"#63574b","width":4},"landcover_wood":{"color":"wood_night","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_scrub":{"color":"scrub_night","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_grass":{"color":"grass_night","opacity":["stops",{"z":13,"val":1},{"z":14,"val":0.8},{"z":17,"val":0.2}]},"landcover_crop":{"color":"crop_night"},"landcover_snow":{"color":"snow_night"},"landuse_wood":{"color":"wood_night","opacity":0.8},"landuse_scrub":{"color":"scrub_night","opacity":0.8},"landuse_grass":{"color":"grass_night","opacity":0.8},"landuse_crop":{"color":"crop_night","opacity":0.8},"landuse_snow":{"color":"snow_night","opacity":0.8},"landuse_rock":{"color":"rock_night","opacity":0.8},"landuse_sand":{"color":"sand_night","opacity":0.8},"landuse_park":{"color":"park_night"},"landuse_cemetery":{"color":"cemetery_night"},"landuse_hospital":{"color":"hospital_night"},"landuse_school":{"color":"school_night"},"landuse_pitch":{"color":"pitch_night","stroke":"pitch"},"landuse_industrial":{"color":"builtup_night"},"overlay_wetland":{"color":"rgba(210,225,225,0.2)","image":"wetland_noveg_64"},"overlay_breakwater_pier":{"color":"land_night"},"hillshade_full_shadow":{"color":"#103","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":16,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.1},{"z":19,"val":0.05}]},"hillshade_medium_shadow":{"color":"#206","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":16,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.1},{"z":19,"val":0.05}]},"hillshade_full_highlight":{"color":"#fdfdad","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":14,"val":0.4},{"z":15,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.1},{"z":19,"val":0.05}]},"hillshade_medium_highlight":{"color":"#ffe1b7","antialias":false,"prerender":"hillshade_prerender","prerender-size":"hillshade_prerender_size","prerender-blur":1,"opacity":["stops",{"z":15,"val":0.3},{"z":17,"val":0.2},{"z":18,"val":0.15},{"z":19,"val":0.05}]},"contour_line_loud":{"color":"contour_night","width":0.9,"opacity":["stops",{"z":12,"val":0.1},{"z":13,"val":0.2}]},"contour_line_regular":{"color":"contour_night","width":0.5,"opacity":["stops",{"z":12,"val":0.1},{"z":13,"val":0.4}]},"contour_label":{"color":"contour_night","stroke":"land_night","strokeWidth":0.3,"strokeBlur":3,"size":10},"water":{"color":"water_night","stroke":"water_dark_night"},"aeroway_fill":{"color":"#367"},"aeroway_runway":{"color":"#367","width":"runway_width"},"aeroway_taxiway":{"color":"#367","width":"taxiway_width"},"building":{"color":"#027797"},"building_wall":{"color":"#027797","stroke":"#026688","opacity":["stops",{"z":16.5,"val":0},{"z":17,"val":0.7}]},"building_shadow":{"color":"#026688","stroke":"#026688","translate":[1,1],"opacity":["stops",{"z":16.5,"val":0},{"z":17,"val":1}]},"tunnel_motorway_casing":{"color":"case_night","dasharray":[6,6],"width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"tunnel_motorway":{"color":"#78b0c1","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"tunnel_main_casing":{"color":"case_night","dasharray":[6,6],"width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"tunnel_main":{"color":"#78b0c1","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"tunnel_motorway_link_casing":{"color":"case_night","dasharray":[6,6],"width":"motorway_link_casing_width"},"tunnel_motorway_link":{"color":"#78b0c1","width":"motorway_link_width"},"tunnel_street_casing":{"color":"street_case_night","width":"street_casing_width","opacity":"street_casing_opacity"},"tunnel_street":{"color":"street_night","width":"street_width"},"tunnel_service_casing":{"color":"#000","opacity":0.04,"dasharray":[6,6],"width":"service_casing_width"},"tunnel_service":{"color":"#017ca0","width":2},"road_motorway_casing":{"color":"case_night","width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"road_motorway":{"color":"motorway_night","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"road_main_casing":{"color":"case_night","width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"road_main":{"color":"main_night","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"road_motorway_link_casing":{"color":"case_night","width":"motorway_link_casing_width"},"road_motorway_link":{"color":"motorway_night","width":"motorway_link_width"},"road_street_casing":{"color":"street_case_night","width":"street_casing_width","opacity":"street_casing_opacity"},"road_street":{"color":"street_night","width":"street_width"},"road_service_casing":{"color":"#000","opacity":0.04,"width":"service_casing_width"},"road_service":{"color":"street_night","width":2},"road_path_case":{"color":"land_night","opacity":0.2},"road_path_footway":{"color":"#fff","dasharray":[10,4],"width":"path_width"},"road_path_path":{"color":"#fff","dasharray":[10,4],"opacity":0.8,"width":["stops",{"z":14,"val":0.8},{"z":15,"val":0.9},{"z":16,"val":1.2}]},"road_path_cycleway":{"color":"#94e6ff","dasharray":[10,4],"width":"path_width"},"road_path_mtb":{"color":"#94e6ff","dasharray":[12,4],"width":"path_width"},"road_path_piste":{"color":"#715dae","dasharray":[8,4],"width":"path_width"},"road_path_steps":{"color":"#016684","dasharray":[10,4],"opacity":0.3,"width":6},"road_major_rail":{"color":"#c8c4c0","width":0.8},"road_major_rail_hatching":{"color":"#c8c4c0","dasharray":[2,31],"width":5},"bridge_motorway_casing":{"color":"case_night","width":"motorway_casing_width","opacity":["stops",{"z":9.5,"val":0},{"z":10,"val":1}]},"bridge_motorway":{"color":"motorway_night","width":"motorway_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"bridge_main_casing":{"color":"case_night","width":"main_casing_width","opacity":["stops",{"z":9,"val":0},{"z":10,"val":1}]},"bridge_main":{"color":"main_night","width":"main_width","opacity":["stops",{"z":6.5,"val":0},{"z":7,"val":1}]},"bridge_motorway_link_casing":{"color":"case_night","width":"motorway_link_casing_width"},"bridge_motorway_link":{"color":"motorway_night","width":"motorway_link_width"},"bridge_street_casing":{"color":"street_case_night","width":"street_casing_width","opacity":"street_casing_opacity"},"bridge_street":{"color":"street_night","width":"street_width"},"bridge_service_casing":{"color":"#000","opacity":0.04,"width":"service_casing_width"},"bridge_service":{"color":"street_night","width":2},"bridge_aerialway_casing":{"color":"white","opacity":0.5,"width":"aerialway_casing_width"},"bridge_aerialway":{"color":"#876","opacity":0.5,"width":"aerialway_width"},"country_label":{"color":"text_night","stroke":"text2_stroke_night","strokeWidth":0.4,"strokeBlur":2,"size":"country_label_size"},"country_label_line":{"color":"text_night","width":0.5,"opacity":0.5},"marine_label_line_1":{"color":"water_dark_night","size":["stops",{"z":3,"val":20},{"z":4,"val":25},{"z":5,"val":30},{"z":22,"val":30}],"stroke":"water_night"},"marine_label_line_2":{"color":"water_dark_night","size":["stops",{"z":3,"val":13},{"z":4,"val":14},{"z":5,"val":20},{"z":6,"val":24},{"z":22,"val":24}],"stroke":"water_night"},"marine_label_line_3":{"color":"water_dark_night","size":["stops",{"z":3,"val":12},{"z":4,"val":13},{"z":5,"val":15},{"z":6,"val":18},{"z":22,"val":18}],"stroke":"water_night"},"marine_label_line_other":{"color":"water_dark_night","size":["stops",{"z":4,"val":12},{"z":5,"val":14},{"z":6,"val":16},{"z":22,"val":16}],"stroke":"water_night"},"marine_label_point_1":{"color":"water_dark_night","size":["stops",{"z":3,"val":20},{"z":4,"val":25},{"z":5,"val":30},{"z":22,"val":30}],"stroke":"water_night"},"marine_label_point_2":{"color":"water_dark_night","size":["stops",{"z":3,"val":13},{"z":4,"val":14},{"z":5,"val":20},{"z":6,"val":24},{"z":22,"val":24}],"stroke":"water_night"},"marine_label_point_3":{"color":"water_dark_night","size":["stops",{"z":3,"val":12},{"z":4,"val":13},{"z":5,"val":15},{"z":6,"val":18},{"z":22,"val":18}],"stroke":"water_night"},"marine_label_point_other":{"color":"water_dark_night","size":["stops",{"z":4,"val":12},{"z":5,"val":14},{"z":6,"val":16},{"z":22,"val":16}],"stroke":"water_night"},"state_label":{"color":"#fff","strokeWidth":0.4,"strokeBlur":1,"stroke":"land_night","size":["stops",{"z":3.99,"val":0},{"z":4,"val":10},{"z":9.99,"val":16},{"z":10,"val":0}]},"place_label_city":{"color":"#fff","strokeWidth":0.4,"stroke":"text2_stroke_night","size":["stops",{"z":3.99,"val":0},{"z":4,"val":10},{"z":7,"val":14},{"z":14.99,"val":20},{"z":15,"val":0}]},"place_label_town":{"color":"text_night","strokeWidth":0.3,"strokeBlur":2,"stroke":"text2_stroke_night","size":["stops",{"z":9,"val":10},{"z":12,"val":13},{"z":14,"val":17},{"z":16,"val":22}]},"place_label_village":{"color":"text_night","strokeWidth":0.3,"strokeBlur":2,"stroke":"text2_stroke_night","size":["stops",{"z":9,"val":8},{"z":12,"val":10},{"z":14,"val":14},{"z":16,"val":16},{"z":17,"val":20}]},"place_label_other":{"color":"text_night","stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":2,"size":["stops",{"z":13,"val":11},{"z":14,"val":12},{"z":16,"val":14},{"z":18,"val":18}]},"road_label_1":{"color":"text_night","stroke":"text2_stroke_night","strokeWidth":0.5,"strokeBlur":3,"size":"road_label_1_size"},"road_label_2":{"color":"text_night","stroke":"text2_stroke_night","strokeWidth":0.5,"strokeBlur":3,"size":"road_label_2_size"},"road_label_3":{"color":"text_night","stroke":"text2_stroke_night","strokeWidth":0.5,"strokeBlur":3,"size":"road_label_3_size"},"water_label":{"color":"text_water_night","stroke":"water_night"},"waterway_label":{"color":"text_water_night","stroke":"water_night"},"poi":{"color":"white","antialias":false},"poi_3":{"antialias":false,"opacity":["stops",{"z":17.5,"val":0},{"z":17.75,"val":1}]},"poi_4_4":{"antialias":false,"opacity":["stops",{"z":18,"val":0},{"z":17.25,"val":1}]},"poi_4_16":{"antialias":false,"opacity":["stops",{"z":18.5,"val":0},{"z":18.75,"val":1}]},"poi_4_all":{"antialias":false,"opacity":["stops",{"z":18.75,"val":0},{"z":19,"val":1}]},"poi_label_1-2":{"color":"#fff","size":"poi_label_1-2_size","stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":1},"poi_label_3":{"color":"#fff","size":"poi_label_3_size","stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":1,"opacity":["stops",{"z":17.5,"val":0},{"z":17.75,"val":1}]},"poi_label_4_4":{"color":"#fff","size":10,"opacity":["stops",{"z":18,"val":0},{"z":18.25,"val":1}],"stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":1},"poi_label_4_16":{"color":"#fff","size":10,"opacity":["stops",{"z":18.5,"val":0},{"z":18.75,"val":1}],"stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":1},"poi_label_4_all":{"color":"#fff","size":10,"opacity":["stops",{"z":18.75,"val":0},{"z":19,"val":1}],"stroke":"text2_stroke_night","strokeWidth":0.3,"strokeBlur":1},"poi_aerodrome":{"opacity":["stops",{"z":13,"val":0},{"z":13.25,"val":1}],"antialias":false}}}],"sprite":"/img/maki-sprite"} \ No newline at end of file From 2853d797801a8f5bc96feeda498bef83561293d6 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 11:36:15 +1100 Subject: [PATCH 134/415] Revert "Travis needs submodules too" This reverts commit d1389cc7ec5a687021be3bed13a14d46886b8c74. --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index e766c9db4..1d03340c8 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,6 @@ build/linux/mapboxgl-app.xcodeproj: linux/mapboxgl-app.gyp config.gypi .PHONY: android android: - git submodule update --init --recursive && \ ./scripts/local_mason.sh && \ MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env PATH && \ export CXX="`MASON_DIR=./.mason MASON_PLATFORM=android ./.mason/mason env CXX`" && \ From 1a7b9a1669645efd95b5cfb3dc069992cc4a4d39 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 11:38:06 +1100 Subject: [PATCH 135/415] Make Travis load submodules in init --- scripts/travis_before_install.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 05bd60aba..8370eefc8 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -7,6 +7,9 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then + echo "debug: load submodules" + git submodule update --init --recursive + echo "debug: installing 7z" #mapbox_time "installing 7z" \ MASON_PLATFORM= ./.mason/mason install 7z 9.20 From 6ffb4951df985f1e887556bb78e0592d69c06f7c Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 18:57:38 +1100 Subject: [PATCH 136/415] Update to new build tools --- android/java/app/build.gradle | 2 +- android/java/lib/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/java/app/build.gradle b/android/java/app/build.gradle index 668ff7993..498844399 100644 --- a/android/java/app/build.gradle +++ b/android/java/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 21 - buildToolsVersion "21.1.0" + buildToolsVersion "21.1.1" defaultConfig { applicationId "com.mapbox.mapboxgl.app" diff --git a/android/java/lib/build.gradle b/android/java/lib/build.gradle index 3784d2a9f..5fe9f7398 100644 --- a/android/java/lib/build.gradle +++ b/android/java/lib/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion 21 - buildToolsVersion "21.1.0" + buildToolsVersion "21.1.1" defaultConfig { applicationId "com.mapbox.mapboxgl.lib" From 6f78ec8a70b2a18abae0c0426f72c32cccd141f9 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 19:37:41 +1100 Subject: [PATCH 137/415] Give Travis latest build tools --- README.md | 2 +- android/java/app/build.gradle | 2 +- android/java/lib/build.gradle | 2 +- scripts/travis_before_install.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6327fcc90..9e304447a 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ Install Android SDK: sudo apt-get install lib32stdc++6 lib32z1 wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz tar -xzf ./android-sdk_r23.0.2-linux.tgz - ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository + ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.1,android-21,extra-android-m2repository,extra-google-m2repository export ANDROID_HOME="/dir/to/android-sdk-linux" Run: diff --git a/android/java/app/build.gradle b/android/java/app/build.gradle index 498844399..c0acf868f 100644 --- a/android/java/app/build.gradle +++ b/android/java/app/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { applicationId "com.mapbox.mapboxgl.app" - minSdkVersion 19 + minSdkVersion 18 targetSdkVersion 21 } diff --git a/android/java/lib/build.gradle b/android/java/lib/build.gradle index 5fe9f7398..9f650bb2a 100644 --- a/android/java/lib/build.gradle +++ b/android/java/lib/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { applicationId "com.mapbox.mapboxgl.lib" - minSdkVersion 19 + minSdkVersion 18 targetSdkVersion 21 ndk { diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index 8370eefc8..fd7d175a7 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -49,7 +49,7 @@ if [[ "${MASON_PLATFORM}" == "android" ]]; then echo "debug: installing SDK" #mapbox_time "installing SDK" \ sudo apt-get -y install lib32stdc++6 lib32z1 - echo y | ./android-sdk/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository + echo y | ./android-sdk/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.1,android-21,extra-android-m2repository,extra-google-m2repository elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then From 2404f6ca0e4f0c53ed8746ef73e80e46f345b931 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 19:49:13 +1100 Subject: [PATCH 138/415] Android api 17 compat --- android/java/app/build.gradle | 2 +- android/java/lib/build.gradle | 2 +- .../lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/android/java/app/build.gradle b/android/java/app/build.gradle index c0acf868f..7ddd75698 100644 --- a/android/java/app/build.gradle +++ b/android/java/app/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { applicationId "com.mapbox.mapboxgl.app" - minSdkVersion 18 + minSdkVersion 17 targetSdkVersion 21 } diff --git a/android/java/lib/build.gradle b/android/java/lib/build.gradle index 9f650bb2a..9a191aba7 100644 --- a/android/java/lib/build.gradle +++ b/android/java/lib/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { applicationId "com.mapbox.mapboxgl.lib" - minSdkVersion 18 + minSdkVersion 17 targetSdkVersion 21 ndk { diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index f664f2cf0..4ae6d991a 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -1116,7 +1116,8 @@ public class MapView extends SurfaceView { // Mouse events // TODO: SOURCE_TOUCH_NAVIGATION? // TODO: source device resolution? - if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { + //if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { // this is not available before API 18 + if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == InputDevice.SOURCE_CLASS_POINTER) { // Choose the action switch (event.getActionMasked()) { // Mouse scrolls From 16c8b3198ae0438ac2caa214842a8e0765f315ed Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 19:50:33 +1100 Subject: [PATCH 139/415] Ignore *~ --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index abed3bbf5..589b16675 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ /src/shader/shaders_gl.cpp /src/shader/shaders_gles2.cpp /bin/style.bin.js +*~ From bd9d7fe776b09ab366da40f1300be3eaa25adc57 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 19:55:26 +1100 Subject: [PATCH 140/415] Forgot osx --- scripts/travis_before_install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis_before_install.sh b/scripts/travis_before_install.sh index fd7d175a7..6fc55bbb9 100755 --- a/scripts/travis_before_install.sh +++ b/scripts/travis_before_install.sh @@ -91,7 +91,7 @@ elif [[ ${TRAVIS_OS_NAME} == "osx" ]]; then echo "debug: installing SDK" #mapbox_time "installing SDK" \ - echo y | ./android-sdk/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.0,android-19,android-21,extra-android-m2repository,extra-google-m2repository + echo y | ./android-sdk/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.1,android-21,extra-android-m2repository,extra-google-m2repository fi From 940b796bb44b3a87d0a0a8d09f94e20e32cf0889 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 20:08:18 +1100 Subject: [PATCH 141/415] Revert "Add noncopyable" This reverts commit 11061ee5da02650e36abbc4f558446db793d3df2. --- include/mbgl/platform/default/glfw_view.hpp | 3 +-- include/mbgl/platform/default/headless_display.hpp | 3 +-- include/mbgl/platform/default/headless_view.hpp | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/include/mbgl/platform/default/glfw_view.hpp b/include/mbgl/platform/default/glfw_view.hpp index c63b205a4..6e91c1125 100644 --- a/include/mbgl/platform/default/glfw_view.hpp +++ b/include/mbgl/platform/default/glfw_view.hpp @@ -3,14 +3,13 @@ #include #include -#include #ifdef NVIDIA #define GLFW_INCLUDE_ES2 #endif #include -class GLFWView : public mbgl::View, private mbgl::util::noncopyable { +class GLFWView : public mbgl::View { public: GLFWView(bool fullscreen = false); ~GLFWView(); diff --git a/include/mbgl/platform/default/headless_display.hpp b/include/mbgl/platform/default/headless_display.hpp index cd6cb4f75..5b33fd699 100644 --- a/include/mbgl/platform/default/headless_display.hpp +++ b/include/mbgl/platform/default/headless_display.hpp @@ -1,12 +1,11 @@ #ifndef MBGL_COMMON_HEADLESS_DISPLAY #define MBGL_COMMON_HEADLESS_DISPLAY -#include #include "headless_view.hpp" namespace mbgl { -class HeadlessDisplay, private mbgl::util::noncopyable { +class HeadlessDisplay { public: HeadlessDisplay(); ~HeadlessDisplay(); diff --git a/include/mbgl/platform/default/headless_view.hpp b/include/mbgl/platform/default/headless_view.hpp index 9ecdf695e..9d8acf70f 100644 --- a/include/mbgl/platform/default/headless_view.hpp +++ b/include/mbgl/platform/default/headless_view.hpp @@ -9,7 +9,6 @@ #define MBGL_USE_GLX 1 #endif -#include #include #include @@ -19,7 +18,7 @@ namespace mbgl { class HeadlessDisplay; -class HeadlessView : public View, private util::noncopyable { +class HeadlessView : public View { public: HeadlessView(); HeadlessView(std::shared_ptr display); From 314eba0002467384eac1e181b671e675704fdeed Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 20:08:34 +1100 Subject: [PATCH 142/415] api 16 compat --- android/java/app/build.gradle | 2 +- android/java/lib/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/java/app/build.gradle b/android/java/app/build.gradle index 7ddd75698..11cb3c14d 100644 --- a/android/java/app/build.gradle +++ b/android/java/app/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { applicationId "com.mapbox.mapboxgl.app" - minSdkVersion 17 + minSdkVersion 15 targetSdkVersion 21 } diff --git a/android/java/lib/build.gradle b/android/java/lib/build.gradle index 9a191aba7..5378d8c73 100644 --- a/android/java/lib/build.gradle +++ b/android/java/lib/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { applicationId "com.mapbox.mapboxgl.lib" - minSdkVersion 17 + minSdkVersion 15 targetSdkVersion 21 ndk { From f3193b399ed42a12b7a870c50f2f2d95fa7160b8 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sat, 15 Nov 2014 20:14:59 +1100 Subject: [PATCH 143/415] API 14 compat --- android/java/app/build.gradle | 2 +- android/java/lib/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/java/app/build.gradle b/android/java/app/build.gradle index 11cb3c14d..40d2fc710 100644 --- a/android/java/app/build.gradle +++ b/android/java/app/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { applicationId "com.mapbox.mapboxgl.app" - minSdkVersion 15 + minSdkVersion 14 targetSdkVersion 21 } diff --git a/android/java/lib/build.gradle b/android/java/lib/build.gradle index 5378d8c73..6ba0bb574 100644 --- a/android/java/lib/build.gradle +++ b/android/java/lib/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { applicationId "com.mapbox.mapboxgl.lib" - minSdkVersion 15 + minSdkVersion 14 targetSdkVersion 21 ndk { From a1fdc8dcf90ce97746453b9ee9c5b01ebb68fc8c Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 16 Nov 2014 16:37:23 +1100 Subject: [PATCH 144/415] Delete assets before extraction --- .../main/java/com/arieslabs/assetbridge/Assetbridge.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java index f12468dce..e32475931 100644 --- a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java +++ b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java @@ -37,6 +37,7 @@ public class Assetbridge { // iterate on the files... for(String asset : assets) { + delete(datadir + "/" + asset); copyAssetFolder(am, asset, datadir + "/" + asset); } @@ -50,6 +51,13 @@ public class Assetbridge { } + public static void delete(String dest) { + File destfh = new File(dest); + if (destfh.exists()) { + destfh.delete(); + } + } + public static void copyAssetFolder(AssetManager am, String src, String dest) throws IOException { From 583f8d6b61753a2efb6818d1a70049cbd579c408 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 16 Nov 2014 18:29:22 +1100 Subject: [PATCH 145/415] Add missing gesture detection library licence --- android/LICENSE_GESTURES.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 android/LICENSE_GESTURES.md diff --git a/android/LICENSE_GESTURES.md b/android/LICENSE_GESTURES.md new file mode 100644 index 000000000..d2d488c6e --- /dev/null +++ b/android/LICENSE_GESTURES.md @@ -0,0 +1,13 @@ +Android Gesture Detectors Framework Licence +=================================== + +Copyright (c) 2012, Almer Thie + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 97dc28883e31ab319a06d8276ee68e964de165be Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 16 Nov 2014 19:32:51 +1100 Subject: [PATCH 146/415] Add coffeecatch --- android/LICENSE_COFFEECATCH | 23 + android/cpp/coffeecatch.c | 1215 ++++++++++++++++++++++++++ android/cpp/coffeecatch.c.orig | 1338 +++++++++++++++++++++++++++++ android/cpp/coffeecatch_r10c.diff | 216 +++++ android/cpp/coffeejni.c | 185 ++++ android/cpp/jni.cpp | 155 ++-- android/cpp/native_map_view.cpp | 12 +- android/mapboxgl-app.gyp | 2 + include/coffeecatch/coffeecatch.h | 226 +++++ include/coffeecatch/coffeejni.h | 133 +++ 10 files changed, 3437 insertions(+), 68 deletions(-) create mode 100644 android/LICENSE_COFFEECATCH create mode 100644 android/cpp/coffeecatch.c create mode 100644 android/cpp/coffeecatch.c.orig create mode 100644 android/cpp/coffeecatch_r10c.diff create mode 100644 android/cpp/coffeejni.c create mode 100644 include/coffeecatch/coffeecatch.h create mode 100644 include/coffeecatch/coffeejni.h diff --git a/android/LICENSE_COFFEECATCH b/android/LICENSE_COFFEECATCH new file mode 100644 index 000000000..1a44ae19c --- /dev/null +++ b/android/LICENSE_COFFEECATCH @@ -0,0 +1,23 @@ +Copyright (c) 2013, Xavier Roche (http://www.httrack.com/) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/android/cpp/coffeecatch.c b/android/cpp/coffeecatch.c new file mode 100644 index 000000000..c5428aedc --- /dev/null +++ b/android/cpp/coffeecatch.c @@ -0,0 +1,1215 @@ +/* CoffeeCatch, a tiny native signal handler/catcher for JNI code. + * (especially for Android/Dalvik) + * + * Copyright (c) 2013, Xavier Roche (http://www.httrack.com/) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __ANDROID__ +#define USE_UNWIND +#define USE_CORKSCREW +#endif + +/* #undef NO_USE_SIGALTSTACK */ +/* #undef USE_SILENT_SIGALTSTACK */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (defined(USE_UNWIND) && !defined(USE_CORKSCREW)) +#include +#endif +#include +#include +#include + +/*#define NDK_DEBUG 1*/ +#if ( defined(NDK_DEBUG) && ( NDK_DEBUG == 1 ) ) +#define DEBUG(A) do { A; } while(0) +#define FD_ERRNO 2 +static void print(const char *const s) { + size_t count; + for(count = 0; s[count] != '\0'; count++) ; + /* write() is async-signal-safe. */ + (void) write(FD_ERRNO, s, count); +} +#else +#define DEBUG(A) +#endif + +/* Alternative stack size. */ +#define SIG_STACK_BUFFER_SIZE SIGSTKSZ + +#ifdef USE_UNWIND +/* Number of backtraces to get. */ +#define BACKTRACE_FRAMES_MAX 32 +#endif + +/* Signals to be caught. */ +#define SIG_CATCH_COUNT 7 +static const int native_sig_catch[SIG_CATCH_COUNT + 1] + = { SIGABRT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV +#ifdef SIGSTKFLT + , SIGSTKFLT +#endif + , 0 }; + +/* Maximum value of a caught signal. */ +#define SIG_NUMBER_MAX 32 + +#ifdef USE_CORKSCREW +typedef struct map_info_t map_info_t; +/* Extracted from Android's include/corkscrew/backtrace.h */ +typedef struct { + uintptr_t absolute_pc; + uintptr_t stack_top; + size_t stack_size; +} backtrace_frame_t; +typedef struct { + uintptr_t relative_pc; + uintptr_t relative_symbol_addr; + char* map_name; + char* symbol_name; + char* demangled_name; +} backtrace_symbol_t; +/* Extracted from Android's libcorkscrew/arch-arm/backtrace-arm.c */ +typedef ssize_t (*t_unwind_backtrace_signal_arch) +(siginfo_t* si, void* sc, const map_info_t* lst, backtrace_frame_t* bt, +size_t ignore_depth, size_t max_depth); +typedef map_info_t* (*t_acquire_my_map_info_list)(); +typedef void (*t_release_my_map_info_list)(map_info_t* milist); +typedef void (*t_get_backtrace_symbols)(const backtrace_frame_t* backtrace, + size_t frames, + backtrace_symbol_t* symbols); +typedef void (*t_free_backtrace_symbols)(backtrace_symbol_t* symbols, + size_t frames); +#endif + +/* Process-wide crash handler structure. */ +typedef struct native_code_global_struct { + /* Initialized. */ + int initialized; + + /* Lock. */ + pthread_mutex_t mutex; + + /* Backup of sigaction. */ + struct sigaction *sa_old; +} native_code_global_struct; +#define NATIVE_CODE_GLOBAL_INITIALIZER { 0, PTHREAD_MUTEX_INITIALIZER, NULL } + +/* Thread-specific crash handler structure. */ +typedef struct native_code_handler_struct { + /* Restore point context. */ + sigjmp_buf ctx; + int ctx_is_set; + + /* Alternate stack. */ + char *stack_buffer; + size_t stack_buffer_size; + stack_t stack_old; + + /* Signal code and info. */ + int code; + siginfo_t si; + ucontext_t uc; + + /* Uwind context. */ +#if (defined(USE_CORKSCREW)) + backtrace_frame_t frames[BACKTRACE_FRAMES_MAX]; +#elif (defined(USE_UNWIND)) + uintptr_t frames[BACKTRACE_FRAMES_MAX]; +#endif + size_t frames_size; + size_t frames_skip; + + /* Custom assertion failures. */ + const char *expression; + const char *file; + int line; + + /* Alarm was fired. */ + int alarm; +} native_code_handler_struct; + +/* Global crash handler structure. */ +static native_code_global_struct native_code_g = + NATIVE_CODE_GLOBAL_INITIALIZER; + +/* Thread variable holding context. */ +pthread_key_t native_code_thread; + +#if (defined(USE_UNWIND) && !defined(USE_CORKSCREW)) +/* Unwind callback */ +static _Unwind_Reason_Code +coffeecatch_unwind_callback(struct _Unwind_Context* context, void* arg) { + native_code_handler_struct *const s = (native_code_handler_struct*) arg; + + const uintptr_t ip = _Unwind_GetIP(context); + + DEBUG(print("called unwind callback\n")); + + if (ip != 0x0) { + if (s->frames_skip == 0) { + s->frames[s->frames_size] = ip; + s->frames_size++; + } else { + s->frames_skip--; + } + } + + if (s->frames_size == BACKTRACE_FRAMES_MAX) { + return _URC_END_OF_STACK; + } else { + DEBUG(print("returned _URC_OK\n")); + return _URC_OK; + } +} +#endif + +/* Use libcorkscrew to get a backtrace inside a signal handler. + Will only return a non-zero code on Android >= 4 (with libcorkscrew.so + being shipped) */ +#ifdef USE_CORKSCREW +static ssize_t coffeecatch_backtrace_signal(siginfo_t* si, void* sc, + backtrace_frame_t* frames, + size_t ignore_depth, + size_t max_depth) { + void *const libcorkscrew = dlopen("libcorkscrew.so", RTLD_LAZY | RTLD_LOCAL); + if (libcorkscrew != NULL) { + t_unwind_backtrace_signal_arch unwind_backtrace_signal_arch + = (t_unwind_backtrace_signal_arch) + dlsym(libcorkscrew, "unwind_backtrace_signal_arch"); + t_acquire_my_map_info_list acquire_my_map_info_list + = (t_acquire_my_map_info_list) + dlsym(libcorkscrew, "acquire_my_map_info_list"); + t_release_my_map_info_list release_my_map_info_list + = (t_release_my_map_info_list) + dlsym(libcorkscrew, "release_my_map_info_list"); + if (unwind_backtrace_signal_arch != NULL + && acquire_my_map_info_list != NULL + && release_my_map_info_list != NULL) { + map_info_t*const info = acquire_my_map_info_list(); + const ssize_t size = + unwind_backtrace_signal_arch(si, sc, info, frames, ignore_depth, + max_depth); + release_my_map_info_list(info); + return size; + } else { + DEBUG(print("symbols not founs in libcorkscrew.so\n")); + } + dlclose(libcorkscrew); + } else { + DEBUG(print("libcorkscrew.so could not be loaded\n")); + } + return -1; +} + +static void coffeecatch_backtrace_symbols(const backtrace_frame_t* backtrace, + size_t frames, + void (*fun)(void *arg, + const backtrace_symbol_t *sym), + void *arg) { + void *const libcorkscrew = dlopen("libcorkscrew.so", RTLD_LAZY | RTLD_LOCAL); + if (libcorkscrew != NULL) { + t_get_backtrace_symbols get_backtrace_symbols + = (t_get_backtrace_symbols) + dlsym(libcorkscrew, "get_backtrace_symbols"); + t_free_backtrace_symbols free_backtrace_symbols + = (t_free_backtrace_symbols) + dlsym(libcorkscrew, "free_backtrace_symbols"); + if (get_backtrace_symbols != NULL + && free_backtrace_symbols != NULL) { + backtrace_symbol_t symbols[BACKTRACE_FRAMES_MAX]; + size_t i; + if (frames > BACKTRACE_FRAMES_MAX) { + frames = BACKTRACE_FRAMES_MAX; + } + get_backtrace_symbols(backtrace, frames, symbols); + for(i = 0; i < frames; i++) { + fun(arg, &symbols[i]); + } + free_backtrace_symbols(symbols, frames); + } + dlclose(libcorkscrew); + } +} +#endif + +/* Call the old handler. */ +static void coffeecatch_call_old_signal_handler(const int code, siginfo_t *const si, + void * const sc) { + /* Call the "real" Java handler for JIT and internals. */ + if (code >= 0 && code < SIG_NUMBER_MAX) { + if (native_code_g.sa_old[code].sa_sigaction != NULL) { + native_code_g.sa_old[code].sa_sigaction(code, si, sc); + } else if (native_code_g.sa_old[code].sa_handler != NULL) { + native_code_g.sa_old[code].sa_handler(code); + } + } +} + +/* Unflag "on stack" */ +static void coffeecatch_revert_alternate_stack(void) { +#ifndef NO_USE_SIGALTSTACK + stack_t ss; + if (sigaltstack(NULL, &ss) == 0) { + ss.ss_flags &= ~SS_ONSTACK; + sigaltstack (&ss, NULL); + } +#endif +} + +/* Try to jump to userland. */ +static void coffeecatch_try_jump_userland(native_code_handler_struct* + const t, + const int code, + siginfo_t *const si, + void * const sc) { + (void) si; /* UNUSED */ + (void) sc; /* UNUSED */ + + /* Valid context ? */ + if (t != NULL && t->ctx_is_set) { + DEBUG(print("calling siglongjmp()\n")); + + /* Invalidate the context */ + t->ctx_is_set = 0; + + /* We need to revert the alternate stack before jumping. */ + coffeecatch_revert_alternate_stack(); + + /* + * Note on async-signal-safety of siglongjmp() [POSIX] : + * "Note that longjmp() and siglongjmp() are not in the list of + * async-signal-safe functions. This is because the code executing after + * longjmp() and siglongjmp() can call any unsafe functions with the same + * danger as calling those unsafe functions directly from the signal + * handler. Applications that use longjmp() and siglongjmp() from within + * signal handlers require rigorous protection in order to be portable. + * Many of the other functions that are excluded from the list are + * traditionally implemented using either malloc() or free() functions or + * the standard I/O library, both of which traditionally use data + * structures in a non-async-signal-safe manner. Since any combination of + * different functions using a common data structure can cause + * async-signal-safety problems, this volume of POSIX.1-2008 does not + * define the behavior when any unsafe function is called in a signal + * handler that interrupts an unsafe function." + */ + siglongjmp(t->ctx, code); + } +} + +static void coffeecatch_start_alarm(void) { + /* Ensure we do not deadlock. Default of ALRM is to die. + * (signal() and alarm() are signal-safe) */ + (void) alarm(30); +} + +static void coffeecatch_mark_alarm(native_code_handler_struct *const t) { + t->alarm = 1; +} + +/* Copy context infos (signal code, etc.) */ +static void coffeecatch_copy_context(native_code_handler_struct *const t, + const int code, siginfo_t *const si, + void *const sc) { + t->code = code; + t->si = *si; + if (sc != NULL) { + ucontext_t *const uc = (ucontext_t*) sc; + t->uc = *uc; + } else { + memset(&t->uc, 0, sizeof(t->uc)); + } + +#ifdef USE_UNWIND + /* Frame buffer initial position. */ + t->frames_size = 0; + + /* Skip us and the caller. */ + t->frames_skip = 2; + + /* Use the corkscrew library to extract the backtrace. */ +#ifdef USE_CORKSCREW + t->frames_size = coffeecatch_backtrace_signal(si, sc, t->frames, 0, + BACKTRACE_FRAMES_MAX); +#else + /* Unwind frames (equivalent to backtrace()) */ + _Unwind_Backtrace(coffeecatch_unwind_callback, t); +#endif + + if (t->frames_size != 0) { + DEBUG(print("called _Unwind_Backtrace()\n")); + } else { + DEBUG(print("called _Unwind_Backtrace(), but no traces\n")); + } +#endif +} + +/* Return the thread-specific native_code_handler_struct structure, or + * @c null if no such structure is available. */ +static native_code_handler_struct* coffeecatch_get() { + return (native_code_handler_struct*) + pthread_getspecific(native_code_thread); +} + +int coffeecatch_cancel_pending_alarm() { + native_code_handler_struct *const t = coffeecatch_get(); + if (t != NULL && t->alarm) { + t->alarm = 0; + /* "If seconds is 0, a pending alarm request, if any, is canceled." */ + alarm(0); + return 0; + } + return -1; +} + +/* Internal signal pass-through. Allows to peek the "real" crash before + * calling the Java handler. Remember than Java needs many of the signals + * (for the JIT, for test-free NullPointerException handling, etc.) + * We record the siginfo_t context in this function each time it is being + * called, to be able to know what error caused an issue. + */ +static void coffeecatch_signal_pass(const int code, siginfo_t *const si, + void *const sc) { + native_code_handler_struct *t; + + DEBUG(print("caught signal\n")); + + /* Call the "real" Java handler for JIT and internals. */ + coffeecatch_call_old_signal_handler(code, si, sc); + + /* Still here ? + * FIXME TODO: This is the Dalvik behavior - but is it the SunJVM one ? */ + + /* Ensure we do not deadlock. Default of ALRM is to die. + * (signal() and alarm() are signal-safe) */ + signal(code, SIG_DFL); + coffeecatch_start_alarm(); + + /* Available context ? */ + t = coffeecatch_get(); + if (t != NULL) { + /* An alarm() call was triggered. */ + coffeecatch_mark_alarm(t); + + /* Take note of the signal. */ + coffeecatch_copy_context(t, code, si, sc); + + /* Back to the future. */ + coffeecatch_try_jump_userland(t, code, si, sc); + } + + /* Nope. (abort() is signal-safe) */ + DEBUG(print("calling abort()\n")); + signal(SIGABRT, SIG_DFL); + abort(); +} + +/* Internal crash handler for abort(). Java calls abort() if its signal handler + * could not resolve the signal ; thus calling us through this handler. */ +static void coffeecatch_signal_abort(const int code, siginfo_t *const si, + void *const sc) { + native_code_handler_struct *t; + + (void) sc; /* UNUSED */ + + DEBUG(print("caught abort\n")); + + /* Ensure we do not deadlock. Default of ALRM is to die. + * (signal() and alarm() are signal-safe) */ + signal(code, SIG_DFL); + coffeecatch_start_alarm(); + + /* Available context ? */ + t = coffeecatch_get(); + if (t != NULL) { + /* An alarm() call was triggered. */ + coffeecatch_mark_alarm(t); + + /* Take note (real "abort()") */ + coffeecatch_copy_context(t, code, si, sc); + + /* Back to the future. */ + coffeecatch_try_jump_userland(t, code, si, sc); + } + + /* No such restore point, call old signal handler then. */ + DEBUG(print("calling old signal handler\n")); + coffeecatch_call_old_signal_handler(code, si, sc); + + /* Nope. (abort() is signal-safe) */ + DEBUG(print("calling abort()\n")); + abort(); +} + +/* Internal globals initialization. */ +static int coffeecatch_handler_setup_global(void) { + if (native_code_g.initialized++ == 0) { + size_t i; + struct sigaction sa_abort; + struct sigaction sa_pass; + + DEBUG(print("installing global signal handlers\n")); + + /* Setup handler structure. */ + memset(&sa_abort, 0, sizeof(sa_abort)); + sigemptyset(&sa_abort.sa_mask); + sa_abort.sa_sigaction = coffeecatch_signal_abort; + sa_abort.sa_flags = SA_SIGINFO | SA_ONSTACK; + + memset(&sa_pass, 0, sizeof(sa_pass)); + sigemptyset(&sa_pass.sa_mask); + sa_pass.sa_sigaction = coffeecatch_signal_pass; + sa_pass.sa_flags = SA_SIGINFO | SA_ONSTACK; + + /* Allocate */ + native_code_g.sa_old = calloc(sizeof(struct sigaction), SIG_NUMBER_MAX); + if (native_code_g.sa_old == NULL) { + return -1; + } + + /* Setup signal handlers for SIGABRT (Java calls abort()) and others. **/ + for (i = 0; native_sig_catch[i] != 0; i++) { + const int sig = native_sig_catch[i]; + const struct sigaction * const action = + sig == SIGABRT ? &sa_abort : &sa_pass; + assert(sig < SIG_NUMBER_MAX); + if (sigaction(sig, action, &native_code_g.sa_old[sig]) != 0) { + return -1; + } + } + + /* Initialize thread var. */ + if (pthread_key_create(&native_code_thread, NULL) != 0) { + return -1; + } + + DEBUG(print("installed global signal handlers\n")); + } + + /* OK. */ + return 0; +} + +/** + * Free a native_code_handler_struct structure. + **/ +static int coffeecatch_native_code_handler_struct_free(native_code_handler_struct *const t) { + int code = 0; + + if (t == NULL) { + return -1; + } + +#ifndef NO_USE_SIGALTSTACK + /* Restore previous alternative stack. */ + if (t->stack_old.ss_sp != NULL && sigaltstack(&t->stack_old, NULL) != 0) { +#ifndef USE_SILENT_SIGALTSTACK + code = -1; +#endif + } +#endif + + /* Free alternative stack */ + if (t->stack_buffer != NULL) { + free(t->stack_buffer); + t->stack_buffer = NULL; + t->stack_buffer_size = 0; + } + + /* Free structure. */ + free(t); + + return code; +} + +/** + * Create a native_code_handler_struct structure. + **/ +static native_code_handler_struct* coffeecatch_native_code_handler_struct_init(void) { + stack_t stack; + native_code_handler_struct *const t = + calloc(sizeof(native_code_handler_struct), 1); + + if (t == NULL) { + return NULL; + } + + DEBUG(print("installing thread alternative stack\n")); + + /* Initialize structure */ + t->stack_buffer_size = SIG_STACK_BUFFER_SIZE; + t->stack_buffer = malloc(t->stack_buffer_size); + if (t->stack_buffer == NULL) { + coffeecatch_native_code_handler_struct_free(t); + return NULL; + } + + /* Setup alternative stack. */ + memset(&stack, 0, sizeof(stack)); + stack.ss_sp = t->stack_buffer; + stack.ss_size = t->stack_buffer_size; + stack.ss_flags = 0; + +#ifndef NO_USE_SIGALTSTACK + /* Install alternative stack. This is thread-safe */ + if (sigaltstack(&stack, &t->stack_old) != 0) { +#ifndef USE_SILENT_SIGALTSTACK + coffeecatch_native_code_handler_struct_free(t); + return NULL; +#endif + } +#endif + + return t; +} + +/** + * Acquire the crash handler for the current thread. + * The coffeecatch_handler_cleanup() must be called to release allocated + * resources. + **/ +static int coffeecatch_handler_setup(int setup_thread) { + int code; + + DEBUG(print("setup for a new handler\n")); + + /* Initialize globals. */ + if (pthread_mutex_lock(&native_code_g.mutex) != 0) { + return -1; + } + code = coffeecatch_handler_setup_global(); + if (pthread_mutex_unlock(&native_code_g.mutex) != 0) { + return -1; + } + + /* Global initialization failed. */ + if (code != 0) { + return -1; + } + + /* Initialize locals. */ + if (setup_thread && coffeecatch_get() == NULL) { + native_code_handler_struct *const t = + coffeecatch_native_code_handler_struct_init(); + + if (t == NULL) { + return -1; + } + + DEBUG(print("installing thread alternative stack\n")); + + /* Set thread-specific value. */ + if (pthread_setspecific(native_code_thread, t) != 0) { + coffeecatch_native_code_handler_struct_free(t); + return -1; + } + + DEBUG(print("installed thread alternative stack\n")); + } + + /* OK. */ + return 0; +} + +/** + * Release the resources allocated by a previous call to + * coffeecatch_handler_setup(). + * This function must be called as many times as + * coffeecatch_handler_setup() was called to fully release allocated + * resources. + **/ +static int coffeecatch_handler_cleanup() { + /* Cleanup locals. */ + native_code_handler_struct *const t = coffeecatch_get(); + if (t != NULL) { + DEBUG(print("removing thread alternative stack\n")); + + /* Erase thread-specific value now (detach). */ + if (pthread_setspecific(native_code_thread, NULL) != 0) { + assert(! "pthread_setspecific() failed"); + } + + /* Free handler and reset slternate stack */ + if (coffeecatch_native_code_handler_struct_free(t) != 0) { + return -1; + } + + DEBUG(print("removed thread alternative stack\n")); + } + + /* Cleanup globals. */ + if (pthread_mutex_lock(&native_code_g.mutex) != 0) { + assert(! "pthread_mutex_lock() failed"); + } + assert(native_code_g.initialized != 0); + if (--native_code_g.initialized == 0) { + size_t i; + + DEBUG(print("removing global signal handlers\n")); + + /* Restore signal handler. */ + for(i = 0; native_sig_catch[i] != 0; i++) { + const int sig = native_sig_catch[i]; + assert(sig < SIG_NUMBER_MAX); + if (sigaction(sig, &native_code_g.sa_old[sig], NULL) != 0) { + return -1; + } + } + + /* Free old structure. */ + free(native_code_g.sa_old); + native_code_g.sa_old = NULL; + + /* Delete thread var. */ + if (pthread_key_delete(native_code_thread) != 0) { + assert(! "pthread_key_delete() failed"); + } + + DEBUG(print("removed global signal handlers\n")); + } + if (pthread_mutex_unlock(&native_code_g.mutex) != 0) { + assert(! "pthread_mutex_unlock() failed"); + } + + return 0; +} + +/** + * Get the signal associated with the crash. + */ +int coffeecatch_get_signal() { + const native_code_handler_struct* const t = coffeecatch_get(); + if (t != NULL) { + return t->code; + } else { + return -1; + } +} + +/* Signal descriptions. + See +*/ +static const char* coffeecatch_desc_sig(int sig, int code) { + switch(sig) { + case SIGILL: + switch(code) { + case ILL_ILLOPC: + return "Illegal opcode"; + case ILL_ILLOPN: + return "Illegal operand"; + case ILL_ILLADR: + return "Illegal addressing mode"; + case ILL_ILLTRP: + return "Illegal trap"; + case ILL_PRVOPC: + return "Privileged opcode"; + case ILL_PRVREG: + return "Privileged register"; + case ILL_COPROC: + return "Coprocessor error"; + case ILL_BADSTK: + return "Internal stack error"; + default: + return "Illegal operation"; + } + break; + case SIGFPE: + switch(code) { + case FPE_INTDIV: + return "Integer divide by zero"; + case FPE_INTOVF: + return "Integer overflow"; + case FPE_FLTDIV: + return "Floating-point divide by zero"; + case FPE_FLTOVF: + return "Floating-point overflow"; + case FPE_FLTUND: + return "Floating-point underflow"; + case FPE_FLTRES: + return "Floating-point inexact result"; + case FPE_FLTINV: + return "Invalid floating-point operation"; + case FPE_FLTSUB: + return "Subscript out of range"; + default: + return "Floating-point"; + } + break; + case SIGSEGV: + switch(code) { + case SEGV_MAPERR: + return "Address not mapped to object"; + case SEGV_ACCERR: + return "Invalid permissions for mapped object"; + default: + return "Segmentation violation"; + } + break; + case SIGBUS: + switch(code) { + case BUS_ADRALN: + return "Invalid address alignment"; + case BUS_ADRERR: + return "Nonexistent physical address"; + case BUS_OBJERR: + return "Object-specific hardware error"; + default: + return "Bus error"; + } + break; + case SIGTRAP: + switch(code) { + case TRAP_BRKPT: + return "Process breakpoint"; + case TRAP_TRACE: + return "Process trace trap"; + default: + return "Trap"; + } + break; + case SIGCHLD: + switch(code) { + case CLD_EXITED: + return "Child has exited"; + case CLD_KILLED: + return "Child has terminated abnormally and did not create a core file"; + case CLD_DUMPED: + return "Child has terminated abnormally and created a core file"; + case CLD_TRAPPED: + return "Traced child has trapped"; + case CLD_STOPPED: + return "Child has stopped"; + case CLD_CONTINUED: + return "Stopped child has continued"; + default: + return "Child"; + } + break; + case SIGPOLL: + switch(code) { + case POLL_IN: + return "Data input available"; + case POLL_OUT: + return "Output buffers available"; + case POLL_MSG: + return "Input message available"; + case POLL_ERR: + return "I/O error"; + case POLL_PRI: + return "High priority input available"; + case POLL_HUP: + return "Device disconnected"; + default: + return "Pool"; + } + break; + case SIGABRT: + return "Process abort signal"; + case SIGALRM: + return "Alarm clock"; + case SIGCONT: + return "Continue executing, if stopped"; + case SIGHUP: + return "Hangup"; + case SIGINT: + return "Terminal interrupt signal"; + case SIGKILL: + return "Kill"; + case SIGPIPE: + return "Write on a pipe with no one to read it"; + case SIGQUIT: + return "Terminal quit signal"; + case SIGSTOP: + return "Stop executing"; + case SIGTERM: + return "Termination signal"; + case SIGTSTP: + return "Terminal stop signal"; + case SIGTTIN: + return "Background process attempting read"; + case SIGTTOU: + return "Background process attempting write"; + case SIGUSR1: + return "User-defined signal 1"; + case SIGUSR2: + return "User-defined signal 2"; + case SIGPROF: + return "Profiling timer expired"; + case SIGSYS: + return "Bad system call"; + case SIGVTALRM: + return "Virtual timer expired"; + case SIGURG: + return "High bandwidth data is available at a socket"; + case SIGXCPU: + return "CPU time limit exceeded"; + case SIGXFSZ: + return "File size limit exceeded"; + default: + switch(code) { + case SI_USER: + return "Signal sent by kill()"; + case SI_QUEUE: + return "Signal sent by the sigqueue()"; + case SI_TIMER: + return "Signal generated by expiration of a timer set by timer_settime()"; + case SI_ASYNCIO: + return "Signal generated by completion of an asynchronous I/O request"; + case SI_MESGQ: + return + "Signal generated by arrival of a message on an empty message queue"; + default: + return "Unknown signal"; + } + break; + } +} + +/** + * Get the backtrace size. Returns 0 if no backtrace is available. + */ +size_t coffeecatch_get_backtrace_size(void) { +#ifdef USE_UNWIND + const native_code_handler_struct* const t = coffeecatch_get(); + if (t != NULL) { + return t->frames_size; + } else { + return 0; + } +#else + return 0; +#endif +} + +/** + * Get the th element of the backtrace, or 0 upon error. + */ +uintptr_t coffeecatch_get_backtrace(ssize_t index) { +#ifdef USE_UNWIND + const native_code_handler_struct* const t = coffeecatch_get(); + if (t != NULL) { + if (index < 0) { + index = t->frames_size + index; + } + if (index >= 0 && (size_t) index < t->frames_size) { +#ifdef USE_CORKSCREW + return t->frames[index].absolute_pc; +#else + return t->frames[index]; +#endif + } + } +#else + (void) index; +#endif + return 0; +} + +/** + * Get the program counter, given a pointer to a ucontext_t context. + **/ +static uintptr_t coffeecatch_get_pc_from_ucontext(const ucontext_t *uc) { +#if (defined(__arm__)) + return uc->uc_mcontext.arm_pc; +#elif (defined(__x86_64__)) + return uc->uc_mcontext.gregs[REG_RIP]; +#elif (defined(__i386)) + return uc->uc_mcontext.gregs[REG_EIP]; +#elif (defined (__ppc__)) || (defined (__powerpc__)) + return uc->uc_mcontext.regs->nip; +#elif (defined(__hppa__)) + return uc->uc_mcontext.sc_iaoq[0] & ~0x3UL; +#elif (defined(__sparc__) && defined (__arch64__)) + return uc->uc_mcontext.mc_gregs[MC_PC]; +#elif (defined(__sparc__) && !defined (__arch64__)) + return uc->uc_mcontext.gregs[REG_PC]; +#elif (defined(__mips__)) + return uc->uc_mcontext.gregs[31]; +#else +#error "Architecture is unknown, please report me!" +#endif +} + +/* Is this module name look like a DLL ? + FIXME: find a better way to do that... */ +static int coffeecatch_is_dll(const char *name) { + size_t i; + for(i = 0; name[i] != '\0'; i++) { + if (name[i + 0] == '.' && + name[i + 1] == 's' && + name[i + 2] == 'o' && + ( name[i + 3] == '\0' || name[i + 3] == '.') ) { + return 1; + } + } + return 0; +} + +/* Extract a line information on a PC address. */ +static void format_pc_address_cb(uintptr_t pc, + void (*fun)(void *arg, const char *module, + uintptr_t addr, + const char *function, + uintptr_t offset), void *arg) { + if (pc != 0) { + Dl_info info; + void * const addr = (void*) pc; + /* dladdr() returns 0 on error, and nonzero on success. */ + if (dladdr(addr, &info) != 0 && info.dli_fname != NULL) { + const uintptr_t near = (uintptr_t) info.dli_saddr; + const uintptr_t offs = pc - near; + const uintptr_t addr_rel = pc - (uintptr_t) info.dli_fbase; + /* We need the absolute address for the main module (?). + TODO FIXME to be investigated. */ + const uintptr_t addr_to_use = coffeecatch_is_dll(info.dli_fname) + ? addr_rel : pc; + fun(arg, info.dli_fname, addr_to_use, info.dli_sname, offs); + } else { + fun(arg, NULL, pc, NULL, 0); + } + } +} + +typedef struct t_print_fun { + char *buffer; + size_t buffer_size; +} t_print_fun; + +static void print_fun(void *arg, const char *module, uintptr_t uaddr, + const char *function, uintptr_t offset) { + t_print_fun *const t = (t_print_fun*) arg; + char *const buffer = t->buffer; + const size_t buffer_size = t->buffer_size; + const void*const addr = (void*) uaddr; + if (module == NULL) { + snprintf(buffer, buffer_size, "[at %p]", addr); + } else if (function != NULL) { + snprintf(buffer, buffer_size, "[at %s:%p (%s+0x%x)]", module, addr, + function, (int) offset); + } else { + snprintf(buffer, buffer_size, "[at %s:%p]", module, addr); + } +} + +/* Format a line information on a PC address. */ +static void format_pc_address(char *buffer, size_t buffer_size, uintptr_t pc) { + t_print_fun t; + t.buffer = buffer; + t.buffer_size = buffer_size; + format_pc_address_cb(pc, print_fun, &t); +} + +/** + * Get the full error message associated with the crash. + */ +const char* coffeecatch_get_message() { + const int error = errno; + const native_code_handler_struct* const t = coffeecatch_get(); + + /* Found valid handler. */ + if (t != NULL) { + char * const buffer = t->stack_buffer; + const size_t buffer_len = t->stack_buffer_size; + size_t buffer_offs = 0; + + const char* const posix_desc = + coffeecatch_desc_sig(t->si.si_signo, t->si.si_code); + + /* Assertion failure ? */ + if ((t->code == SIGABRT +#ifdef __ANDROID__ + /* See Android BUG #16672: + * "C assert() failure causes SIGSEGV when it should cause SIGABRT" */ + || (t->code == SIGSEGV && (uintptr_t) t->si.si_addr == 0xdeadbaad) +#endif + ) && t->expression != NULL) { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, + "assertion '%s' failed at %s:%d", + t->expression, t->file, t->line); + buffer_offs += strlen(&buffer[buffer_offs]); + } + /* Signal */ + else { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, "signal %d", + t->si.si_signo); + buffer_offs += strlen(&buffer[buffer_offs]); + + /* Description */ + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " (%s)", + posix_desc); + buffer_offs += strlen(&buffer[buffer_offs]); + + /* Address of faulting instruction */ + if (t->si.si_signo == SIGILL || t->si.si_signo == SIGSEGV) { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " at address %p", + t->si.si_addr); + buffer_offs += strlen(&buffer[buffer_offs]); + } + } + + /* [POSIX] If non-zero, an errno value associated with this signal, + as defined in . */ + if (t->si.si_errno != 0) { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, ": "); + buffer_offs += strlen(&buffer[buffer_offs]); + if (strerror_r(t->si.si_errno, &buffer[buffer_offs], + buffer_len - buffer_offs) == 0) { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, + "unknown error"); + buffer_offs += strlen(&buffer[buffer_offs]); + } + } + + /* Sending process ID. */ + if (t->si.si_signo == SIGCHLD && t->si.si_pid != 0) { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, + " (sent by pid %d)", (int) t->si.si_pid); + buffer_offs += strlen(&buffer[buffer_offs]); + } + + /* Faulting program counter location. */ + if (coffeecatch_get_pc_from_ucontext(&t->uc) != 0) { + const uintptr_t pc = coffeecatch_get_pc_from_ucontext(&t->uc); + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " "); + buffer_offs += strlen(&buffer[buffer_offs]); + format_pc_address(&buffer[buffer_offs], buffer_len - buffer_offs, pc); + buffer_offs += strlen(&buffer[buffer_offs]); + } + + /* Return string. */ + buffer[buffer_offs] = '\0'; + return t->stack_buffer; + } else { + /* Static buffer in case of emergency */ + static char buffer[256]; +#ifdef _GNU_SOURCE + return strerror_r(error, &buffer[0], sizeof(buffer)); +#else + const int code = strerror_r(error, &buffer[0], sizeof(buffer)); + errno = error; + if (code == 0) { + return buffer; + } else { + return "unknown error during crash handler setup"; + } +#endif + } +} + +#if (defined(USE_CORKSCREW)) +typedef struct t_coffeecatch_backtrace_symbols_fun { + void (*fun)(void *arg, const char *module, uintptr_t addr, + const char *function, uintptr_t offset); + void *arg; +} t_coffeecatch_backtrace_symbols_fun; + +static void coffeecatch_backtrace_symbols_fun(void *arg, const backtrace_symbol_t *sym) { + t_coffeecatch_backtrace_symbols_fun *const bt = + (t_coffeecatch_backtrace_symbols_fun*) arg; + const char *symbol = sym->demangled_name != NULL + ? sym->demangled_name : sym->symbol_name; + const uintptr_t rel = sym->relative_pc - sym->relative_symbol_addr; + bt->fun(bt->arg, sym->map_name, sym->relative_pc, symbol, rel); +} +#endif + +/** + * Enumerate backtrace information. + */ +void coffeecatch_get_backtrace_info(void (*fun)(void *arg, + const char *module, + uintptr_t addr, + const char *function, + uintptr_t offset), void *arg) { + const native_code_handler_struct* const t = coffeecatch_get(); + if (t != NULL) { +#if (defined(USE_CORKSCREW)) + t_coffeecatch_backtrace_symbols_fun bt; + bt.fun = fun; + bt.arg = arg; + coffeecatch_backtrace_symbols(t->frames, t->frames_size, + coffeecatch_backtrace_symbols_fun, &bt); +#elif (defined(USE_UNWIND)) + size_t i; + for(i = 0; i < t->frames_size; i++) { + const uintptr_t pc = t->frames[i]; + format_pc_address_cb(pc, fun, arg); + } +#else + (void) fun; + (void) arg; +#endif + } +} + +/** + * Calls coffeecatch_handler_setup(1) to setup a crash handler, mark the + * context as valid, and return 0 upon success. + */ +int coffeecatch_setup() { + if (coffeecatch_handler_setup(1) == 0) { + native_code_handler_struct *const t = coffeecatch_get(); + assert(t != NULL); + t->ctx_is_set = 1; + return 0; + } else { + return -1; + } +} + +/** + * Calls coffeecatch_handler_cleanup() + */ +void coffeecatch_cleanup() { + native_code_handler_struct *const t = coffeecatch_get(); + assert(t != NULL); + t->ctx_is_set = 0; + coffeecatch_handler_cleanup(); +} + +sigjmp_buf* coffeecatch_get_ctx() { + native_code_handler_struct* t = coffeecatch_get(); + assert(t != NULL); + return &t->ctx; +} + +void coffeecatch_abort(const char* exp, const char* file, int line) { + native_code_handler_struct *const t = coffeecatch_get(); + if (t != NULL) { + t->expression = exp; + t->file = file; + t->line = line; + } + abort(); +} diff --git a/android/cpp/coffeecatch.c.orig b/android/cpp/coffeecatch.c.orig new file mode 100644 index 000000000..1889dc0b9 --- /dev/null +++ b/android/cpp/coffeecatch.c.orig @@ -0,0 +1,1338 @@ +/* CoffeeCatch, a tiny native signal handler/catcher for JNI code. + * (especially for Android/Dalvik) + * + * Copyright (c) 2013, Xavier Roche (http://www.httrack.com/) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __ANDROID__ +#define USE_UNWIND +#define USE_CORKSCREW +#endif + +/* #undef NO_USE_SIGALTSTACK */ +/* #undef USE_SILENT_SIGALTSTACK */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__ANDROID__) && !defined(__BIONIC_HAVE_UCONTEXT_T) && \ + defined(__arm__) && !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT) +#include +#endif +#if (defined(USE_UNWIND) && !defined(USE_CORKSCREW)) +#include +#endif +#include +#include +#include + +/*#define NDK_DEBUG 1*/ +#if ( defined(NDK_DEBUG) && ( NDK_DEBUG == 1 ) ) +#define DEBUG(A) do { A; } while(0) +#define FD_ERRNO 2 +static void print(const char *const s) { + size_t count; + for(count = 0; s[count] != '\0'; count++) ; + /* write() is async-signal-safe. */ + (void) write(FD_ERRNO, s, count); +} +#else +#define DEBUG(A) +#endif + +/* Alternative stack size. */ +#define SIG_STACK_BUFFER_SIZE SIGSTKSZ + +#ifdef USE_UNWIND +/* Number of backtraces to get. */ +#define BACKTRACE_FRAMES_MAX 32 +#endif + +/* Signals to be caught. */ +#define SIG_CATCH_COUNT 7 +static const int native_sig_catch[SIG_CATCH_COUNT + 1] + = { SIGABRT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV +#ifdef SIGSTKFLT + , SIGSTKFLT +#endif + , 0 }; + +/* Maximum value of a caught signal. */ +#define SIG_NUMBER_MAX 32 + +#if (defined(__ANDROID__) && (!defined(__BIONIC_HAVE_UCONTEXT_T))) +#ifndef ucontext_h_seen +#define ucontext_h_seen + +/* stack_t definition */ +#include + +#if defined(__arm__) + +/* Taken from richard.quirk's header file. (Android does not have it) */ + +typedef struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + unsigned long uc_sigmask; +} ucontext_t; + +#elif defined(__i386__) + +/* Taken from Google Breakpad. */ + +/* 80-bit floating-point register */ +struct _libc_fpreg { + unsigned short significand[4]; + unsigned short exponent; +}; + +/* Simple floating-point state, see FNSTENV instruction */ +struct _libc_fpstate { + unsigned long cw; + unsigned long sw; + unsigned long tag; + unsigned long ipoff; + unsigned long cssel; + unsigned long dataoff; + unsigned long datasel; + struct _libc_fpreg _st[8]; + unsigned long status; +}; + +typedef uint32_t greg_t; + +typedef struct { + uint32_t gregs[19]; + struct _libc_fpstate* fpregs; + uint32_t oldmask; + uint32_t cr2; +} mcontext_t; + +enum { + REG_GS = 0, + REG_FS, + REG_ES, + REG_DS, + REG_EDI, + REG_ESI, + REG_EBP, + REG_ESP, + REG_EBX, + REG_EDX, + REG_ECX, + REG_EAX, + REG_TRAPNO, + REG_ERR, + REG_EIP, + REG_CS, + REG_EFL, + REG_UESP, + REG_SS, +}; + +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; +} ucontext_t; + +#elif defined(__mips__) + +/* Taken from Google Breakpad. */ + +typedef struct { + uint32_t regmask; + uint32_t status; + uint64_t pc; + uint64_t gregs[32]; + uint64_t fpregs[32]; + uint32_t acx; + uint32_t fpc_csr; + uint32_t fpc_eir; + uint32_t used_math; + uint32_t dsp; + uint64_t mdhi; + uint64_t mdlo; + uint32_t hi1; + uint32_t lo1; + uint32_t hi2; + uint32_t lo2; + uint32_t hi3; + uint32_t lo3; +} mcontext_t; + +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; +} ucontext_t; + +#else +#error "Architecture is not supported (unknown ucontext layout)" +#endif + +#endif + +#ifdef USE_CORKSCREW +typedef struct map_info_t map_info_t; +/* Extracted from Android's include/corkscrew/backtrace.h */ +typedef struct { + uintptr_t absolute_pc; + uintptr_t stack_top; + size_t stack_size; +} backtrace_frame_t; +typedef struct { + uintptr_t relative_pc; + uintptr_t relative_symbol_addr; + char* map_name; + char* symbol_name; + char* demangled_name; +} backtrace_symbol_t; +/* Extracted from Android's libcorkscrew/arch-arm/backtrace-arm.c */ +typedef ssize_t (*t_unwind_backtrace_signal_arch) +(siginfo_t* si, void* sc, const map_info_t* lst, backtrace_frame_t* bt, +size_t ignore_depth, size_t max_depth); +typedef map_info_t* (*t_acquire_my_map_info_list)(); +typedef void (*t_release_my_map_info_list)(map_info_t* milist); +typedef void (*t_get_backtrace_symbols)(const backtrace_frame_t* backtrace, + size_t frames, + backtrace_symbol_t* symbols); +typedef void (*t_free_backtrace_symbols)(backtrace_symbol_t* symbols, + size_t frames); +#endif + +#endif + +/* Process-wide crash handler structure. */ +typedef struct native_code_global_struct { + /* Initialized. */ + int initialized; + + /* Lock. */ + pthread_mutex_t mutex; + + /* Backup of sigaction. */ + struct sigaction *sa_old; +} native_code_global_struct; +#define NATIVE_CODE_GLOBAL_INITIALIZER { 0, PTHREAD_MUTEX_INITIALIZER, NULL } + +/* Thread-specific crash handler structure. */ +typedef struct native_code_handler_struct { + /* Restore point context. */ + sigjmp_buf ctx; + int ctx_is_set; + + /* Alternate stack. */ + char *stack_buffer; + size_t stack_buffer_size; + stack_t stack_old; + + /* Signal code and info. */ + int code; + siginfo_t si; + ucontext_t uc; + + /* Uwind context. */ +#if (defined(USE_CORKSCREW)) + backtrace_frame_t frames[BACKTRACE_FRAMES_MAX]; +#elif (defined(USE_UNWIND)) + uintptr_t frames[BACKTRACE_FRAMES_MAX]; +#endif + size_t frames_size; + size_t frames_skip; + + /* Custom assertion failures. */ + const char *expression; + const char *file; + int line; + + /* Alarm was fired. */ + int alarm; +} native_code_handler_struct; + +/* Global crash handler structure. */ +static native_code_global_struct native_code_g = + NATIVE_CODE_GLOBAL_INITIALIZER; + +/* Thread variable holding context. */ +pthread_key_t native_code_thread; + +#if (defined(USE_UNWIND) && !defined(USE_CORKSCREW)) +/* Unwind callback */ +static _Unwind_Reason_Code +coffeecatch_unwind_callback(struct _Unwind_Context* context, void* arg) { + native_code_handler_struct *const s = (native_code_handler_struct*) arg; + + const uintptr_t ip = _Unwind_GetIP(context); + + DEBUG(print("called unwind callback\n")); + + if (ip != 0x0) { + if (s->frames_skip == 0) { + s->frames[s->frames_size] = ip; + s->frames_size++; + } else { + s->frames_skip--; + } + } + + if (s->frames_size == BACKTRACE_FRAMES_MAX) { + return _URC_END_OF_STACK; + } else { + DEBUG(print("returned _URC_OK\n")); + return _URC_OK; + } +} +#endif + +/* Use libcorkscrew to get a backtrace inside a signal handler. + Will only return a non-zero code on Android >= 4 (with libcorkscrew.so + being shipped) */ +#ifdef USE_CORKSCREW +static ssize_t coffeecatch_backtrace_signal(siginfo_t* si, void* sc, + backtrace_frame_t* frames, + size_t ignore_depth, + size_t max_depth) { + void *const libcorkscrew = dlopen("libcorkscrew.so", RTLD_LAZY | RTLD_LOCAL); + if (libcorkscrew != NULL) { + t_unwind_backtrace_signal_arch unwind_backtrace_signal_arch + = (t_unwind_backtrace_signal_arch) + dlsym(libcorkscrew, "unwind_backtrace_signal_arch"); + t_acquire_my_map_info_list acquire_my_map_info_list + = (t_acquire_my_map_info_list) + dlsym(libcorkscrew, "acquire_my_map_info_list"); + t_release_my_map_info_list release_my_map_info_list + = (t_release_my_map_info_list) + dlsym(libcorkscrew, "release_my_map_info_list"); + if (unwind_backtrace_signal_arch != NULL + && acquire_my_map_info_list != NULL + && release_my_map_info_list != NULL) { + map_info_t*const info = acquire_my_map_info_list(); + const ssize_t size = + unwind_backtrace_signal_arch(si, sc, info, frames, ignore_depth, + max_depth); + release_my_map_info_list(info); + return size; + } else { + DEBUG(print("symbols not founs in libcorkscrew.so\n")); + } + dlclose(libcorkscrew); + } else { + DEBUG(print("libcorkscrew.so could not be loaded\n")); + } + return -1; +} + +static void coffeecatch_backtrace_symbols(const backtrace_frame_t* backtrace, + size_t frames, + void (*fun)(void *arg, + const backtrace_symbol_t *sym), + void *arg) { + void *const libcorkscrew = dlopen("libcorkscrew.so", RTLD_LAZY | RTLD_LOCAL); + if (libcorkscrew != NULL) { + t_get_backtrace_symbols get_backtrace_symbols + = (t_get_backtrace_symbols) + dlsym(libcorkscrew, "get_backtrace_symbols"); + t_free_backtrace_symbols free_backtrace_symbols + = (t_free_backtrace_symbols) + dlsym(libcorkscrew, "free_backtrace_symbols"); + if (get_backtrace_symbols != NULL + && free_backtrace_symbols != NULL) { + backtrace_symbol_t symbols[BACKTRACE_FRAMES_MAX]; + size_t i; + if (frames > BACKTRACE_FRAMES_MAX) { + frames = BACKTRACE_FRAMES_MAX; + } + get_backtrace_symbols(backtrace, frames, symbols); + for(i = 0; i < frames; i++) { + fun(arg, &symbols[i]); + } + free_backtrace_symbols(symbols, frames); + } + dlclose(libcorkscrew); + } +} +#endif + +/* Call the old handler. */ +static void coffeecatch_call_old_signal_handler(const int code, siginfo_t *const si, + void * const sc) { + /* Call the "real" Java handler for JIT and internals. */ + if (code >= 0 && code < SIG_NUMBER_MAX) { + if (native_code_g.sa_old[code].sa_sigaction != NULL) { + native_code_g.sa_old[code].sa_sigaction(code, si, sc); + } else if (native_code_g.sa_old[code].sa_handler != NULL) { + native_code_g.sa_old[code].sa_handler(code); + } + } +} + +/* Unflag "on stack" */ +static void coffeecatch_revert_alternate_stack(void) { +#ifndef NO_USE_SIGALTSTACK + stack_t ss; + if (sigaltstack(NULL, &ss) == 0) { + ss.ss_flags &= ~SS_ONSTACK; + sigaltstack (&ss, NULL); + } +#endif +} + +/* Try to jump to userland. */ +static void coffeecatch_try_jump_userland(native_code_handler_struct* + const t, + const int code, + siginfo_t *const si, + void * const sc) { + (void) si; /* UNUSED */ + (void) sc; /* UNUSED */ + + /* Valid context ? */ + if (t != NULL && t->ctx_is_set) { + DEBUG(print("calling siglongjmp()\n")); + + /* Invalidate the context */ + t->ctx_is_set = 0; + + /* We need to revert the alternate stack before jumping. */ + coffeecatch_revert_alternate_stack(); + + /* + * Note on async-signal-safety of siglongjmp() [POSIX] : + * "Note that longjmp() and siglongjmp() are not in the list of + * async-signal-safe functions. This is because the code executing after + * longjmp() and siglongjmp() can call any unsafe functions with the same + * danger as calling those unsafe functions directly from the signal + * handler. Applications that use longjmp() and siglongjmp() from within + * signal handlers require rigorous protection in order to be portable. + * Many of the other functions that are excluded from the list are + * traditionally implemented using either malloc() or free() functions or + * the standard I/O library, both of which traditionally use data + * structures in a non-async-signal-safe manner. Since any combination of + * different functions using a common data structure can cause + * async-signal-safety problems, this volume of POSIX.1-2008 does not + * define the behavior when any unsafe function is called in a signal + * handler that interrupts an unsafe function." + */ + siglongjmp(t->ctx, code); + } +} + +static void coffeecatch_start_alarm(void) { + /* Ensure we do not deadlock. Default of ALRM is to die. + * (signal() and alarm() are signal-safe) */ + (void) alarm(30); +} + +static void coffeecatch_mark_alarm(native_code_handler_struct *const t) { + t->alarm = 1; +} + +/* Copy context infos (signal code, etc.) */ +static void coffeecatch_copy_context(native_code_handler_struct *const t, + const int code, siginfo_t *const si, + void *const sc) { + t->code = code; + t->si = *si; + if (sc != NULL) { + ucontext_t *const uc = (ucontext_t*) sc; + t->uc = *uc; + } else { + memset(&t->uc, 0, sizeof(t->uc)); + } + +#ifdef USE_UNWIND + /* Frame buffer initial position. */ + t->frames_size = 0; + + /* Skip us and the caller. */ + t->frames_skip = 2; + + /* Use the corkscrew library to extract the backtrace. */ +#ifdef USE_CORKSCREW + t->frames_size = coffeecatch_backtrace_signal(si, sc, t->frames, 0, + BACKTRACE_FRAMES_MAX); +#else + /* Unwind frames (equivalent to backtrace()) */ + _Unwind_Backtrace(coffeecatch_unwind_callback, t); +#endif + + if (t->frames_size != 0) { + DEBUG(print("called _Unwind_Backtrace()\n")); + } else { + DEBUG(print("called _Unwind_Backtrace(), but no traces\n")); + } +#endif +} + +/* Return the thread-specific native_code_handler_struct structure, or + * @c null if no such structure is available. */ +static native_code_handler_struct* coffeecatch_get() { + return (native_code_handler_struct*) + pthread_getspecific(native_code_thread); +} + +int coffeecatch_cancel_pending_alarm() { + native_code_handler_struct *const t = coffeecatch_get(); + if (t != NULL && t->alarm) { + t->alarm = 0; + /* "If seconds is 0, a pending alarm request, if any, is canceled." */ + alarm(0); + return 0; + } + return -1; +} + +/* Internal signal pass-through. Allows to peek the "real" crash before + * calling the Java handler. Remember than Java needs many of the signals + * (for the JIT, for test-free NullPointerException handling, etc.) + * We record the siginfo_t context in this function each time it is being + * called, to be able to know what error caused an issue. + */ +static void coffeecatch_signal_pass(const int code, siginfo_t *const si, + void *const sc) { + native_code_handler_struct *t; + + DEBUG(print("caught signal\n")); + + /* Call the "real" Java handler for JIT and internals. */ + coffeecatch_call_old_signal_handler(code, si, sc); + + /* Still here ? + * FIXME TODO: This is the Dalvik behavior - but is it the SunJVM one ? */ + + /* Ensure we do not deadlock. Default of ALRM is to die. + * (signal() and alarm() are signal-safe) */ + signal(code, SIG_DFL); + coffeecatch_start_alarm(); + + /* Available context ? */ + t = coffeecatch_get(); + if (t != NULL) { + /* An alarm() call was triggered. */ + coffeecatch_mark_alarm(t); + + /* Take note of the signal. */ + coffeecatch_copy_context(t, code, si, sc); + + /* Back to the future. */ + coffeecatch_try_jump_userland(t, code, si, sc); + } + + /* Nope. (abort() is signal-safe) */ + DEBUG(print("calling abort()\n")); + signal(SIGABRT, SIG_DFL); + abort(); +} + +/* Internal crash handler for abort(). Java calls abort() if its signal handler + * could not resolve the signal ; thus calling us through this handler. */ +static void coffeecatch_signal_abort(const int code, siginfo_t *const si, + void *const sc) { + native_code_handler_struct *t; + + (void) sc; /* UNUSED */ + + DEBUG(print("caught abort\n")); + + /* Ensure we do not deadlock. Default of ALRM is to die. + * (signal() and alarm() are signal-safe) */ + signal(code, SIG_DFL); + coffeecatch_start_alarm(); + + /* Available context ? */ + t = coffeecatch_get(); + if (t != NULL) { + /* An alarm() call was triggered. */ + coffeecatch_mark_alarm(t); + + /* Take note (real "abort()") */ + coffeecatch_copy_context(t, code, si, sc); + + /* Back to the future. */ + coffeecatch_try_jump_userland(t, code, si, sc); + } + + /* No such restore point, call old signal handler then. */ + DEBUG(print("calling old signal handler\n")); + coffeecatch_call_old_signal_handler(code, si, sc); + + /* Nope. (abort() is signal-safe) */ + DEBUG(print("calling abort()\n")); + abort(); +} + +/* Internal globals initialization. */ +static int coffeecatch_handler_setup_global(void) { + if (native_code_g.initialized++ == 0) { + size_t i; + struct sigaction sa_abort; + struct sigaction sa_pass; + + DEBUG(print("installing global signal handlers\n")); + + /* Setup handler structure. */ + memset(&sa_abort, 0, sizeof(sa_abort)); + sigemptyset(&sa_abort.sa_mask); + sa_abort.sa_sigaction = coffeecatch_signal_abort; + sa_abort.sa_flags = SA_SIGINFO | SA_ONSTACK; + + memset(&sa_pass, 0, sizeof(sa_pass)); + sigemptyset(&sa_pass.sa_mask); + sa_pass.sa_sigaction = coffeecatch_signal_pass; + sa_pass.sa_flags = SA_SIGINFO | SA_ONSTACK; + + /* Allocate */ + native_code_g.sa_old = calloc(sizeof(struct sigaction), SIG_NUMBER_MAX); + if (native_code_g.sa_old == NULL) { + return -1; + } + + /* Setup signal handlers for SIGABRT (Java calls abort()) and others. **/ + for (i = 0; native_sig_catch[i] != 0; i++) { + const int sig = native_sig_catch[i]; + const struct sigaction * const action = + sig == SIGABRT ? &sa_abort : &sa_pass; + assert(sig < SIG_NUMBER_MAX); + if (sigaction(sig, action, &native_code_g.sa_old[sig]) != 0) { + return -1; + } + } + + /* Initialize thread var. */ + if (pthread_key_create(&native_code_thread, NULL) != 0) { + return -1; + } + + DEBUG(print("installed global signal handlers\n")); + } + + /* OK. */ + return 0; +} + +/** + * Free a native_code_handler_struct structure. + **/ +static int coffeecatch_native_code_handler_struct_free(native_code_handler_struct *const t) { + int code = 0; + + if (t == NULL) { + return -1; + } + +#ifndef NO_USE_SIGALTSTACK + /* Restore previous alternative stack. */ + if (t->stack_old.ss_sp != NULL && sigaltstack(&t->stack_old, NULL) != 0) { +#ifndef USE_SILENT_SIGALTSTACK + code = -1; +#endif + } +#endif + + /* Free alternative stack */ + if (t->stack_buffer != NULL) { + free(t->stack_buffer); + t->stack_buffer = NULL; + t->stack_buffer_size = 0; + } + + /* Free structure. */ + free(t); + + return code; +} + +/** + * Create a native_code_handler_struct structure. + **/ +static native_code_handler_struct* coffeecatch_native_code_handler_struct_init(void) { + stack_t stack; + native_code_handler_struct *const t = + calloc(sizeof(native_code_handler_struct), 1); + + if (t == NULL) { + return NULL; + } + + DEBUG(print("installing thread alternative stack\n")); + + /* Initialize structure */ + t->stack_buffer_size = SIG_STACK_BUFFER_SIZE; + t->stack_buffer = malloc(t->stack_buffer_size); + if (t->stack_buffer == NULL) { + coffeecatch_native_code_handler_struct_free(t); + return NULL; + } + + /* Setup alternative stack. */ + memset(&stack, 0, sizeof(stack)); + stack.ss_sp = t->stack_buffer; + stack.ss_size = t->stack_buffer_size; + stack.ss_flags = 0; + +#ifndef NO_USE_SIGALTSTACK + /* Install alternative stack. This is thread-safe */ + if (sigaltstack(&stack, &t->stack_old) != 0) { +#ifndef USE_SILENT_SIGALTSTACK + coffeecatch_native_code_handler_struct_free(t); + return NULL; +#endif + } +#endif + + return t; +} + +/** + * Acquire the crash handler for the current thread. + * The coffeecatch_handler_cleanup() must be called to release allocated + * resources. + **/ +static int coffeecatch_handler_setup(int setup_thread) { + int code; + + DEBUG(print("setup for a new handler\n")); + + /* Initialize globals. */ + if (pthread_mutex_lock(&native_code_g.mutex) != 0) { + return -1; + } + code = coffeecatch_handler_setup_global(); + if (pthread_mutex_unlock(&native_code_g.mutex) != 0) { + return -1; + } + + /* Global initialization failed. */ + if (code != 0) { + return -1; + } + + /* Initialize locals. */ + if (setup_thread && coffeecatch_get() == NULL) { + native_code_handler_struct *const t = + coffeecatch_native_code_handler_struct_init(); + + if (t == NULL) { + return -1; + } + + DEBUG(print("installing thread alternative stack\n")); + + /* Set thread-specific value. */ + if (pthread_setspecific(native_code_thread, t) != 0) { + coffeecatch_native_code_handler_struct_free(t); + return -1; + } + + DEBUG(print("installed thread alternative stack\n")); + } + + /* OK. */ + return 0; +} + +/** + * Release the resources allocated by a previous call to + * coffeecatch_handler_setup(). + * This function must be called as many times as + * coffeecatch_handler_setup() was called to fully release allocated + * resources. + **/ +static int coffeecatch_handler_cleanup() { + /* Cleanup locals. */ + native_code_handler_struct *const t = coffeecatch_get(); + if (t != NULL) { + DEBUG(print("removing thread alternative stack\n")); + + /* Erase thread-specific value now (detach). */ + if (pthread_setspecific(native_code_thread, NULL) != 0) { + assert(! "pthread_setspecific() failed"); + } + + /* Free handler and reset slternate stack */ + if (coffeecatch_native_code_handler_struct_free(t) != 0) { + return -1; + } + + DEBUG(print("removed thread alternative stack\n")); + } + + /* Cleanup globals. */ + if (pthread_mutex_lock(&native_code_g.mutex) != 0) { + assert(! "pthread_mutex_lock() failed"); + } + assert(native_code_g.initialized != 0); + if (--native_code_g.initialized == 0) { + size_t i; + + DEBUG(print("removing global signal handlers\n")); + + /* Restore signal handler. */ + for(i = 0; native_sig_catch[i] != 0; i++) { + const int sig = native_sig_catch[i]; + assert(sig < SIG_NUMBER_MAX); + if (sigaction(sig, &native_code_g.sa_old[sig], NULL) != 0) { + return -1; + } + } + + /* Free old structure. */ + free(native_code_g.sa_old); + native_code_g.sa_old = NULL; + + /* Delete thread var. */ + if (pthread_key_delete(native_code_thread) != 0) { + assert(! "pthread_key_delete() failed"); + } + + DEBUG(print("removed global signal handlers\n")); + } + if (pthread_mutex_unlock(&native_code_g.mutex) != 0) { + assert(! "pthread_mutex_unlock() failed"); + } + + return 0; +} + +/** + * Get the signal associated with the crash. + */ +int coffeecatch_get_signal() { + const native_code_handler_struct* const t = coffeecatch_get(); + if (t != NULL) { + return t->code; + } else { + return -1; + } +} + +/* Signal descriptions. + See +*/ +static const char* coffeecatch_desc_sig(int sig, int code) { + switch(sig) { + case SIGILL: + switch(code) { + case ILL_ILLOPC: + return "Illegal opcode"; + case ILL_ILLOPN: + return "Illegal operand"; + case ILL_ILLADR: + return "Illegal addressing mode"; + case ILL_ILLTRP: + return "Illegal trap"; + case ILL_PRVOPC: + return "Privileged opcode"; + case ILL_PRVREG: + return "Privileged register"; + case ILL_COPROC: + return "Coprocessor error"; + case ILL_BADSTK: + return "Internal stack error"; + default: + return "Illegal operation"; + } + break; + case SIGFPE: + switch(code) { + case FPE_INTDIV: + return "Integer divide by zero"; + case FPE_INTOVF: + return "Integer overflow"; + case FPE_FLTDIV: + return "Floating-point divide by zero"; + case FPE_FLTOVF: + return "Floating-point overflow"; + case FPE_FLTUND: + return "Floating-point underflow"; + case FPE_FLTRES: + return "Floating-point inexact result"; + case FPE_FLTINV: + return "Invalid floating-point operation"; + case FPE_FLTSUB: + return "Subscript out of range"; + default: + return "Floating-point"; + } + break; + case SIGSEGV: + switch(code) { + case SEGV_MAPERR: + return "Address not mapped to object"; + case SEGV_ACCERR: + return "Invalid permissions for mapped object"; + default: + return "Segmentation violation"; + } + break; + case SIGBUS: + switch(code) { + case BUS_ADRALN: + return "Invalid address alignment"; + case BUS_ADRERR: + return "Nonexistent physical address"; + case BUS_OBJERR: + return "Object-specific hardware error"; + default: + return "Bus error"; + } + break; + case SIGTRAP: + switch(code) { + case TRAP_BRKPT: + return "Process breakpoint"; + case TRAP_TRACE: + return "Process trace trap"; + default: + return "Trap"; + } + break; + case SIGCHLD: + switch(code) { + case CLD_EXITED: + return "Child has exited"; + case CLD_KILLED: + return "Child has terminated abnormally and did not create a core file"; + case CLD_DUMPED: + return "Child has terminated abnormally and created a core file"; + case CLD_TRAPPED: + return "Traced child has trapped"; + case CLD_STOPPED: + return "Child has stopped"; + case CLD_CONTINUED: + return "Stopped child has continued"; + default: + return "Child"; + } + break; + case SIGPOLL: + switch(code) { + case POLL_IN: + return "Data input available"; + case POLL_OUT: + return "Output buffers available"; + case POLL_MSG: + return "Input message available"; + case POLL_ERR: + return "I/O error"; + case POLL_PRI: + return "High priority input available"; + case POLL_HUP: + return "Device disconnected"; + default: + return "Pool"; + } + break; + case SIGABRT: + return "Process abort signal"; + case SIGALRM: + return "Alarm clock"; + case SIGCONT: + return "Continue executing, if stopped"; + case SIGHUP: + return "Hangup"; + case SIGINT: + return "Terminal interrupt signal"; + case SIGKILL: + return "Kill"; + case SIGPIPE: + return "Write on a pipe with no one to read it"; + case SIGQUIT: + return "Terminal quit signal"; + case SIGSTOP: + return "Stop executing"; + case SIGTERM: + return "Termination signal"; + case SIGTSTP: + return "Terminal stop signal"; + case SIGTTIN: + return "Background process attempting read"; + case SIGTTOU: + return "Background process attempting write"; + case SIGUSR1: + return "User-defined signal 1"; + case SIGUSR2: + return "User-defined signal 2"; + case SIGPROF: + return "Profiling timer expired"; + case SIGSYS: + return "Bad system call"; + case SIGVTALRM: + return "Virtual timer expired"; + case SIGURG: + return "High bandwidth data is available at a socket"; + case SIGXCPU: + return "CPU time limit exceeded"; + case SIGXFSZ: + return "File size limit exceeded"; + default: + switch(code) { + case SI_USER: + return "Signal sent by kill()"; + case SI_QUEUE: + return "Signal sent by the sigqueue()"; + case SI_TIMER: + return "Signal generated by expiration of a timer set by timer_settime()"; + case SI_ASYNCIO: + return "Signal generated by completion of an asynchronous I/O request"; + case SI_MESGQ: + return + "Signal generated by arrival of a message on an empty message queue"; + default: + return "Unknown signal"; + } + break; + } +} + +/** + * Get the backtrace size. Returns 0 if no backtrace is available. + */ +size_t coffeecatch_get_backtrace_size(void) { +#ifdef USE_UNWIND + const native_code_handler_struct* const t = coffeecatch_get(); + if (t != NULL) { + return t->frames_size; + } else { + return 0; + } +#else + return 0; +#endif +} + +/** + * Get the th element of the backtrace, or 0 upon error. + */ +uintptr_t coffeecatch_get_backtrace(ssize_t index) { +#ifdef USE_UNWIND + const native_code_handler_struct* const t = coffeecatch_get(); + if (t != NULL) { + if (index < 0) { + index = t->frames_size + index; + } + if (index >= 0 && (size_t) index < t->frames_size) { +#ifdef USE_CORKSCREW + return t->frames[index].absolute_pc; +#else + return t->frames[index]; +#endif + } + } +#else + (void) index; +#endif + return 0; +} + +/** + * Get the program counter, given a pointer to a ucontext_t context. + **/ +static uintptr_t coffeecatch_get_pc_from_ucontext(const ucontext_t *uc) { +#if (defined(__arm__)) + return uc->uc_mcontext.arm_pc; +#elif (defined(__x86_64__)) + return uc->uc_mcontext.gregs[REG_RIP]; +#elif (defined(__i386)) + return uc->uc_mcontext.gregs[REG_EIP]; +#elif (defined (__ppc__)) || (defined (__powerpc__)) + return uc->uc_mcontext.regs->nip; +#elif (defined(__hppa__)) + return uc->uc_mcontext.sc_iaoq[0] & ~0x3UL; +#elif (defined(__sparc__) && defined (__arch64__)) + return uc->uc_mcontext.mc_gregs[MC_PC]; +#elif (defined(__sparc__) && !defined (__arch64__)) + return uc->uc_mcontext.gregs[REG_PC]; +#elif (defined(__mips__)) + return uc->uc_mcontext.gregs[31]; +#else +#error "Architecture is unknown, please report me!" +#endif +} + +/* Is this module name look like a DLL ? + FIXME: find a better way to do that... */ +static int coffeecatch_is_dll(const char *name) { + size_t i; + for(i = 0; name[i] != '\0'; i++) { + if (name[i + 0] == '.' && + name[i + 1] == 's' && + name[i + 2] == 'o' && + ( name[i + 3] == '\0' || name[i + 3] == '.') ) { + return 1; + } + } + return 0; +} + +/* Extract a line information on a PC address. */ +static void format_pc_address_cb(uintptr_t pc, + void (*fun)(void *arg, const char *module, + uintptr_t addr, + const char *function, + uintptr_t offset), void *arg) { + if (pc != 0) { + Dl_info info; + void * const addr = (void*) pc; + /* dladdr() returns 0 on error, and nonzero on success. */ + if (dladdr(addr, &info) != 0 && info.dli_fname != NULL) { + const uintptr_t near = (uintptr_t) info.dli_saddr; + const uintptr_t offs = pc - near; + const uintptr_t addr_rel = pc - (uintptr_t) info.dli_fbase; + /* We need the absolute address for the main module (?). + TODO FIXME to be investigated. */ + const uintptr_t addr_to_use = coffeecatch_is_dll(info.dli_fname) + ? addr_rel : pc; + fun(arg, info.dli_fname, addr_to_use, info.dli_sname, offs); + } else { + fun(arg, NULL, pc, NULL, 0); + } + } +} + +typedef struct t_print_fun { + char *buffer; + size_t buffer_size; +} t_print_fun; + +static void print_fun(void *arg, const char *module, uintptr_t uaddr, + const char *function, uintptr_t offset) { + t_print_fun *const t = (t_print_fun*) arg; + char *const buffer = t->buffer; + const size_t buffer_size = t->buffer_size; + const void*const addr = (void*) uaddr; + if (module == NULL) { + snprintf(buffer, buffer_size, "[at %p]", addr); + } else if (function != NULL) { + snprintf(buffer, buffer_size, "[at %s:%p (%s+0x%x)]", module, addr, + function, (int) offset); + } else { + snprintf(buffer, buffer_size, "[at %s:%p]", module, addr); + } +} + +/* Format a line information on a PC address. */ +static void format_pc_address(char *buffer, size_t buffer_size, uintptr_t pc) { + t_print_fun t; + t.buffer = buffer; + t.buffer_size = buffer_size; + format_pc_address_cb(pc, print_fun, &t); +} + +/** + * Get the full error message associated with the crash. + */ +const char* coffeecatch_get_message() { + const int error = errno; + const native_code_handler_struct* const t = coffeecatch_get(); + + /* Found valid handler. */ + if (t != NULL) { + char * const buffer = t->stack_buffer; + const size_t buffer_len = t->stack_buffer_size; + size_t buffer_offs = 0; + + const char* const posix_desc = + coffeecatch_desc_sig(t->si.si_signo, t->si.si_code); + + /* Assertion failure ? */ + if ((t->code == SIGABRT +#ifdef __ANDROID__ + /* See Android BUG #16672: + * "C assert() failure causes SIGSEGV when it should cause SIGABRT" */ + || (t->code == SIGSEGV && (uintptr_t) t->si.si_addr == 0xdeadbaad) +#endif + ) && t->expression != NULL) { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, + "assertion '%s' failed at %s:%d", + t->expression, t->file, t->line); + buffer_offs += strlen(&buffer[buffer_offs]); + } + /* Signal */ + else { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, "signal %d", + t->si.si_signo); + buffer_offs += strlen(&buffer[buffer_offs]); + + /* Description */ + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " (%s)", + posix_desc); + buffer_offs += strlen(&buffer[buffer_offs]); + + /* Address of faulting instruction */ + if (t->si.si_signo == SIGILL || t->si.si_signo == SIGSEGV) { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " at address %p", + t->si.si_addr); + buffer_offs += strlen(&buffer[buffer_offs]); + } + } + + /* [POSIX] If non-zero, an errno value associated with this signal, + as defined in . */ + if (t->si.si_errno != 0) { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, ": "); + buffer_offs += strlen(&buffer[buffer_offs]); + if (strerror_r(t->si.si_errno, &buffer[buffer_offs], + buffer_len - buffer_offs) == 0) { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, + "unknown error"); + buffer_offs += strlen(&buffer[buffer_offs]); + } + } + + /* Sending process ID. */ + if (t->si.si_signo == SIGCHLD && t->si.si_pid != 0) { + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, + " (sent by pid %d)", (int) t->si.si_pid); + buffer_offs += strlen(&buffer[buffer_offs]); + } + + /* Faulting program counter location. */ + if (coffeecatch_get_pc_from_ucontext(&t->uc) != 0) { + const uintptr_t pc = coffeecatch_get_pc_from_ucontext(&t->uc); + snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " "); + buffer_offs += strlen(&buffer[buffer_offs]); + format_pc_address(&buffer[buffer_offs], buffer_len - buffer_offs, pc); + buffer_offs += strlen(&buffer[buffer_offs]); + } + + /* Return string. */ + buffer[buffer_offs] = '\0'; + return t->stack_buffer; + } else { + /* Static buffer in case of emergency */ + static char buffer[256]; +#ifdef _GNU_SOURCE + return strerror_r(error, &buffer[0], sizeof(buffer)); +#else + const int code = strerror_r(error, &buffer[0], sizeof(buffer)); + errno = error; + if (code == 0) { + return buffer; + } else { + return "unknown error during crash handler setup"; + } +#endif + } +} + +#if (defined(USE_CORKSCREW)) +typedef struct t_coffeecatch_backtrace_symbols_fun { + void (*fun)(void *arg, const char *module, uintptr_t addr, + const char *function, uintptr_t offset); + void *arg; +} t_coffeecatch_backtrace_symbols_fun; + +static void coffeecatch_backtrace_symbols_fun(void *arg, const backtrace_symbol_t *sym) { + t_coffeecatch_backtrace_symbols_fun *const bt = + (t_coffeecatch_backtrace_symbols_fun*) arg; + const char *symbol = sym->demangled_name != NULL + ? sym->demangled_name : sym->symbol_name; + const uintptr_t rel = sym->relative_pc - sym->relative_symbol_addr; + bt->fun(bt->arg, sym->map_name, sym->relative_pc, symbol, rel); +} +#endif + +/** + * Enumerate backtrace information. + */ +void coffeecatch_get_backtrace_info(void (*fun)(void *arg, + const char *module, + uintptr_t addr, + const char *function, + uintptr_t offset), void *arg) { + const native_code_handler_struct* const t = coffeecatch_get(); + if (t != NULL) { +#if (defined(USE_CORKSCREW)) + t_coffeecatch_backtrace_symbols_fun bt; + bt.fun = fun; + bt.arg = arg; + coffeecatch_backtrace_symbols(t->frames, t->frames_size, + coffeecatch_backtrace_symbols_fun, &bt); +#elif (defined(USE_UNWIND)) + size_t i; + for(i = 0; i < t->frames_size; i++) { + const uintptr_t pc = t->frames[i]; + format_pc_address_cb(pc, fun, arg); + } +#else + (void) fun; + (void) arg; +#endif + } +} + +/** + * Calls coffeecatch_handler_setup(1) to setup a crash handler, mark the + * context as valid, and return 0 upon success. + */ +int coffeecatch_setup() { + if (coffeecatch_handler_setup(1) == 0) { + native_code_handler_struct *const t = coffeecatch_get(); + assert(t != NULL); + t->ctx_is_set = 1; + return 0; + } else { + return -1; + } +} + +/** + * Calls coffeecatch_handler_cleanup() + */ +void coffeecatch_cleanup() { + native_code_handler_struct *const t = coffeecatch_get(); + assert(t != NULL); + t->ctx_is_set = 0; + coffeecatch_handler_cleanup(); +} + +sigjmp_buf* coffeecatch_get_ctx() { + native_code_handler_struct* t = coffeecatch_get(); + assert(t != NULL); + return &t->ctx; +} + +void coffeecatch_abort(const char* exp, const char* file, int line) { + native_code_handler_struct *const t = coffeecatch_get(); + if (t != NULL) { + t->expression = exp; + t->file = file; + t->line = line; + } + abort(); +} diff --git a/android/cpp/coffeecatch_r10c.diff b/android/cpp/coffeecatch_r10c.diff new file mode 100644 index 000000000..ddee5b329 --- /dev/null +++ b/android/cpp/coffeecatch_r10c.diff @@ -0,0 +1,216 @@ +diff --git a/coffeecatch.c b/coffeecatch.c +index 448fe0f..f39fcdb 100644 +--- a/coffeecatch.c ++++ b/coffeecatch.c +@@ -43,10 +43,7 @@ + #include + #include + #include +-#if defined(__ANDROID__) && !defined(__BIONIC_HAVE_UCONTEXT_T) && \ +- defined(__arm__) && !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT) +-#include +-#endif ++#include + #if (defined(USE_UNWIND) && !defined(USE_CORKSCREW)) + #include + #endif +@@ -88,154 +85,34 @@ static const int native_sig_catch[SIG_CATCH_COUNT + 1] + /* Maximum value of a caught signal. */ + #define SIG_NUMBER_MAX 32 + +-#if (defined(__ANDROID__) && (!defined(__BIONIC_HAVE_UCONTEXT_T))) +-#ifndef ucontext_h_seen +-#define ucontext_h_seen +- +-/* stack_t definition */ +-#include +- +-#if defined(__arm__) +- +-/* Taken from richard.quirk's header file. (Android does not have it) */ +- +-typedef struct ucontext { +- unsigned long uc_flags; +- struct ucontext *uc_link; +- stack_t uc_stack; +- struct sigcontext uc_mcontext; +- unsigned long uc_sigmask; +-} ucontext_t; +- +-#elif defined(__i386__) +- +-/* Taken from Google Breakpad. */ +- +-/* 80-bit floating-point register */ +-struct _libc_fpreg { +- unsigned short significand[4]; +- unsigned short exponent; +-}; +- +-/* Simple floating-point state, see FNSTENV instruction */ +-struct _libc_fpstate { +- unsigned long cw; +- unsigned long sw; +- unsigned long tag; +- unsigned long ipoff; +- unsigned long cssel; +- unsigned long dataoff; +- unsigned long datasel; +- struct _libc_fpreg _st[8]; +- unsigned long status; +-}; +- +-typedef uint32_t greg_t; +- +-typedef struct { +- uint32_t gregs[19]; +- struct _libc_fpstate* fpregs; +- uint32_t oldmask; +- uint32_t cr2; +-} mcontext_t; +- +-enum { +- REG_GS = 0, +- REG_FS, +- REG_ES, +- REG_DS, +- REG_EDI, +- REG_ESI, +- REG_EBP, +- REG_ESP, +- REG_EBX, +- REG_EDX, +- REG_ECX, +- REG_EAX, +- REG_TRAPNO, +- REG_ERR, +- REG_EIP, +- REG_CS, +- REG_EFL, +- REG_UESP, +- REG_SS, +-}; +- +-typedef struct ucontext { +- uint32_t uc_flags; +- struct ucontext* uc_link; +- stack_t uc_stack; +- mcontext_t uc_mcontext; +-} ucontext_t; +- +-#elif defined(__mips__) +- +-/* Taken from Google Breakpad. */ +- +-typedef struct { +- uint32_t regmask; +- uint32_t status; +- uint64_t pc; +- uint64_t gregs[32]; +- uint64_t fpregs[32]; +- uint32_t acx; +- uint32_t fpc_csr; +- uint32_t fpc_eir; +- uint32_t used_math; +- uint32_t dsp; +- uint64_t mdhi; +- uint64_t mdlo; +- uint32_t hi1; +- uint32_t lo1; +- uint32_t hi2; +- uint32_t lo2; +- uint32_t hi3; +- uint32_t lo3; +-} mcontext_t; +- +-typedef struct ucontext { +- uint32_t uc_flags; +- struct ucontext* uc_link; +- stack_t uc_stack; +- mcontext_t uc_mcontext; +-} ucontext_t; +- +-#else +-#error "Architecture is not supported (unknown ucontext layout)" +-#endif +- +-#endif +- + #ifdef USE_CORKSCREW + typedef struct map_info_t map_info_t; + /* Extracted from Android's include/corkscrew/backtrace.h */ +-typedef struct { +- uintptr_t absolute_pc; +- uintptr_t stack_top; +- size_t stack_size; +-} backtrace_frame_t; +-typedef struct { +- uintptr_t relative_pc; +- uintptr_t relative_symbol_addr; +- char* map_name; +- char* symbol_name; +- char* demangled_name; ++typedef struct { ++ uintptr_t absolute_pc; ++ uintptr_t stack_top; ++ size_t stack_size; ++} backtrace_frame_t; ++typedef struct { ++ uintptr_t relative_pc; ++ uintptr_t relative_symbol_addr; ++ char* map_name; ++ char* symbol_name; ++ char* demangled_name; + } backtrace_symbol_t; + /* Extracted from Android's libcorkscrew/arch-arm/backtrace-arm.c */ +-typedef ssize_t (*t_unwind_backtrace_signal_arch) +-(siginfo_t* si, void* sc, const map_info_t* lst, backtrace_frame_t* bt, +-size_t ignore_depth, size_t max_depth); +-typedef map_info_t* (*t_acquire_my_map_info_list)(); +-typedef void (*t_release_my_map_info_list)(map_info_t* milist); +-typedef void (*t_get_backtrace_symbols)(const backtrace_frame_t* backtrace, ++typedef ssize_t (*t_unwind_backtrace_signal_arch) ++(siginfo_t* si, void* sc, const map_info_t* lst, backtrace_frame_t* bt, ++size_t ignore_depth, size_t max_depth); ++typedef map_info_t* (*t_acquire_my_map_info_list)(); ++typedef void (*t_release_my_map_info_list)(map_info_t* milist); ++typedef void (*t_get_backtrace_symbols)(const backtrace_frame_t* backtrace, + size_t frames, + backtrace_symbol_t* symbols); + typedef void (*t_free_backtrace_symbols)(backtrace_symbol_t* symbols, + size_t frames); + #endif + +-#endif +- + /* Process-wide crash handler structure. */ + typedef struct native_code_global_struct { + /* Initialized. */ +@@ -341,7 +218,7 @@ static ssize_t coffeecatch_backtrace_signal(siginfo_t* si, void* sc, + && acquire_my_map_info_list != NULL + && release_my_map_info_list != NULL) { + map_info_t*const info = acquire_my_map_info_list(); +- const ssize_t size = ++ const ssize_t size = + unwind_backtrace_signal_arch(si, sc, info, frames, ignore_depth, + max_depth); + release_my_map_info_list(info); +@@ -1073,12 +950,12 @@ static uintptr_t coffeecatch_get_pc_from_ucontext(const ucontext_t *uc) { + return uc->uc_mcontext.regs->nip; + #elif (defined(__hppa__)) + return uc->uc_mcontext.sc_iaoq[0] & ~0x3UL; +-#elif (defined(__sparc__) && defined (__arch64__)) +- return uc->uc_mcontext.mc_gregs[MC_PC]; +-#elif (defined(__sparc__) && !defined (__arch64__)) +- return uc->uc_mcontext.gregs[REG_PC]; +-#elif (defined(__mips__)) +- return uc->uc_mcontext.gregs[31]; ++#elif (defined(__sparc__) && defined (__arch64__)) ++ return uc->uc_mcontext.mc_gregs[MC_PC]; ++#elif (defined(__sparc__) && !defined (__arch64__)) ++ return uc->uc_mcontext.gregs[REG_PC]; ++#elif (defined(__mips__)) ++ return uc->uc_mcontext.gregs[31]; + #else + #error "Architecture is unknown, please report me!" + #endif \ No newline at end of file diff --git a/android/cpp/coffeejni.c b/android/cpp/coffeejni.c new file mode 100644 index 000000000..6dc9db5fa --- /dev/null +++ b/android/cpp/coffeejni.c @@ -0,0 +1,185 @@ +/* CoffeeCatch, a tiny native signal handler/catcher for JNI code. + * (especially for Android/Dalvik) + * + * Copyright (c) 2013, Xavier Roche (http://www.httrack.com/) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef COFFEECATCH_JNI_H +#define COFFEECATCH_JNI_H + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct t_bt_fun { + JNIEnv* env; + jclass cls; + jclass cls_ste; + jmethodID cons_ste; + jobjectArray elements; + size_t size; + size_t index; +} t_bt_fun; + +static char* bt_print(const char *function, uintptr_t offset) { + if (function != NULL) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), "%s:%p", function, (void*) offset); + return strdup(buffer); + } else { + return ""; + } +} + +static char* bt_addr(uintptr_t addr) { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%p", (void*) addr); + return strdup(buffer); +} + +#define IS_VALID_CLASS_CHAR(C) ( \ + ((C) >= 'a' && (C) <= 'z') \ + || ((C) >= 'A' && (C) <= 'Z') \ + || ((C) >= '0' && (C) <= '9') \ + || (C) == '_' \ + ) + +static char* bt_module(const char *module) { + if (module != NULL) { + size_t i; + char *copy; + if (*module == '/') { + module++; + } + copy = strdup(module); + /* Pseudo-java-class. */ + for(i = 0; copy[i] != '\0'; i++) { + if (copy[i] == '/') { + copy[i] = '.'; + } else if (!IS_VALID_CLASS_CHAR(copy[i])) { + copy[i] = '_'; + } + } + return copy; + } else { + return ""; + } +} + +static void bt_fun(void *arg, const char *module, uintptr_t addr, + const char *function, uintptr_t offset) { + t_bt_fun *const t = (t_bt_fun*) arg; + JNIEnv*const env = t->env; + jstring declaringClass = (*env)->NewStringUTF(env, bt_module(module)); + jstring methodName = (*env)->NewStringUTF(env, bt_addr(addr)); + jstring fileName = (*env)->NewStringUTF(env, bt_print(function, offset)); + const int lineNumber = function != NULL ? 0 : -2; /* "-2" is "inside JNI code" */ + jobject trace = (*env)->NewObject(env, t->cls_ste, t->cons_ste, + declaringClass, methodName, fileName, + lineNumber); + if (t->index < t->size) { + (*t->env)->SetObjectArrayElement(t->env, t->elements, t->index++, trace); + } +} + +void coffeecatch_throw_exception(JNIEnv* env) { + jclass cls = (*env)->FindClass(env, "java/lang/Error"); + jclass cls_ste = (*env)->FindClass(env, "java/lang/StackTraceElement"); + + jmethodID cons = (*env)->GetMethodID(env, cls, "", "(Ljava/lang/String;)V"); + jmethodID cons_cause = (*env)->GetMethodID(env, cls, "", "(Ljava/lang/String;Ljava/lang/Throwable;)V"); + jmethodID cons_ste = (*env)->GetMethodID(env, cls_ste, "", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V"); + jmethodID meth_sste = (*env)->GetMethodID(env, cls, "setStackTrace", + "([Ljava/lang/StackTraceElement;)V"); + + /* Exception message. */ + const char*const message = coffeecatch_get_message(); + jstring str = (*env)->NewStringUTF(env, strdup(message)); + + /* Final exception. */ + jthrowable exception; + + /* Add pseudo-stack trace. */ + const ssize_t bt_size = coffeecatch_get_backtrace_size(); + + assert(cls != NULL); + assert(cls_ste != NULL); + assert(cons != NULL); + assert(cons_cause != NULL); + assert(cons_ste != NULL); + assert(meth_sste != NULL); + + assert(message != NULL); + assert(str != NULL); + + /* Can we produce a stack trace ? */ + if (bt_size > 0) { + /* Create secondary exception. */ + jthrowable cause = (jthrowable) (*env)->NewObject(env, cls, cons, str); + + /* Stack trace. */ + jobjectArray elements = + (*env)->NewObjectArray(env, bt_size, cls_ste, NULL); + if (elements != NULL) { + t_bt_fun t; + t.env = env; + t.cls = cls; + t.cls_ste = cls_ste; + t.cons_ste = cons_ste; + t.elements = elements; + t.index = 0; + t.size = bt_size; + coffeecatch_get_backtrace_info(bt_fun, &t); + (*env)->CallVoidMethod(env, cause, meth_sste, elements); + } + + /* Primary exception */ + exception = (jthrowable) (*env)->NewObject(env, cls, cons_cause, str, cause); + } else { + /* Simple exception */ + exception = (jthrowable) (*env)->NewObject(env, cls, cons, str); + } + + /* Throw exception. */ + if (exception != NULL) { + (*env)->Throw(env, exception); + } else { + (*env)->ThrowNew(env, cls, strdup(message)); + } +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index b8e11961a..17230817a 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -11,6 +11,8 @@ #include +#include + #include #include #include @@ -144,7 +146,8 @@ jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_, jstrin mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreate"); cache_path = std_string_from_jstring(env, cache_path_); data_path = std_string_from_jstring(env, data_path_); - NativeMapView* native_map_view = new NativeMapView(env, obj); + NativeMapView* native_map_view = nullptr; + COFFEE_TRY_JNI(env, native_map_view = new NativeMapView(env, obj)); if (native_map_view == nullptr) { throw_error(env, "Unable to create NativeMapView."); return 0; @@ -165,7 +168,9 @@ void JNICALL nativeInitializeDisplay(JNIEnv* env, jobject obj, jlong native_map_ mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeDisplay"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - if (!native_map_view->initializeDisplay()) { + bool ret = false; + COFFEE_TRY_JNI(env, ret = native_map_view->initializeDisplay()); + if (!ret) { throw_error(env, "Unable to initialize GL display."); } } @@ -174,14 +179,16 @@ void JNICALL nativeTerminateDisplay(JNIEnv* env, jobject obj, jlong native_map_v mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminateDisplay"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->terminateDisplay(); + COFFEE_TRY_JNI(env, native_map_view->terminateDisplay()); } void JNICALL nativeInitializeContext(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeInitializeContext"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - if (!native_map_view->initializeContext()) { + bool ret = false; + COFFEE_TRY_JNI(env, ret = native_map_view->initializeContext()); + if (!ret) { throw_error(env, "Unable to initialize GL context."); } } @@ -190,14 +197,16 @@ void JNICALL nativeTerminateContext(JNIEnv* env, jobject obj, jlong native_map_v mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminateContext"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->terminateContext(); + COFFEE_TRY_JNI(env, native_map_view->terminateContext()); } void JNICALL nativeCreateSurface(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject surface) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreateSurface"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - if (!native_map_view->createSurface(ANativeWindow_fromSurface(env, surface))) { + bool ret = false; + COFFEE_TRY_JNI(env, native_map_view->createSurface(ANativeWindow_fromSurface(env, surface))); + if (!ret) { throw_error(env, "Unable to create GL surface."); } } @@ -206,84 +215,86 @@ void JNICALL nativeDestroySurface(JNIEnv* env, jobject obj, jlong native_map_vie mbgl::Log::Debug(mbgl::Event::JNI, "nativeDestroySurface"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->destroySurface(); + COFFEE_TRY_JNI(env, native_map_view->destroySurface()); } void JNICALL nativeStart(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeStart"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->start(); + COFFEE_TRY_JNI(env, native_map_view->start()); } void JNICALL nativeStop(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeStop"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->stop(); + COFFEE_TRY_JNI(env, native_map_view->stop()); } void JNICALL nativePause(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativePause"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->pause(); + COFFEE_TRY_JNI(env, native_map_view->pause()); } void JNICALL nativeResume(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeResume"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->resume(); + COFFEE_TRY_JNI(env, native_map_view->resume()); } void JNICALL nativeRun(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeRun"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().run(); + COFFEE_TRY_JNI(env, native_map_view->getMap().run()); } void JNICALL nativeRerender(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeRerender"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().rerender(); + COFFEE_TRY_JNI(env, native_map_view->getMap().rerender()); } void JNICALL nativeUpdate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeUpdate"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().update(); + COFFEE_TRY_JNI(env, native_map_view->getMap().update()); } void JNICALL nativeCleanup(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeCleanup"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().cleanup(); + COFFEE_TRY_JNI(env, native_map_view->getMap().cleanup()); } void JNICALL nativeTerminate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeTerminate"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().terminate(); + COFFEE_TRY_JNI(env, native_map_view->getMap().terminate()); } jboolean JNICALL nativeNeedsSwap(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeNeedsSwap"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap().needsSwap(); + jboolean ret = false; + COFFEE_TRY_JNI(env, ret = native_map_view->getMap().needsSwap()); + return ret; } void JNICALL nativeSwapped(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSwapped"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().swapped(); + COFFEE_TRY_JNI(env, (native_map_view->getMap().swapped())); } void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio) { @@ -294,7 +305,7 @@ void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, j assert(width <= UINT16_MAX); assert(height <= UINT16_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().resize(width, height, ratio); + COFFEE_TRY_JNI(env, native_map_view->getMap().resize(width, height, ratio)); } void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jint width, jint height, jfloat ratio, jint fb_width, jint fb_height) { @@ -309,21 +320,23 @@ void JNICALL nativeResize(JNIEnv* env, jobject obj, jlong native_map_view_ptr, j assert(fb_width <= UINT16_MAX); assert(fb_height <= UINT16_MAX); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().resize(width, height, ratio, fb_width, fb_height); + COFFEE_TRY_JNI(env, native_map_view->getMap().resize(width, height, ratio, fb_width, fb_height)); } void JNICALL nativeSetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject classes) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetAppliedClasses"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setAppliedClasses(std_vector_string_from_jobject(env, classes)); + COFFEE_TRY_JNI(env, native_map_view->getMap().setAppliedClasses(std_vector_string_from_jobject(env, classes))); } jobject JNICALL nativeGetAppliedClasses(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetAppliedClasses"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return std_vector_string_to_jobject(env, native_map_view->getMap().getAppliedClasses()); + jobject ret = nullptr; + COFFEE_TRY_JNI(env, ret = std_vector_string_to_jobject(env, native_map_view->getMap().getAppliedClasses())); + return ret; } void JNICALL nativeSetDefaultTransitionDuration(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jlong duration_milliseconds) { @@ -331,56 +344,60 @@ void JNICALL nativeSetDefaultTransitionDuration(JNIEnv* env, jobject obj, jlong assert(native_map_view_ptr != 0); assert(duration_milliseconds >= 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setDefaultTransitionDuration(duration_milliseconds); + COFFEE_TRY_JNI(env, native_map_view->getMap().setDefaultTransitionDuration(duration_milliseconds)); } void JNICALL nativeSetStyleURL(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring url) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetStyleURL"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setStyleURL(std_string_from_jstring(env, url)); + COFFEE_TRY_JNI(env, native_map_view->getMap().setStyleURL(std_string_from_jstring(env, url))); } void JNICALL nativeSetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring new_style_json, jstring base) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetStyleJSON"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setStyleJSON(std_string_from_jstring(env, new_style_json), std_string_from_jstring(env, base)); + COFFEE_TRY_JNI(env, native_map_view->getMap().setStyleJSON(std_string_from_jstring(env, new_style_json), std_string_from_jstring(env, base))); } jstring JNICALL nativeGetStyleJSON(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetStyleJSON"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return std_string_to_jstring(env, native_map_view->getMap().getStyleJSON()); + jstring ret = nullptr; + COFFEE_TRY_JNI(env, ret = std_string_to_jstring(env, native_map_view->getMap().getStyleJSON())); + return ret; } void JNICALL nativeSetAccessToken(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jstring access_token) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetAccessToken"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setAccessToken(std_string_from_jstring(env, access_token)); + COFFEE_TRY_JNI(env, native_map_view->getMap().setAccessToken(std_string_from_jstring(env, access_token))); } jstring JNICALL nativeGetAccessToken(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetAccessToken"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return std_string_to_jstring(env, native_map_view->getMap().getAccessToken()); + jstring ret = nullptr; + COFFEE_TRY_JNI(env, ret = std_string_to_jstring(env, native_map_view->getMap().getAccessToken())); + return ret; } void JNICALL nativeCancelTransitions(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeCancelTransitions"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().cancelTransitions(); + COFFEE_TRY_JNI(env, native_map_view->getMap().cancelTransitions()); } void JNICALL nativeMoveBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble dx, jdouble dy, jdouble duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeMoveBy"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().moveBy(dx, dy, duration); + COFFEE_TRY_JNI(env, native_map_view->getMap().moveBy(dx, dy, duration)); } void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat, jdouble duration) { @@ -400,15 +417,15 @@ void JNICALL nativeSetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr return; } - native_map_view->getMap().setLonLat(lon, lat, duration); + COFFEE_TRY_JNI(env, native_map_view->getMap().setLonLat(lon, lat, duration)); } jobject JNICALL nativeGetLonLat(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetLonLat"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - double lon, lat; - native_map_view->getMap().getLonLat(lon, lat); + double lon = 0.0, lat = 0.0; + COFFEE_TRY_JNI(env, native_map_view->getMap().getLonLat(lon, lat)); jobject ret = env->NewObject(lon_lat_class, lon_lat_constructor_id, lon, lat); if (ret == nullptr) { @@ -423,56 +440,60 @@ void JNICALL nativeStartPanning(JNIEnv* env, jobject obj, jlong native_map_view_ mbgl::Log::Debug(mbgl::Event::JNI, "nativeStartPanning"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().startPanning(); + COFFEE_TRY_JNI(env, native_map_view->getMap().startPanning()); } void JNICALL nativeStopPanning(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeStopPanning"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().stopPanning(); + COFFEE_TRY_JNI(env, native_map_view->getMap().stopPanning()); } void JNICALL nativeResetPosition(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeResetPosition"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().resetPosition(); + COFFEE_TRY_JNI(env, native_map_view->getMap().resetPosition()); } void JNICALL nativeScaleBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble ds, jdouble cx, jdouble cy, jdouble duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeScaleBy"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().scaleBy(ds, cx, cy, duration); + COFFEE_TRY_JNI(env, native_map_view->getMap().scaleBy(ds, cx, cy, duration)); } void JNICALL nativeSetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble scale, jdouble cx, jdouble cy, jdouble duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetScale"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setScale(scale, cx, cy, duration); + COFFEE_TRY_JNI(env, native_map_view->getMap().setScale(scale, cx, cy, duration)); } jdouble JNICALL nativeGetScale(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetScale"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap().getScale(); + jdouble ret = 0.0; + COFFEE_TRY_JNI(env, ret = native_map_view->getMap().getScale()); + return ret; } void JNICALL nativeSetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble zoom, jdouble duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetZoom"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setZoom(zoom, duration); + COFFEE_TRY_JNI(env, native_map_view->getMap().setZoom(zoom, duration)); } jdouble JNICALL nativeGetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetZoom"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap().getZoom(); + jdouble ret = 0.0; + COFFEE_TRY_JNI(env, ret = native_map_view->getMap().getZoom()); + return ret; } void JNICALL nativeSetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jobject lon_lat_zoom, jdouble duration) { @@ -498,15 +519,15 @@ void JNICALL nativeSetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view return; } - native_map_view->getMap().setLonLatZoom(lon, lat, zoom, duration); + COFFEE_TRY_JNI(env, native_map_view->getMap().setLonLatZoom(lon, lat, zoom, duration)); } jobject JNICALL nativeGetLonLatZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetLonLatZoom"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - double lon, lat, zoom; - native_map_view->getMap().getLonLatZoom(lon, lat, zoom); + double lon = 0.0, lat = 0.0, zoom = 0.0; + COFFEE_TRY_JNI(env, native_map_view->getMap().getLonLatZoom(lon, lat, zoom)); jobject ret = env->NewObject(lon_lat_zoom_class, lon_lat_zoom_constructor_id, lon, lat, zoom); if (ret == nullptr) { @@ -521,119 +542,129 @@ void JNICALL nativeResetZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr mbgl::Log::Debug(mbgl::Event::JNI, "nativeResetZoom"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().resetZoom(); + COFFEE_TRY_JNI(env, native_map_view->getMap().resetZoom()); } void JNICALL nativeStartScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeStartScaling"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().startScaling(); + COFFEE_TRY_JNI(env, native_map_view->getMap().startScaling()); } void JNICALL nativeStopScaling(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeStopScaling"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().stopScaling(); + COFFEE_TRY_JNI(env, native_map_view->getMap().stopScaling()); } jdouble JNICALL nativeGetMinZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetMinZoom"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap().getMinZoom(); + jdouble ret = 0.0; + COFFEE_TRY_JNI(env, ret = native_map_view->getMap().getMinZoom()); + return ret; } jdouble JNICALL nativeGetMaxZoom(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetMaxZoom"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap().getMaxZoom(); + jdouble ret = 0.0; + COFFEE_TRY_JNI(env, ret = native_map_view->getMap().getMaxZoom()); + return ret; } void JNICALL nativeRotateBy(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble sx, jdouble sy, jdouble ex, jdouble ey, jdouble duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeRotateBy"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().rotateBy(sx, sy, ex, ey, duration); + COFFEE_TRY_JNI(env, native_map_view->getMap().rotateBy(sx, sy, ex, ey, duration)); } void JNICALL nativeSetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble degrees, jdouble duration) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetBearing"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setBearing(degrees, duration); + COFFEE_TRY_JNI(env, native_map_view->getMap().setBearing(degrees, duration)); } void JNICALL nativeSetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jdouble degrees, jdouble cx, jdouble cy) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetBearing"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setBearing(degrees, cx, cy); + COFFEE_TRY_JNI(env, native_map_view->getMap().setBearing(degrees, cx, cy)); } jdouble JNICALL nativeGetBearing(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetBearing"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap().getBearing(); + jdouble ret = 0.0; + COFFEE_TRY_JNI(env, ret = native_map_view->getMap().getBearing()); + return ret; } void JNICALL nativeResetNorth(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeResetNorth"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().resetNorth(); + COFFEE_TRY_JNI(env, native_map_view->getMap().resetNorth()); } void JNICALL nativeStartRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeStartRotating"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().startRotating(); + COFFEE_TRY_JNI(env, native_map_view->getMap().startRotating()); } void JNICALL nativeStopRotating(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeStopRotating"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().stopRotating(); + COFFEE_TRY_JNI(env, native_map_view->getMap().stopRotating()); } jboolean JNICALL nativeCanRotate(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeCanRotate"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap().canRotate(); + jboolean ret = false; + COFFEE_TRY_JNI(env, ret = native_map_view->getMap().canRotate()); + return ret; } void JNICALL nativeSetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jboolean debug) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetDebug"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setDebug(debug); + COFFEE_TRY_JNI(env, native_map_view->getMap().setDebug(debug)); } void JNICALL nativeToggleDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeToggleDebug"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().toggleDebug(); + COFFEE_TRY_JNI(env, native_map_view->getMap().toggleDebug()); } jboolean JNICALL nativeGetDebug(JNIEnv* env, jobject obj, jlong native_map_view_ptr) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetDebug"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - return native_map_view->getMap().getDebug(); + jboolean ret = false; + COFFEE_TRY_JNI(env, ret = native_map_view->getMap().getDebug()); + return ret; } void JNICALL nativeSetReachability(JNIEnv* env, jobject obj, jlong native_map_view_ptr, jboolean status) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetReachability"); assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); - native_map_view->getMap().setReachability(status); + COFFEE_TRY_JNI(env, native_map_view->getMap().setReachability(status)); } } @@ -645,7 +676,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { mbgl::Log::Debug(mbgl::Event::JNI, "JNI_OnLoad"); - JNIEnv* env = nullptr; + JNIEnv* env = nullptr; jint ret = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); if (ret != JNI_OK) { mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret); diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp index c9cb96d5a..7a04688b0 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -329,7 +329,7 @@ bool NativeMapView::createSurface(ANativeWindow* window_) { log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); // In the emulator this returns NULL with error code 0? https://code.google.com/p/android/issues/detail?id=78977 log_gl_string(GL_EXTENSIONS, "Extensions"); - loadExtensions(); + loadExtensions(); if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { @@ -449,7 +449,7 @@ EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], eglGetError()); return nullptr; } - + mbgl::Log::Info(mbgl::Event::OpenGL, "Caveat: %d", caveat); mbgl::Log::Info(mbgl::Event::OpenGL, "Conformant: %d", conformant); mbgl::Log::Info(mbgl::Event::OpenGL, "Color: %d", bits); @@ -508,7 +508,7 @@ void NativeMapView::start() { assert(display != EGL_NO_DISPLAY); assert(context != EGL_NO_CONTEXT); - + map.start(true); } @@ -520,7 +520,7 @@ void NativeMapView::loadExtensions() { } std::string extensions(reinterpret_cast(str)); - + if (extensions.find("GL_OES_vertex_array_object") != std::string::npos) { mbgl::Log::Info(mbgl::Event::OpenGL, "Using GL_OES_vertex_array_object."); gl::BindVertexArray = (gl::PFNGLBINDVERTEXARRAYPROC)eglGetProcAddress("glBindVertexArrayOES"); @@ -543,7 +543,7 @@ void NativeMapView::stop() { assert(display != EGL_NO_DISPLAY); assert(context != EGL_NO_CONTEXT); - + map.stop(); } @@ -553,7 +553,7 @@ void NativeMapView::pause(bool wait_for_pause) { assert(display != EGL_NO_DISPLAY); assert(context != EGL_NO_CONTEXT); - + map.pause(wait_for_pause); } diff --git a/android/mapboxgl-app.gyp b/android/mapboxgl-app.gyp index 118669542..b01050f0b 100644 --- a/android/mapboxgl-app.gyp +++ b/android/mapboxgl-app.gyp @@ -10,6 +10,8 @@ 'sources': [ './cpp/native_map_view.cpp', './cpp/jni.cpp', + './cpp/coffeecatch.c', + './cpp/coffeejni.c', ], 'cflags_cc': [ '-I<(boost_root)/include', diff --git a/include/coffeecatch/coffeecatch.h b/include/coffeecatch/coffeecatch.h new file mode 100644 index 000000000..3aa00ddd4 --- /dev/null +++ b/include/coffeecatch/coffeecatch.h @@ -0,0 +1,226 @@ +/* CoffeeCatch, a tiny native signal handler/catcher for JNI code. + * (especially for Android/Dalvik) + * + * Copyright (c) 2013, Xavier Roche (http://www.httrack.com/) + * All rights reserved. + * See the "License" section below for the licensing terms. + * + * Description: + * + * Allows to "gracefully" recover from a signal (segv, sibus...) as if it was + * a Java exception. It will not gracefully recover from allocator/mutexes + * corruption etc., however, but at least "most" gentle crashes (null pointer + * dereferencing, integer division, stack overflow etc.) should be handled + * without too much troubles. + * + * The handler is thread-safe, but client must have exclusive control on the + * signal handlers (ie. the library is installing its own signal handlers on + * top of the existing ones). + * + * You must build all your libraries with `-funwind-tables', to get proper + * unwinding information on all binaries. On ARM, you may also use the + * `--no-merge-exidx-entries` linker switch, to solve certain issues with + * unwinding (the switch is possibly not needed anymore). + * On Android, this can be achieved by using this line in the Android.mk file + * in each library block: + * LOCAL_CFLAGS := -funwind-tables -Wl,--no-merge-exidx-entries + * + * Example: + * + * COFFEE_TRY() { + * call_some_native_function() + * } COFFEE_CATCH() { + * const char*const message = coffeecatch_get_message(); + * jclass cls = (*env)->FindClass(env, "java/lang/RuntimeException"); + * (*env)->ThrowNew(env, cls, strdup(message)); + * } COFFEE_END(); + * + * Implementation notes: + * + * Currently the library is installing both alternate stack and signal + * handlers for known signals (SIGABRT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, + * SIGSEGV, SIGSTKFLT), and is using sigsetjmp()/siglongjmp() to return to + * "userland" (compared to signal handler context). As a security, an alarm + * is started as soon as a fatal signal is detected (ie. not something the + * JVM will handle) to kill the process after a grace period. Be sure your + * program will exit quickly after the error is caught, or call alarm(0) + * to cancel the pending time-bomb. + * The signal handlers had to be written with caution, because the virtual + * machine might be using signals (including SEGV) to handle JIT compiler, + * and some clever optimizations (such as NullPointerException handling) + * We are using several signal-unsafe functions, namely: + * - siglongjmp() to return to userland + * - pthread_getspecific() to get thread-specific setup + * + * License: + * + * Copyright (c) 2013, Xavier Roche (http://www.httrack.com/) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef COFFEECATCH_H +#define COFFEECATCH_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Setup crash handler to enter in a protected section. If a recognized signal + * is received in this section, the execution will be diverted to the + * COFFEE_CATCH() block. + * + * Note: you MUST use the following pattern when using this macro: + * COFFEE_TRY() { + * .. protected section without exit point + * } COFFEE_CATCH() { + * .. handler section without exit point + * } COFFEE_END(); + * + * You can not exit the protected section block, or the handler section block, + * using statements such as "return", because the cleanup code would not be + * executed. + * + * It is advised to enclose this complete try/catch/end block in a dedicated + * function declared extern or __attribute__ ((noinline)). + * + * Example: + * + * void my_native_function(JNIEnv* env, jobject object, jint *retcode) { + * COFFEE_TRY() { + * *retcode = call_dangerous_function(env, object); + * } COFFEE_CATCH() { + * const char*const message = coffeecatch_get_message(); + * jclass cls = (*env)->FindClass(env, "java/lang/RuntimeException"); + * (*env)->ThrowNew(env, cls, strdup(message)); + * *retcode = -1; + * } COFFEE_END(); + * } + * + * In addition, the following restrictions MUST be followed: + * - the function must be declared extern, or with the special attribute + * __attribute__ ((noinline)). + * - you must not use local variables before the complete try/catch/end block, + * or define them as "volatile". + * - your function should not ignore the crash silently, as the library will + * ensure the process is killed after a grace period (typically 30s) to + * prevent any deadlock that may occur if the crash was caught inside a + * non-signal-safe function, for example (such as malloc()). + * +COFFEE_TRY() + **/ + +/** + * Declare the signal handler block. This block will be executed if a signal + * was received, and recognized, in the previous COFFEE_TRY() {} section. + * You may call audit functions in this block, such as coffeecatch_get_signal() + * or coffeecatch_get_message(). + * +COFFEE_CATCH() + **/ + +/** + * Declare the end of the COFFEE_TRY()/COFFEE_CATCH() section. + * Diagnostic functions must not be called beyond this point. + * +COFFEE_END() + **/ + +/** + * Get the signal associated with the crash. + * This function can only be called inside a COFFEE_CATCH() block. + */ +extern int coffeecatch_get_signal(void); + +/** + * Get the full error message associated with the crash. + * This function can only be called inside a COFFEE_CATCH() block, and the + * returned pointer is only valid within this block. (you may want to copy + * the string in a static buffer, or use strdup()) + */ +const char* coffeecatch_get_message(void); + +/** + * Raise an abort() signal in the current thread. If the current code section + * is protected, the 'exp', 'file' and 'line' information are stored for + * further audit. + */ +extern void coffeecatch_abort(const char* exp, const char* file, int line); + +/** + * Assertion check. If the expression is false, an abort() signal is raised + * using coffeecatch_abort(). + */ +#define coffeecatch_assert(EXP) (void)( (EXP) || (coffeecatch_abort(#EXP, __FILE__, __LINE__), 0) ) + +/** + * Get the backtrace size, or 0 upon error. + * This function can only be called inside a COFFEE_CATCH() block. + */ +extern size_t coffeecatch_get_backtrace_size(void); + +/** + * Get the backtrace pointer, or 0 upon error. + * This function can only be called inside a COFFEE_CATCH() block. + */ +extern uintptr_t coffeecatch_get_backtrace(ssize_t index); + +/** + * Enumerate the backtrace with information. + * This function can only be called inside a COFFEE_CATCH() block. + */ +extern void coffeecatch_get_backtrace_info(void (*fun)(void *arg, + const char *module, + uintptr_t addr, + const char *function, + uintptr_t offset), void *arg); + +/** + * Cancel any pending alarm() triggered after a signal was caught. + * Calling this function is dangerous, because it exposes the process to + * a possible deadlock if the signal was caught due to internal low-level + * library error (mutex being in a locked state, for example). + */ +extern int coffeecatch_cancel_pending_alarm(void); + +/** Internal functions & definitions, not to be used directly. **/ +#include +extern int coffeecatch_setup(void); +extern sigjmp_buf* coffeecatch_get_ctx(void); +extern void coffeecatch_cleanup(void); +#define COFFEE_TRY() \ + if (coffeecatch_setup() == 0 \ + && sigsetjmp(*coffeecatch_get_ctx(), 1) == 0) +#define COFFEE_CATCH() else +#define COFFEE_END() coffeecatch_cleanup() +/** End of internal functions & definitions. **/ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/include/coffeecatch/coffeejni.h b/include/coffeecatch/coffeejni.h new file mode 100644 index 000000000..3d7fe1b28 --- /dev/null +++ b/include/coffeecatch/coffeejni.h @@ -0,0 +1,133 @@ +/* CoffeeCatch, a tiny native signal handler/catcher for JNI code. + * (especially for Android/Dalvik) + * + * Copyright (c) 2013, Xavier Roche (http://www.httrack.com/) + * All rights reserved. + * See the "License" section below for the licensing terms. + * + * Description: + * + * Allows to "gracefully" recover from a signal (segv, sibus...) as if it was + * a Java exception. It will not gracefully recover from allocator/mutexes + * corruption etc., however, but at least "most" gentle crashes (null pointer + * dereferencing, integer division, stack overflow etc.) should be handled + * without too much troubles. + * + * The handler is thread-safe, but client must have exclusive control on the + * signal handlers (ie. the library is installing its own signal handlers on + * top of the existing ones). + * + * You must build all your libraries with `-funwind-tables', to get proper + * unwinding information on all binaries. On ARM, you may also use the + * `--no-merge-exidx-entries` linker switch, to solve certain issues with + * unwinding (the switch is possibly not needed anymore). + * On Android, this can be achieved by using this line in the Android.mk file + * in each library block: + * LOCAL_CFLAGS := -funwind-tables -Wl,--no-merge-exidx-entries + * + * Example: + * COFFEE_TRY_JNI(env, *retcode = call_dangerous_function(env, object)); + * + * Implementation notes: + * + * Currently the library is installing both alternate stack and signal + * handlers for known signals (SIGABRT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, + * SIGSEGV, SIGSTKFLT), and is using sigsetjmp()/siglongjmp() to return to + * "userland" (compared to signal handler context). As a security, an alarm + * is started as soon as a fatal signal is detected (ie. not something the + * JVM will handle) to kill the process after a grace period. Be sure your + * program will exit quickly after the error is caught, or call alarm(0) + * to cancel the pending time-bomb. + * The signal handlers had to be written with caution, because the virtual + * machine might be using signals (including SEGV) to handle JIT compiler, + * and some clever optimizations (such as NullPointerException handling) + * We are using several signal-unsafe functions, namely: + * - siglongjmp() to return to userland + * - pthread_getspecific() to get thread-specific setup + * + * License: + * + * Copyright (c) 2013, Xavier Roche (http://www.httrack.com/) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef COFFEECATCH_JNI_H +#define COFFEECATCH_JNI_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Setup crash handler to enter in a protected section. If a recognized signal + * is received in this section, an appropriate native Java Error will be + * raised. + * + * You can not exit the protected section block CODE_TO_BE_EXECUTED, using + * statements such as "return", because the cleanup code would not be + * executed. + * + * It is advised to enclose the complete CODE_TO_BE_EXECUTED block in a + * dedicated function declared extern or __attribute__ ((noinline)). + * + * You must build all your libraries with `-funwind-tables', to get proper + * unwinding information on all binaries. On Android, this can be achieved + * by using this line in the Android.mk file in each library block: + * LOCAL_CFLAGS := -funwind-tables + * + * Example: + * + * void my_native_function(JNIEnv* env, jobject object, jint *retcode) { + * COFFEE_TRY_JNI(env, *retcode = call_dangerous_function(env, object)); + * } + * + * In addition, the following restrictions MUST be followed: + * - the function must be declared extern, or with the special attribute + * __attribute__ ((noinline)). + * - you must not use local variables before the COFFEE_TRY_JNI block, + * or define them as "volatile". + * +COFFEE_TRY_JNI(JNIEnv* env, CODE_TO_BE_EXECUTED) + */ + +/** Internal functions & definitions, not to be used directly. **/ +extern void coffeecatch_throw_exception(JNIEnv* env); +#define COFFEE_TRY_JNI(ENV, CODE) \ + do { \ + COFFEE_TRY() { \ + CODE; \ + } COFFEE_CATCH() { \ + coffeecatch_throw_exception(ENV); \ + } COFFEE_END(); \ + } while(0) +/** End of internal functions & definitions. **/ + +#ifdef __cplusplus +} +#endif + +#endif From b216b04a5908ebb6a6de7d29383ae7ba21954718 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 16 Nov 2014 19:41:54 +1100 Subject: [PATCH 147/415] Fix typo --- android/cpp/jni.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index 17230817a..06d9d4f8e 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -205,7 +205,7 @@ void JNICALL nativeCreateSurface(JNIEnv* env, jobject obj, jlong native_map_view assert(native_map_view_ptr != 0); NativeMapView* native_map_view = reinterpret_cast(native_map_view_ptr); bool ret = false; - COFFEE_TRY_JNI(env, native_map_view->createSurface(ANativeWindow_fromSurface(env, surface))); + COFFEE_TRY_JNI(env, ret = native_map_view->createSurface(ANativeWindow_fromSurface(env, surface))); if (!ret) { throw_error(env, "Unable to create GL surface."); } From 2aed807d4cb4d394f072a9d7ec0eac8aa719227d Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 16 Nov 2014 20:04:36 +1100 Subject: [PATCH 148/415] Add coffeecatch to Map::run() --- include/mbgl/platform/event.hpp | 4 +++- src/map/map.cpp | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/mbgl/platform/event.hpp b/include/mbgl/platform/event.hpp index 77c45fb0f..cf8210181 100644 --- a/include/mbgl/platform/event.hpp +++ b/include/mbgl/platform/event.hpp @@ -35,7 +35,8 @@ enum class Event : uint8_t { Image, OpenGL, JNI, - Android + Android, + Crash }; MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { @@ -52,6 +53,7 @@ MBGL_DEFINE_ENUM_CLASS(EventClass, Event, { { Event::OpenGL, "OpenGL" }, { Event::JNI, "JNI" }, { Event::Android, "Android" }, + { Event::Android, "Crash" }, { Event(-1), "Unknown" }, }); diff --git a/src/map/map.cpp b/src/map/map.cpp index f11fa9194..3746fb4b6 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -31,6 +31,10 @@ #include +#ifdef __ANDROID__ + #include +#endif + // Check libuv library version. const static bool uv_version_check = []() { const unsigned int version = uv_version(); @@ -251,6 +255,10 @@ void Map::resume() { } void Map::run() { +#ifdef __ANDROID__ + COFFEE_TRY() { +#endif + #ifndef NDEBUG if (!async) { map_thread = main_thread; @@ -280,6 +288,12 @@ void Map::run() { map_thread = -1; #endif } +#ifdef __ANDROID__ + } COFFEE_CATCH() { + Log::Error(Event::Crash, "Map::run() crash:\n%s", coffeecatch_get_message()); + abort(); + } +#endif } void Map::check_for_pause() { From d957d65bef158c056f80163f5609be216fb134a8 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Sun, 16 Nov 2014 23:35:15 +1100 Subject: [PATCH 149/415] Enable PNG gamma --- platform/default/image.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/default/image.cpp b/platform/default/image.cpp index 69662bc90..eb4b3e8f5 100644 --- a/platform/default/image.cpp +++ b/platform/default/image.cpp @@ -149,11 +149,11 @@ Image::Image(const std::string &data) { } // FIXME work around gamma crash - /*double gamma; + double gamma; if (png_get_gAMA(png, info, &gamma)) png_set_gamma(png, 2.2, gamma); - png_set_alpha_mode(png, PNG_ALPHA_PREMULTIPLIED, 2.2);*/ + png_set_alpha_mode(png, PNG_ALPHA_PREMULTIPLIED, 2.2); png_read_update_info(png, info); From c092a686a7d898fef5d1436d49d36b4129af11ee Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Mon, 17 Nov 2014 19:23:36 +1100 Subject: [PATCH 150/415] Add libzip --- configure | 7 +++++++ gyp/mbgl-android.gypi | 2 ++ 2 files changed, 9 insertions(+) diff --git a/configure b/configure index 10f8508bf..829bae7f9 100755 --- a/configure +++ b/configure @@ -33,6 +33,7 @@ case ${MASON_PLATFORM} in ZLIB_VERSION=system BOOST_VERSION=1.57.0 NUNICODE_VERSION=1.4 + LIBZIP_VERSION=0.11.2 ;; *) GLFW_VERSION=a21f2377 @@ -136,6 +137,12 @@ if [ ! -z ${NUNICODE_VERSION} ]; then CONFIG+=" 'nu_ldflags': $(quote_flags $(mason ldflags nunicode ${NUNICODE_VERSION})),"$LN fi +if [ ! -z ${LIBZIP_VERSION} ]; then + mason install libzip ${LIBZIP_VERSION} + CONFIG+=" 'zip_static_libs': $(quote_flags $(mason static_libs libzip ${LIBZIP_VERSION})),"$LN + CONFIG+=" 'zip_cflags': $(quote_flags $(mason cflags libzip ${LIBZIP_VERSION})),"$LN + CONFIG+=" 'zip_ldflags': $(quote_flags $(mason ldflags libzip ${LIBZIP_VERSION})),"$LN +fi CONFIG+=" } } diff --git a/gyp/mbgl-android.gypi b/gyp/mbgl-android.gypi index 4601ead47..473e85fe7 100644 --- a/gyp/mbgl-android.gypi +++ b/gyp/mbgl-android.gypi @@ -11,6 +11,7 @@ '<@(uv_cflags)', '<@(curl_cflags)', '<@(nu_cflags)', + '<@(zip_cflags)', '-I<(boost_root)/include', ], 'cflags': [ @@ -22,6 +23,7 @@ '<@(uv_ldflags)', '<@(curl_ldflags)', '<@(nu_ldflags)', + '<@(zip_ldflags)', ], }, 'sources': [ From aee724d6cfed9a8703183c998d7fb7d077abe8b1 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Mon, 17 Nov 2014 20:00:44 +1100 Subject: [PATCH 151/415] Create AssetRequest --- gyp/mbgl-android.gypi | 2 + .../mbgl/platform/android/asset_request.hpp | 27 +++ .../platform/android/asset_request_baton.hpp | 35 ++++ platform/android/asset_request.cpp | 37 ++++ platform/android/asset_request_baton.cpp | 159 ++++++++++++++++++ src/storage/file_source.cpp | 10 +- 6 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 include/mbgl/platform/android/asset_request.hpp create mode 100644 include/mbgl/platform/android/asset_request_baton.hpp create mode 100644 platform/android/asset_request.cpp create mode 100644 platform/android/asset_request_baton.cpp diff --git a/gyp/mbgl-android.gypi b/gyp/mbgl-android.gypi index 473e85fe7..7b9c6faa0 100644 --- a/gyp/mbgl-android.gypi +++ b/gyp/mbgl-android.gypi @@ -29,6 +29,8 @@ 'sources': [ '../platform/android/cache_database_data.cpp', '../platform/android/log_android.cpp', + '../platform/android/asset_request.cpp', + '../platform/android/asset_request_baton.cpp', '../platform/default/string_stdlib.cpp', '../platform/default/http_request_baton_curl.cpp', '../platform/default/image.cpp', diff --git a/include/mbgl/platform/android/asset_request.hpp b/include/mbgl/platform/android/asset_request.hpp new file mode 100644 index 000000000..3114d41ad --- /dev/null +++ b/include/mbgl/platform/android/asset_request.hpp @@ -0,0 +1,27 @@ +#ifndef MBGL_STORAGE_ASSET_REQUEST +#define MBGL_STORAGE_ASSET_REQUEST + +#include + +namespace mbgl { + +typedef struct uv_loop_s uv_loop_t; + +struct AssetRequestBaton; + +class AssetRequest : public BaseRequest { +public: + AssetRequest(const std::string &path, uv_loop_t *loop); + ~AssetRequest(); + + void cancel(); + +private: + AssetRequestBaton *ptr = nullptr; + + friend struct AssetRequestBaton; +}; + +} + +#endif diff --git a/include/mbgl/platform/android/asset_request_baton.hpp b/include/mbgl/platform/android/asset_request_baton.hpp new file mode 100644 index 000000000..d8a0fbf3e --- /dev/null +++ b/include/mbgl/platform/android/asset_request_baton.hpp @@ -0,0 +1,35 @@ +#ifndef MBGL_STORAGE_ASSET_REQUEST_BATON +#define MBGL_STORAGE_ASSET_REQUEST_BATON + +#include + +#include + +namespace mbgl { + +struct AssetRequestBaton { + AssetRequestBaton(AssetRequest *request_, const std::string &path, uv_loop_t *loop); + ~AssetRequestBaton(); + + void cancel(); + static void file_opened(uv_fs_t *req); + static void file_stated(uv_fs_t *req); + static void file_read(uv_fs_t *req); + static void file_closed(uv_fs_t *req); + static void notify_error(uv_fs_t *req); + static void cleanup(uv_fs_t *req); + + const unsigned long thread_id; + AssetRequest *request = nullptr; + uv_fs_t req; + uv_file fd = -1; + bool canceled = false; + std::string body; + uv_buf_t buffer; +}; + + +} + + +#endif diff --git a/platform/android/asset_request.cpp b/platform/android/asset_request.cpp new file mode 100644 index 000000000..4148916d2 --- /dev/null +++ b/platform/android/asset_request.cpp @@ -0,0 +1,37 @@ +#include +#include +#include + +#include + +#include + +#include + +namespace mbgl { + +AssetRequest::AssetRequest(const std::string &path_, uv_loop_t *loop) + : BaseRequest(path_), ptr(new AssetRequestBaton(this, path, loop)) { +} + +void AssetRequest::cancel() { + assert(thread_id == uv_thread_self()); + + if (ptr) { + ptr->cancel(); + + // When deleting a AssetRequest object with a uv_fs_* call is in progress, we are making sure + // that the callback doesn't accidentally reference this object again. + ptr->request = nullptr; + ptr = nullptr; + } + + notify(); +} + +AssetRequest::~AssetRequest() { + assert(thread_id == uv_thread_self()); + cancel(); +} + +} diff --git a/platform/android/asset_request_baton.cpp b/platform/android/asset_request_baton.cpp new file mode 100644 index 000000000..8563312b4 --- /dev/null +++ b/platform/android/asset_request_baton.cpp @@ -0,0 +1,159 @@ +#include +#include +#include + +#include + +namespace mbgl { + +AssetRequestBaton::AssetRequestBaton(AssetRequest *request_, const std::string &path, uv_loop_t *loop) + : thread_id(uv_thread_self()), request(request_) { + req.data = this; + uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, file_opened); +} + +AssetRequestBaton::~AssetRequestBaton() { +} + +void AssetRequestBaton::cancel() { + canceled = true; + + // uv_cancel fails frequently when the request has already been started. + // In that case, we have to let it complete and check the canceled bool + // instead. + uv_cancel((uv_req_t *)&req); +} + +void AssetRequestBaton::notify_error(uv_fs_t *req) { + AssetRequestBaton *ptr = (AssetRequestBaton *)req->data; + assert(ptr->thread_id == uv_thread_self()); + + if (ptr->request && req->result < 0 && !ptr->canceled && req->result != UV_ECANCELED) { + ptr->request->response = std::unique_ptr(new Response); + ptr->request->response->code = req->result == UV_ENOENT ? 404 : 500; +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 + ptr->request->response->message = uv_strerror(uv_last_error(req->loop)); +#else + ptr->request->response->message = uv_strerror(int(req->result)); +#endif + ptr->request->notify(); + } +} + +void AssetRequestBaton::file_opened(uv_fs_t *req) { + AssetRequestBaton *ptr = (AssetRequestBaton *)req->data; + assert(ptr->thread_id == uv_thread_self()); + + if (req->result < 0) { + // Opening failed or was canceled. There isn't much left we can do. + notify_error(req); + cleanup(req); + } else { + const uv_file fd = uv_file(req->result); + + // We're going to reuse this handle, so we need to cleanup first. + uv_fs_req_cleanup(req); + + if (ptr->canceled || !ptr->request) { + // Either the AssetRequest object has been destructed, or the + // request was canceled. + uv_fs_close(req->loop, req, fd, file_closed); + } else { + ptr->fd = fd; + uv_fs_fstat(req->loop, req, fd, file_stated); + } + } +} + +void AssetRequestBaton::file_stated(uv_fs_t *req) { + AssetRequestBaton *ptr = (AssetRequestBaton *)req->data; + assert(ptr->thread_id == uv_thread_self()); + + if (req->result != 0 || ptr->canceled || !ptr->request) { + // Stating failed or was canceled. We already have an open file handle + // though, which we'll have to close. + notify_error(req); + + uv_fs_req_cleanup(req); + uv_fs_close(req->loop, req, ptr->fd, file_closed); + } else { +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 + const uv_statbuf_t *stat = static_cast(req->ptr); +#else + const uv_stat_t *stat = static_cast(req->ptr); +#endif + if (stat->st_size > std::numeric_limits::max()) { + // File is too large for us to open this way because uv_buf's only support unsigned + // ints as maximum size. + if (ptr->request) { + ptr->request->response = std::unique_ptr(new Response); + ptr->request->response->code = UV_EFBIG; +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 + ptr->request->response->message = uv_strerror(uv_err_t {UV_EFBIG, 0}); +#else + ptr->request->response->message = uv_strerror(UV_EFBIG); +#endif + ptr->request->notify(); + } + + uv_fs_req_cleanup(req); + uv_fs_close(req->loop, req, ptr->fd, file_closed); + } else { + const unsigned int size = (unsigned int)(stat->st_size); + ptr->body.resize(size); + ptr->buffer = uv_buf_init(const_cast(ptr->body.data()), size); + uv_fs_req_cleanup(req); +#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 + uv_fs_read(req->loop, req, ptr->fd, ptr->buffer.base, ptr->buffer.len, -1, file_read); +#else + uv_fs_read(req->loop, req, ptr->fd, &ptr->buffer, 1, 0, file_read); +#endif + } + } +} + +void AssetRequestBaton::file_read(uv_fs_t *req) { + AssetRequestBaton *ptr = (AssetRequestBaton *)req->data; + assert(ptr->thread_id == uv_thread_self()); + + if (req->result < 0 || ptr->canceled || !ptr->request) { + // Stating failed or was canceled. We already have an open file handle + // though, which we'll have to close. + notify_error(req); + } else { + // File was successfully read. + if (ptr->request) { + ptr->request->response = std::unique_ptr(new Response); + ptr->request->response->code = 200; + ptr->request->response->data = std::move(ptr->body); + ptr->request->notify(); + } + } + + uv_fs_req_cleanup(req); + uv_fs_close(req->loop, req, ptr->fd, file_closed); +} + +void AssetRequestBaton::file_closed(uv_fs_t *req) { + assert(((AssetRequestBaton *)req->data)->thread_id == uv_thread_self()); + + if (req->result < 0) { + // Closing the file failed. But there isn't anything we can do. + } + + cleanup(req); +} + +void AssetRequestBaton::cleanup(uv_fs_t *req) { + AssetRequestBaton *ptr = (AssetRequestBaton *)req->data; + assert(ptr->thread_id == uv_thread_self()); + + if (ptr->request) { + ptr->request->ptr = nullptr; + } + + uv_fs_req_cleanup(req); + delete ptr; +} + +} diff --git a/src/storage/file_source.cpp b/src/storage/file_source.cpp index a67f57dac..fc71b8ea5 100644 --- a/src/storage/file_source.cpp +++ b/src/storage/file_source.cpp @@ -4,6 +4,10 @@ #include #include +#ifdef __ANDROID__ + #include +#endif + #include namespace mbgl { @@ -72,6 +76,10 @@ std::unique_ptr FileSource::request(ResourceType type, const std::strin if (!req) { if (absoluteURL.substr(0, 7) == "file://") { req = std::make_shared(absoluteURL.substr(7), loop); +#ifdef __ANDROID__ + } else if (absoluteURL.substr(0, 7) == "asset://") { + req = std::make_shared(absoluteURL.substr(7), loop); +#endif } else { req = std::make_shared(type, absoluteURL, loop, store); } @@ -102,4 +110,4 @@ void FileSource::retryAllPending() { } -} \ No newline at end of file +} From f0e4e27cd383f9c8b674cbfebb7a15ba8f8b6c0b Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Mon, 17 Nov 2014 23:17:22 +1100 Subject: [PATCH 152/415] Read styles from APK --- android/cpp/jni.cpp | 6 +- .../arieslabs/assetbridge/Assetbridge.java | 2 +- .../java/com/mapbox/mapboxgl/lib/MapView.java | 5 +- .../mapbox/mapboxgl/lib/NativeMapView.java | 6 +- android/mapboxgl-app.gyp | 2 + include/mbgl/android/jni.hpp | 1 + .../platform/android/asset_request_baton.hpp | 18 +- platform/android/asset_request_baton.cpp | 212 +++++++++--------- src/storage/file_request_baton.cpp | 2 +- src/storage/file_source.cpp | 4 +- 10 files changed, 130 insertions(+), 128 deletions(-) diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index 06d9d4f8e..ec9283b87 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -27,6 +27,7 @@ namespace android { std::string cache_path; std::string data_path; +std::string apk_path; jmethodID on_map_changed_id = nullptr; @@ -142,10 +143,11 @@ namespace { using namespace mbgl::android; -jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_, jstring data_path_) { +jlong JNICALL nativeCreate(JNIEnv* env, jobject obj, jstring cache_path_, jstring data_path_, jstring apk_path_) { mbgl::Log::Debug(mbgl::Event::JNI, "nativeCreate"); cache_path = std_string_from_jstring(env, cache_path_); data_path = std_string_from_jstring(env, data_path_); + apk_path = std_string_from_jstring(env, apk_path_); NativeMapView* native_map_view = nullptr; COFFEE_TRY_JNI(env, native_map_view = new NativeMapView(env, obj)); if (native_map_view == nullptr) { @@ -787,7 +789,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { // NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the array correctly (too large) std::array methods = {{ // Can remove the extra brace in C++14 - { "nativeCreate", "(Ljava/lang/String;Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, + { "nativeCreate", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J", reinterpret_cast(&nativeCreate) }, { "nativeDestroy", "(J)V", reinterpret_cast(&nativeDestroy) }, { "nativeInitializeDisplay", "(J)V", reinterpret_cast(&nativeInitializeDisplay) }, { "nativeTerminateDisplay", "(J)V", reinterpret_cast(&nativeTerminateDisplay) }, diff --git a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java index e32475931..357f053f6 100644 --- a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java +++ b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java @@ -33,7 +33,7 @@ public class Assetbridge { // only extract what we need ArrayList assets = new ArrayList(); assets.add("ca-bundle.crt"); - assets.add("styles"); + //assets.add("styles"); // iterate on the files... for(String asset : assets) { diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 4ae6d991a..48fb8840d 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -141,13 +141,14 @@ public class MapView extends SurfaceView { // Get the cache path String cachePath = context.getCacheDir().getAbsolutePath(); String dataPath = context.getFilesDir().getAbsolutePath(); + String apkPath = context.getPackageCodePath(); // Extract the asset files Assetbridge.unpack(context); // Load the map style and API key //mStyleUrl = "https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"; - mStyleUrl = "file://" + dataPath + "/styles/styles/bright-v6.json"; + mStyleUrl = "asset://styles/styles/bright-v6.json"; mAccessToken = "pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"; // Are we a debug build? @@ -155,7 +156,7 @@ public class MapView extends SurfaceView { ApplicationInfo.FLAG_DEBUGGABLE) == ApplicationInfo.FLAG_DEBUGGABLE; // Create the NativeMapView - mNativeMapView = new NativeMapView(this, cachePath, dataPath); + mNativeMapView = new NativeMapView(this, cachePath, dataPath, apkPath); mNativeMapView.setStyleURL(mStyleUrl); mNativeMapView.setAccessToken(mAccessToken); mNativeMapView.setDebug(isDebuggable); diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index e0e005f66..fc525a594 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -37,11 +37,11 @@ class NativeMapView { // Constructors // - public NativeMapView(MapView mapView, String cachePath, String dataPath) { + public NativeMapView(MapView mapView, String cachePath, String dataPath, String apkPath) { mMapView = mapView; // Create the NativeMapView - mNativeMapViewPtr = nativeCreate(cachePath, dataPath); + mNativeMapViewPtr = nativeCreate(cachePath, dataPath, apkPath); } // @@ -407,7 +407,7 @@ class NativeMapView { super.finalize(); } - private native long nativeCreate(String cachePath, String dataPath); + private native long nativeCreate(String cachePath, String dataPath, String apkPath); private native void nativeDestroy(long nativeMapViewPtr); diff --git a/android/mapboxgl-app.gyp b/android/mapboxgl-app.gyp index b01050f0b..1140dd5cb 100644 --- a/android/mapboxgl-app.gyp +++ b/android/mapboxgl-app.gyp @@ -23,6 +23,7 @@ '<@(sqlite3_static_libs)', '<@(uv_static_libs)', '<@(nu_static_libs)', + '<@(zip_static_libs)', ], 'variables': { 'ldflags': [ @@ -37,6 +38,7 @@ '<@(openssl_ldflags)', '<@(curl_ldflags)', '<@(zlib_ldflags)', + '<@(zip_ldflags)', ], }, 'conditions': [ diff --git a/include/mbgl/android/jni.hpp b/include/mbgl/android/jni.hpp index c1af07c64..28b9b8917 100644 --- a/include/mbgl/android/jni.hpp +++ b/include/mbgl/android/jni.hpp @@ -9,6 +9,7 @@ namespace android { extern std::string cache_path; extern std::string data_path; +extern std::string apk_path; extern jmethodID on_map_changed_id; diff --git a/include/mbgl/platform/android/asset_request_baton.hpp b/include/mbgl/platform/android/asset_request_baton.hpp index d8a0fbf3e..1acbfbae0 100644 --- a/include/mbgl/platform/android/asset_request_baton.hpp +++ b/include/mbgl/platform/android/asset_request_baton.hpp @@ -4,6 +4,7 @@ #include #include +#include namespace mbgl { @@ -12,20 +13,17 @@ struct AssetRequestBaton { ~AssetRequestBaton(); void cancel(); - static void file_opened(uv_fs_t *req); - static void file_stated(uv_fs_t *req); - static void file_read(uv_fs_t *req); - static void file_closed(uv_fs_t *req); - static void notify_error(uv_fs_t *req); - static void cleanup(uv_fs_t *req); + static void run(uv_async_t *async); + static void notify_error(uv_async_t *async, const int code, const char *message); + static void cleanup(uv_async_t *async); const unsigned long thread_id; AssetRequest *request = nullptr; - uv_fs_t req; - uv_file fd = -1; + std::unique_ptr async_run; + struct zip *apk = nullptr; + struct zip_file *apk_file = nullptr; + std::string path; bool canceled = false; - std::string body; - uv_buf_t buffer; }; diff --git a/platform/android/asset_request_baton.cpp b/platform/android/asset_request_baton.cpp index 8563312b4..c4e0a1799 100644 --- a/platform/android/asset_request_baton.cpp +++ b/platform/android/asset_request_baton.cpp @@ -1,158 +1,156 @@ +#include #include #include #include #include +#include namespace mbgl { -AssetRequestBaton::AssetRequestBaton(AssetRequest *request_, const std::string &path, uv_loop_t *loop) - : thread_id(uv_thread_self()), request(request_) { - req.data = this; - uv_fs_open(loop, &req, path.c_str(), O_RDONLY, S_IRUSR, file_opened); +AssetRequestBaton::AssetRequestBaton(AssetRequest *request_, const std::string &path_, uv_loop_t *loop) + : thread_id(uv_thread_self()), + request(request_), + async_run(new uv_async_t()), + path(path_) { + + uv_async_init(loop, async_run.get(), run); + async_run->data = this; + + uv_async_send(async_run.get()); } AssetRequestBaton::~AssetRequestBaton() { + uv_close((uv_handle_t *)async_run.get(), nullptr); } void AssetRequestBaton::cancel() { canceled = true; - - // uv_cancel fails frequently when the request has already been started. - // In that case, we have to let it complete and check the canceled bool - // instead. - uv_cancel((uv_req_t *)&req); } -void AssetRequestBaton::notify_error(uv_fs_t *req) { - AssetRequestBaton *ptr = (AssetRequestBaton *)req->data; +void AssetRequestBaton::notify_error(uv_async_t *async, const int code, const char *message) { + AssetRequestBaton *ptr = (AssetRequestBaton *)async->data; assert(ptr->thread_id == uv_thread_self()); - if (ptr->request && req->result < 0 && !ptr->canceled && req->result != UV_ECANCELED) { + if (ptr->request && !ptr->canceled) { ptr->request->response = std::unique_ptr(new Response); - ptr->request->response->code = req->result == UV_ENOENT ? 404 : 500; -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - ptr->request->response->message = uv_strerror(uv_last_error(req->loop)); -#else - ptr->request->response->message = uv_strerror(int(req->result)); -#endif + ptr->request->response->code = code; + ptr->request->response->message = message; ptr->request->notify(); } } -void AssetRequestBaton::file_opened(uv_fs_t *req) { - AssetRequestBaton *ptr = (AssetRequestBaton *)req->data; +void AssetRequestBaton::run(uv_async_t *async) { + AssetRequestBaton *ptr = (AssetRequestBaton *)async->data; assert(ptr->thread_id == uv_thread_self()); - if (req->result < 0) { - // Opening failed or was canceled. There isn't much left we can do. - notify_error(req); - cleanup(req); - } else { - const uv_file fd = uv_file(req->result); - - // We're going to reuse this handle, so we need to cleanup first. - uv_fs_req_cleanup(req); - - if (ptr->canceled || !ptr->request) { - // Either the AssetRequest object has been destructed, or the - // request was canceled. - uv_fs_close(req->loop, req, fd, file_closed); - } else { - ptr->fd = fd; - uv_fs_fstat(req->loop, req, fd, file_stated); - } + if (ptr->canceled || !ptr->request) { + // Either the AssetRequest object has been destructed, or the + // request was canceled. + cleanup(async); + return; } -} -void AssetRequestBaton::file_stated(uv_fs_t *req) { - AssetRequestBaton *ptr = (AssetRequestBaton *)req->data; - assert(ptr->thread_id == uv_thread_self()); + int error = 0; + ptr->apk = zip_open(mbgl::android::apk_path.c_str(), 0, &error); + if (ptr->apk == nullptr || ptr->canceled || !ptr->request) { + // Opening the APK failed or was canceled. There isn't much left we can do. + const int message_size = zip_error_to_str(nullptr, 0, error, errno); + const std::unique_ptr message = boost::make_unique(message_size); + zip_error_to_str(message.get(), 0, error, errno); + notify_error(async, 500, message.get()); + cleanup(async); + return; + } - if (req->result != 0 || ptr->canceled || !ptr->request) { + std::string apk_file_path = "assets/" + ptr->path; + ptr->apk_file = zip_fopen(ptr->apk, apk_file_path.c_str(), ZIP_FL_NOCASE); + if (ptr->apk_file == nullptr || ptr->canceled || !ptr->request) { + // Opening the asset failed or was canceled. We already have an open file handle + // though, which we'll have to close. + zip_error_get(ptr->apk, &error, nullptr); + notify_error(async, error == ZIP_ER_NOENT ? 404 : 500, zip_strerror(ptr->apk)); + zip_close(ptr->apk); + ptr->apk = nullptr; + cleanup(async); + return; + } + + struct zip_stat stat; + if (zip_stat(ptr->apk, apk_file_path.c_str(), ZIP_FL_NOCASE, &stat) != 0 || + ptr->canceled || !ptr->request) { // Stating failed or was canceled. We already have an open file handle // though, which we'll have to close. - notify_error(req); - - uv_fs_req_cleanup(req); - uv_fs_close(req->loop, req, ptr->fd, file_closed); - } else { -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - const uv_statbuf_t *stat = static_cast(req->ptr); -#else - const uv_stat_t *stat = static_cast(req->ptr); -#endif - if (stat->st_size > std::numeric_limits::max()) { - // File is too large for us to open this way because uv_buf's only support unsigned - // ints as maximum size. - if (ptr->request) { - ptr->request->response = std::unique_ptr(new Response); - ptr->request->response->code = UV_EFBIG; -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - ptr->request->response->message = uv_strerror(uv_err_t {UV_EFBIG, 0}); -#else - ptr->request->response->message = uv_strerror(UV_EFBIG); -#endif - ptr->request->notify(); - } - - uv_fs_req_cleanup(req); - uv_fs_close(req->loop, req, ptr->fd, file_closed); - } else { - const unsigned int size = (unsigned int)(stat->st_size); - ptr->body.resize(size); - ptr->buffer = uv_buf_init(const_cast(ptr->body.data()), size); - uv_fs_req_cleanup(req); -#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10 - uv_fs_read(req->loop, req, ptr->fd, ptr->buffer.base, ptr->buffer.len, -1, file_read); -#else - uv_fs_read(req->loop, req, ptr->fd, &ptr->buffer, 1, 0, file_read); -#endif - } + notify_error(async, 500, zip_strerror(ptr->apk)); + zip_fclose(ptr->apk_file); + ptr->apk_file = nullptr; + zip_close(ptr->apk); + ptr->apk = nullptr; + cleanup(async); + return; } -} - -void AssetRequestBaton::file_read(uv_fs_t *req) { - AssetRequestBaton *ptr = (AssetRequestBaton *)req->data; - assert(ptr->thread_id == uv_thread_self()); - - if (req->result < 0 || ptr->canceled || !ptr->request) { - // Stating failed or was canceled. We already have an open file handle - // though, which we'll have to close. - notify_error(req); - } else { - // File was successfully read. +/* + if (stat.size > std::numeric_limits::max()) { + // File is too large for us to open this way because uv_buf's only support unsigned + // ints as maximum size. if (ptr->request) { ptr->request->response = std::unique_ptr(new Response); - ptr->request->response->code = 200; - ptr->request->response->data = std::move(ptr->body); + ptr->request->response->code = UV_EFBIG; + ptr->request->response->message = uv_strerror(UV_EFBIG); ptr->request->notify(); } + zip_fclose(ptr->apk_file); + ptr->apk_file = nullptr; + zip_close(ptr->apk); + ptr->apk = nullptr; + cleanup(async); + return; + } +*/ + //const unsigned int size = static_cast(stat.size); + const std::unique_ptr body = boost::make_unique(stat.size); + + if (zip_fread(ptr->apk_file, reinterpret_cast(body.get()), stat.size) == -1 || + ptr->canceled || !ptr->request) { + // Reading failed or was canceled. We already have an open file handle + // though, which we'll have to close. + notify_error(async, 500, zip_file_strerror(ptr->apk_file)); + zip_fclose(ptr->apk_file); + ptr->apk_file = nullptr; + zip_close(ptr->apk); + ptr->apk = nullptr; + cleanup(async); + return; } - uv_fs_req_cleanup(req); - uv_fs_close(req->loop, req, ptr->fd, file_closed); -} - -void AssetRequestBaton::file_closed(uv_fs_t *req) { - assert(((AssetRequestBaton *)req->data)->thread_id == uv_thread_self()); - - if (req->result < 0) { - // Closing the file failed. But there isn't anything we can do. + if (ptr->request) { + ptr->request->response = std::unique_ptr(new Response); + ptr->request->response->code = 200; + ptr->request->response->data = body.get(); + ptr->request->notify(); } - cleanup(req); + if (zip_fclose(ptr->apk_file) != 0) { + // Closing the asset failed. But there isn't anything we can do. + } + ptr->apk_file = nullptr; + + if (zip_close(ptr->apk) != 0) { + // Closing the APK failed. But there isn't anything we can do. + } + ptr->apk = nullptr; + + cleanup(async); } -void AssetRequestBaton::cleanup(uv_fs_t *req) { - AssetRequestBaton *ptr = (AssetRequestBaton *)req->data; +void AssetRequestBaton::cleanup(uv_async_t *async) { + AssetRequestBaton *ptr = (AssetRequestBaton *)async->data; assert(ptr->thread_id == uv_thread_self()); if (ptr->request) { ptr->request->ptr = nullptr; } - uv_fs_req_cleanup(req); delete ptr; } diff --git a/src/storage/file_request_baton.cpp b/src/storage/file_request_baton.cpp index 1b41a5370..991853b28 100644 --- a/src/storage/file_request_baton.cpp +++ b/src/storage/file_request_baton.cpp @@ -117,7 +117,7 @@ void FileRequestBaton::file_read(uv_fs_t *req) { assert(ptr->thread_id == uv_thread_self()); if (req->result < 0 || ptr->canceled || !ptr->request) { - // Stating failed or was canceled. We already have an open file handle + // Reading failed or was canceled. We already have an open file handle // though, which we'll have to close. notify_error(req); } else { diff --git a/src/storage/file_source.cpp b/src/storage/file_source.cpp index fc71b8ea5..d8c7ee88b 100644 --- a/src/storage/file_source.cpp +++ b/src/storage/file_source.cpp @@ -77,8 +77,8 @@ std::unique_ptr FileSource::request(ResourceType type, const std::strin if (absoluteURL.substr(0, 7) == "file://") { req = std::make_shared(absoluteURL.substr(7), loop); #ifdef __ANDROID__ - } else if (absoluteURL.substr(0, 7) == "asset://") { - req = std::make_shared(absoluteURL.substr(7), loop); + } else if (absoluteURL.substr(0, 8) == "asset://") { + req = std::make_shared(absoluteURL.substr(8), loop); #endif } else { req = std::make_shared(type, absoluteURL, loop, store); From 80be6c4eb01a23fb0e943e1b4f00f48cc51256a2 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 18 Nov 2014 00:33:59 +1100 Subject: [PATCH 153/415] Load CA bundle from APK Remove Assetbridge --- android/LICENSE_ASSETBRIDGE | 23 ---- .../arieslabs/assetbridge/Assetbridge.java | 124 ------------------ .../java/com/mapbox/mapboxgl/lib/MapView.java | 5 - gyp/mbgl-android.gypi | 1 + platform/android/asset_request_baton.cpp | 20 +-- platform/default/http_request_baton_curl.cpp | 113 +++++++++++++++- 6 files changed, 108 insertions(+), 178 deletions(-) delete mode 100644 android/LICENSE_ASSETBRIDGE delete mode 100644 android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java diff --git a/android/LICENSE_ASSETBRIDGE b/android/LICENSE_ASSETBRIDGE deleted file mode 100644 index 09d4125bc..000000000 --- a/android/LICENSE_ASSETBRIDGE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2013 Steve Havelka - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - diff --git a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java b/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java deleted file mode 100644 index 357f053f6..000000000 --- a/android/java/lib/src/main/java/com/arieslabs/assetbridge/Assetbridge.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.arieslabs.assetbridge; - -import android.content.Context; -import android.content.res.AssetManager; -import android.util.Log; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; - -public class Assetbridge { - - private static final String TAG = "Assetbridge"; - - /*static { - System.loadLibrary("assetbridge"); - }*/ - - // unpack - public static void unpack(Context c) { - - try { - // first let's get the data directory - String datadir = c.getFilesDir().getAbsolutePath(); - Log.v(TAG, "Extracting assets to " + datadir); - - // now we need the assetmanager - AssetManager am = c.getAssets(); - - // only extract what we need - ArrayList assets = new ArrayList(); - assets.add("ca-bundle.crt"); - //assets.add("styles"); - - // iterate on the files... - for(String asset : assets) { - delete(datadir + "/" + asset); - copyAssetFolder(am, asset, datadir + "/" + asset); - } - - // last, set the ASSETDIR environment variable for the C - // parts of the procee - //setassetdir(c.getCacheDir().getPath()); - - } catch (IOException e) { - Log.e("Assetbridge", "Can't unpack assets from APK", e); - } - - } - - public static void delete(String dest) { - File destfh = new File(dest); - if (destfh.exists()) { - destfh.delete(); - } - } - - - public static void copyAssetFolder(AssetManager am, String src, String dest) - throws IOException { - Log.v(TAG, "Copying " + src); - - InputStream srcIS = null; - File destfh; - - // this is the only way we can tell if this is a file or a - // folder - we have to open the asset, and if the open fails, - // it's a folder... - boolean isDir = false; - try { - srcIS = am.open(src); - } catch (FileNotFoundException e) { - isDir = true; - } - - Log.v(TAG, src + " was " + (isDir ? "a dir" : "a file")); - - // either way, we'll use the dest as a File - destfh = new File(dest); - - // and now, depending on .. - if(isDir) { - - // If the directory doesn't yet exist, create it - if( !destfh.exists() ) { - if (!destfh.mkdir()) { - throw new IOException("Failed to mkdir"); - } - } - - // list the assets in the directory... - String assets[] = am.list(src); - - // and copy them all using same. - for(String asset : assets) { - copyAssetFolder(am, src + "/" + asset, dest + "/" + asset); - } - - } else { - - int count, buffer_len = 2048; - byte[] data = new byte[buffer_len]; - - // copy the file from the assets subsystem to the filesystem - FileOutputStream destOS = new FileOutputStream(destfh); - - //copy the file content in bytes - while( (count = srcIS.read(data, 0, buffer_len)) != -1) { - destOS.write(data, 0, count); - } - - // and close the two files - srcIS.close(); - destOS.close(); - - } - } - - // the native method to set the environment variable - //public static native void setassetdir(String s); -} diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 48fb8840d..ce6496db1 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -16,7 +16,6 @@ import android.net.NetworkInfo; import android.os.Bundle; import android.support.annotation.NonNull; import android.util.AttributeSet; -import android.util.DisplayMetrics; import android.util.Log; import android.view.GestureDetector; import android.view.InputDevice; @@ -31,7 +30,6 @@ import android.widget.ZoomButtonsController; import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector; -import com.arieslabs.assetbridge.Assetbridge; // Custom view that shows a Map // Based on SurfaceView as we use OpenGL ES to render @@ -143,9 +141,6 @@ public class MapView extends SurfaceView { String dataPath = context.getFilesDir().getAbsolutePath(); String apkPath = context.getPackageCodePath(); - // Extract the asset files - Assetbridge.unpack(context); - // Load the map style and API key //mStyleUrl = "https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"; mStyleUrl = "asset://styles/styles/bright-v6.json"; diff --git a/gyp/mbgl-android.gypi b/gyp/mbgl-android.gypi index 7b9c6faa0..8f35ddea9 100644 --- a/gyp/mbgl-android.gypi +++ b/gyp/mbgl-android.gypi @@ -12,6 +12,7 @@ '<@(curl_cflags)', '<@(nu_cflags)', '<@(zip_cflags)', + '<@(openssl_cflags)', '-I<(boost_root)/include', ], 'cflags': [ diff --git a/platform/android/asset_request_baton.cpp b/platform/android/asset_request_baton.cpp index c4e0a1799..30819e506 100644 --- a/platform/android/asset_request_baton.cpp +++ b/platform/android/asset_request_baton.cpp @@ -89,25 +89,7 @@ void AssetRequestBaton::run(uv_async_t *async) { cleanup(async); return; } -/* - if (stat.size > std::numeric_limits::max()) { - // File is too large for us to open this way because uv_buf's only support unsigned - // ints as maximum size. - if (ptr->request) { - ptr->request->response = std::unique_ptr(new Response); - ptr->request->response->code = UV_EFBIG; - ptr->request->response->message = uv_strerror(UV_EFBIG); - ptr->request->notify(); - } - zip_fclose(ptr->apk_file); - ptr->apk_file = nullptr; - zip_close(ptr->apk); - ptr->apk = nullptr; - cleanup(async); - return; - } -*/ - //const unsigned int size = static_cast(stat.size); + const std::unique_ptr body = boost::make_unique(stat.size); if (zip_fread(ptr->apk_file, reinterpret_cast(body.get()), stat.size) == -1 || diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index fb66215c0..23029bc5e 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -6,6 +6,9 @@ #ifdef __ANDROID__ #include + #include + #include + #include #endif #include @@ -408,6 +411,103 @@ size_t curl_header_cb(char * const buffer, const size_t size, const size_t nmemb return length; } +// This function is called to load the CA bundle +// from http://curl.haxx.se/libcurl/c/cacertinmem.html +#ifdef __ANDROID__ +static CURLcode sslctx_function(CURL */*curl*/, void *sslctx, void */*parm*/) { + + int error = 0; + struct zip *apk = zip_open(mbgl::android::apk_path.c_str(), 0, &error); + if (apk == nullptr) { + return CURLE_SSL_CACERT_BADFILE; + } + + struct zip_file *apk_file = zip_fopen(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE); + if (apk_file == nullptr) { + zip_close(apk); + apk = nullptr; + return CURLE_SSL_CACERT_BADFILE; + } + + struct zip_stat stat; + if (zip_stat(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE, &stat) != 0) { + zip_fclose(apk_file); + apk_file = nullptr; + zip_close(apk); + apk = nullptr; + return CURLE_SSL_CACERT_BADFILE; + } + + if (stat.size > std::numeric_limits::max()) { + zip_fclose(apk_file); + apk_file = nullptr; + zip_close(apk); + apk = nullptr; + return CURLE_SSL_CACERT_BADFILE; + } + + const std::unique_ptr pem = boost::make_unique(stat.size); + + if (zip_fread(apk_file, reinterpret_cast(pem.get()), stat.size) == -1) { + zip_fclose(apk_file); + apk_file = nullptr; + zip_close(apk); + apk = nullptr; + return CURLE_SSL_CACERT_BADFILE; + } + + // get a pointer to the X509 certificate store (which may be empty!) + X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)sslctx); + if (store == nullptr) { + return CURLE_SSL_CACERT_BADFILE; + } + + // get a BIO + BIO *bio = BIO_new_mem_buf(pem.get(), static_cast(stat.size)); + if (bio == nullptr) { + store = nullptr; + return CURLE_SSL_CACERT_BADFILE; + } + + // use it to read the PEM formatted certificate from memory into an X509 + // structure that SSL can use + X509 *cert = nullptr; + while (PEM_read_bio_X509(bio, &cert, 0, nullptr) != nullptr) { + if (cert == nullptr) { + BIO_free(bio); + bio = nullptr; + store = nullptr; + return CURLE_SSL_CACERT_BADFILE; + } + + // add our certificate to this store + if (X509_STORE_add_cert(store, cert) == 0) { + X509_free(cert); + cert = nullptr; + BIO_free(bio); + bio = nullptr; + store = nullptr; + return CURLE_SSL_CACERT_BADFILE; + } + + X509_free(cert); + cert = nullptr; + } + + // decrease reference counts + BIO_free(bio); + bio = nullptr; + + zip_fclose(apk_file); + apk_file = nullptr; + zip_close(apk); + apk = nullptr; + + // all set to go + return CURLE_OK; +} +#endif + // This function must run in the CURL thread. void start_request(void *const ptr) { assert(uv_thread_self() == thread_id); @@ -437,15 +537,14 @@ void start_request(void *const ptr) { context->baton->response = std::unique_ptr(new Response()); } -#ifndef __ANDROID__ - std::string ca_path = "ca-bundle.crt"; -#else - std::string ca_path = mbgl::android::data_path + "/ca-bundle.crt"; -#endif - // Carry on the shared pointer in the private information of the CURL handle. curl_easy_setopt(context->handle, CURLOPT_PRIVATE, context); - curl_easy_setopt(context->handle, CURLOPT_CAINFO, ca_path.c_str()); +#ifndef __ANDROID__ + curl_easy_setopt(context->handle, CURLOPT_CAINFO, "ca-bundle.crt") +#else + curl_easy_setopt(context->handle, CURLOPT_SSLCERTTYPE, "PEM"); + curl_easy_setopt(context->handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function); +#endif curl_easy_setopt(context->handle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(context->handle, CURLOPT_URL, context->baton->path.c_str()); curl_easy_setopt(context->handle, CURLOPT_WRITEFUNCTION, curl_write_cb); From 16328bc00b0db17e94345d3516fcf1e8e43baf75 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 18 Nov 2014 00:55:59 +1100 Subject: [PATCH 154/415] Remove debug mode --- .../lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index ce6496db1..3131f75d4 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -7,13 +7,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.graphics.PointF; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; +import android.os.Debug; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.util.Log; @@ -146,15 +146,10 @@ public class MapView extends SurfaceView { mStyleUrl = "asset://styles/styles/bright-v6.json"; mAccessToken = "pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"; - // Are we a debug build? - boolean isDebuggable = (context.getApplicationInfo().flags & - ApplicationInfo.FLAG_DEBUGGABLE) == ApplicationInfo.FLAG_DEBUGGABLE; - // Create the NativeMapView mNativeMapView = new NativeMapView(this, cachePath, dataPath, apkPath); mNativeMapView.setStyleURL(mStyleUrl); mNativeMapView.setAccessToken(mAccessToken); - mNativeMapView.setDebug(isDebuggable); // Load the attributes TypedArray typedArray = context.obtainStyledAttributes(attrs, From 92c712748ab80d146451bdb1ab8ceed5550c3c8d Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 18 Nov 2014 01:01:28 +1100 Subject: [PATCH 155/415] Fix no debug mode --- .../java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 3131f75d4..f2443c675 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -173,7 +173,7 @@ public class MapView extends SurfaceView { setRotateEnabled(typedArray.getBoolean( R.styleable.MapView_rotateEnabled, true)); setDebugActive(typedArray.getBoolean( - R.styleable.MapView_debugActive, true)); + R.styleable.MapView_debugActive, false)); } finally { typedArray.recycle(); } From 36780a85b8f915b08ee0c7a67cb22d01c0fc71c1 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 17 Nov 2014 12:03:49 -0800 Subject: [PATCH 156/415] improve the android linux setup instructions --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9e304447a..8180f4472 100644 --- a/README.md +++ b/README.md @@ -107,19 +107,20 @@ Or proceed to building the debug application with: Follow Linux steps until `./configure`. -Install Oracle JDK 7: +Install Oracle JDK 7 (requires license agreement) from http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html - wget http://download.oracle.com/otn-pub/java/jdk/7u71-b14/jdk-7u71-linux-x64.tar.gz - tar -xzf jdk-7u71-linux-x64.tar.gz - export JAVA_HOME="/dir/to/jdk1.7.0_71" +Or the OPEN JDK: + + sudo apt-get install -y openjdk-7-jdk Install Android NDK: + sudo apt-get install p7zip-full wget http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin chmod +x android-ndk-r10c-linux-x86_64.bin - ./android-ndk-r10c-linux-x86_64.bin - export ANDROID_NDK_PATH="/dir/to/android-ndk-r10c" - export NDK_PATH=$(ANDROID_NDK_PATH) + 7z x ./android-ndk-r10c-linux-x86_64.bin > /dev/null + export ANDROID_NDK_PATH="$(pwd)/android-ndk-r10c" + export NDK_PATH=${ANDROID_NDK_PATH} Install Android SDK: @@ -127,7 +128,7 @@ Install Android SDK: wget http://dl.google.com/android/android-sdk_r23.0.2-linux.tgz tar -xzf ./android-sdk_r23.0.2-linux.tgz ./android-sdk-linux/tools/android update sdk -u -a -t tools,platform-tools,build-tools-21.1.1,android-21,extra-android-m2repository,extra-google-m2repository - export ANDROID_HOME="/dir/to/android-sdk-linux" + export ANDROID_HOME="$(pwd)/android-sdk-linux" Run: From 417a156b5781b223f1c8c2129006160738da687f Mon Sep 17 00:00:00 2001 From: Jordan Hollinger Date: Mon, 17 Nov 2014 15:53:14 -0800 Subject: [PATCH 157/415] Fix missing semicolon; --- platform/default/http_request_baton_curl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index 23029bc5e..ffd352cfe 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -540,7 +540,7 @@ void start_request(void *const ptr) { // Carry on the shared pointer in the private information of the CURL handle. curl_easy_setopt(context->handle, CURLOPT_PRIVATE, context); #ifndef __ANDROID__ - curl_easy_setopt(context->handle, CURLOPT_CAINFO, "ca-bundle.crt") + curl_easy_setopt(context->handle, CURLOPT_CAINFO, "ca-bundle.crt"); #else curl_easy_setopt(context->handle, CURLOPT_SSLCERTTYPE, "PEM"); curl_easy_setopt(context->handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function); From c02ea37ed6a92e626d2151c60345d0107fbd1922 Mon Sep 17 00:00:00 2001 From: Jordan Hollinger Date: Mon, 17 Nov 2014 15:54:36 -0800 Subject: [PATCH 158/415] Fix android map rotation. Rotate by degrees rather than radians. Fixes part of #553. --- .../java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index f2443c675..a49a431ab 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -854,7 +854,7 @@ public class MapView extends SurfaceView { // Rotate the map double bearing = mNativeMapView.getBearing(); - bearing += detector.getRotationDegreesDelta() * Math.PI / 180.0; + bearing += detector.getRotationDegreesDelta(); Log.d("rotate", "rotate to " + bearing); mNativeMapView.setBearing(bearing, detector.getFocusX() / mScreenDensity, detector.getFocusY() / mScreenDensity); From 1e8db0bda23cf92ba3de10fc8ba83a27b14d8424 Mon Sep 17 00:00:00 2001 From: Jordan Hollinger Date: Mon, 17 Nov 2014 16:43:53 -0800 Subject: [PATCH 159/415] Enable fling handling code. #553 --- .../java/com/mapbox/mapboxgl/lib/MapView.java | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index a49a431ab..45eeda4e8 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -679,25 +679,23 @@ public class MapView extends SurfaceView { // Fling the map // TODO Google Maps also has a rotate and zoom fling - // TODO does not work - /* - * float ease = 0.25f; - * - * velocityX = velocityX * ease; velocityY = velocityY * ease; - * - * double speed = Math.sqrt(velocityX * velocityX + velocityY * - * velocityY); double deceleration = 2500; double duration = speed / - * (deceleration * ease); - * - * - * // Cancel any animation mNativeMapView.cancelTransitions(); - * - * mNativeMapView.moveBy(velocityX * duration / 2.0 / mScreenDensity, velocityY * - * duration / 2.0 / mScreenDensity, duration); - * - * return true; - */ - return false; + + float ease = 0.25f; + + velocityX = velocityX * ease; + velocityY = velocityY * ease; + + double speed = Math.sqrt(velocityX * velocityX + velocityY * velocityY); + double deceleration = 2500; + double duration = speed / (deceleration * ease); + + // Cancel any animation + mNativeMapView.cancelTransitions(); + + mNativeMapView.moveBy(velocityX * duration / 2.0 / mScreenDensity, velocityY * + duration / 2.0 / mScreenDensity, duration); + + return true; } // Called for drags From c959b8683f853ec241f6845af7ddca473225654a Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 18 Nov 2014 21:16:09 +1100 Subject: [PATCH 160/415] Ignore APKs --- android/java/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/android/java/.gitignore b/android/java/.gitignore index 7c3a4c469..2e8c5e2ba 100644 --- a/android/java/.gitignore +++ b/android/java/.gitignore @@ -9,6 +9,7 @@ build/ */build/ *.so +*.apk # Lib assets lib/src/main/assets/ From 5cfcb9b460426c93a1c6dd3c3e2d93f703b3d7ac Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 18 Nov 2014 21:34:22 +1100 Subject: [PATCH 161/415] TIdy up gradle and manifest --- android/java/app/build.gradle | 9 ++++++++- android/java/app/src/main/AndroidManifest.xml | 4 +--- android/java/lib/build.gradle | 11 +++++++---- android/java/lib/src/main/AndroidManifest.xml | 4 +--- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/android/java/app/build.gradle b/android/java/app/build.gradle index 40d2fc710..c59944baf 100644 --- a/android/java/app/build.gradle +++ b/android/java/app/build.gradle @@ -8,11 +8,18 @@ android { applicationId "com.mapbox.mapboxgl.app" minSdkVersion 14 targetSdkVersion 21 + versionCode 1 + versionName "0.1" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 } buildTypes { release { - runProguard false + runProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } diff --git a/android/java/app/src/main/AndroidManifest.xml b/android/java/app/src/main/AndroidManifest.xml index 567529187..80a39ed4b 100644 --- a/android/java/app/src/main/AndroidManifest.xml +++ b/android/java/app/src/main/AndroidManifest.xml @@ -1,8 +1,6 @@ + package="com.mapbox.mapboxgl.app" > + package="com.mapbox.mapboxgl.lib" > From 773f1d8212cf4bb6223b517a79fbf75d61d6afbc Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 18 Nov 2014 21:36:53 +1100 Subject: [PATCH 162/415] Disable proguard --- android/java/app/build.gradle | 2 +- android/java/lib/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/android/java/app/build.gradle b/android/java/app/build.gradle index c59944baf..2e8f9bcfe 100644 --- a/android/java/app/build.gradle +++ b/android/java/app/build.gradle @@ -19,7 +19,7 @@ android { buildTypes { release { - runProguard true + runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } diff --git a/android/java/lib/build.gradle b/android/java/lib/build.gradle index 1700dd6c8..59c097f82 100644 --- a/android/java/lib/build.gradle +++ b/android/java/lib/build.gradle @@ -23,7 +23,7 @@ android { } release { - runProguard true + runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } From eaece4f8bba91657db8002e8e8607e1378e7d3dd Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 18 Nov 2014 22:19:24 +1100 Subject: [PATCH 163/415] Move stlye url and access token to property Remove fragment stuff (for now) --- android/cpp/jni.cpp | 6 +- android/java/app/src/main/AndroidManifest.xml | 5 - .../mapbox/mapboxgl/app/FragmentActivity.java | 29 ---- .../com/mapbox/mapboxgl/app/MainActivity.java | 3 + .../src/main/res/layout/activity_fragment.xml | 11 -- .../com/mapbox/mapboxgl/lib/MapFragment.java | 150 ------------------ .../java/com/mapbox/mapboxgl/lib/MapView.java | 56 ++++--- .../mapbox/mapboxgl/lib/NativeMapView.java | 30 ++-- .../java/lib/src/main/res/values/attrs.xml | 15 +- .../java/lib/src/main/res/values/strings.xml | 3 - 10 files changed, 56 insertions(+), 252 deletions(-) delete mode 100644 android/java/app/src/main/java/com/mapbox/mapboxgl/app/FragmentActivity.java delete mode 100644 android/java/app/src/main/res/layout/activity_fragment.xml delete mode 100644 android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapFragment.java delete mode 100644 android/java/lib/src/main/res/values/strings.xml diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index ec9283b87..37ed35f77 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -813,9 +813,9 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { { "nativeSetAppliedClasses", "(JLjava/util/List;)V", reinterpret_cast(&nativeSetAppliedClasses) }, { "nativeGetAppliedClasses", "(J)Ljava/util/List;", reinterpret_cast(&nativeGetAppliedClasses) }, { "nativeSetDefaultTransitionDuration", "(JJ)V", reinterpret_cast(&nativeSetDefaultTransitionDuration) }, - { "nativeSetStyleURL", "(JLjava/lang/String;)V", reinterpret_cast(&nativeSetStyleURL) }, - { "nativeSetStyleJSON", "(JLjava/lang/String;Ljava/lang/String;)V", reinterpret_cast(&nativeSetStyleJSON) }, - { "nativeGetStyleJSON", "(J)Ljava/lang/String;", reinterpret_cast(&nativeGetStyleJSON) }, + { "nativeSetStyleUrl", "(JLjava/lang/String;)V", reinterpret_cast(&nativeSetStyleURL) }, + { "nativeSetStyleJson", "(JLjava/lang/String;Ljava/lang/String;)V", reinterpret_cast(&nativeSetStyleJSON) }, + { "nativeGetStyleJson", "(J)Ljava/lang/String;", reinterpret_cast(&nativeGetStyleJSON) }, { "nativeSetAccessToken", "(JLjava/lang/String;)V", reinterpret_cast(&nativeSetAccessToken) }, { "nativeGetAccessToken", "(J)Ljava/lang/String;", reinterpret_cast(&nativeGetAccessToken) }, { "nativeCancelTransitions", "(J)V", reinterpret_cast(&nativeCancelTransitions) }, diff --git a/android/java/app/src/main/AndroidManifest.xml b/android/java/app/src/main/AndroidManifest.xml index 80a39ed4b..5b73a63ff 100644 --- a/android/java/app/src/main/AndroidManifest.xml +++ b/android/java/app/src/main/AndroidManifest.xml @@ -17,11 +17,6 @@ - - diff --git a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/FragmentActivity.java b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/FragmentActivity.java deleted file mode 100644 index 1b27ca2fd..000000000 --- a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/FragmentActivity.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.mapbox.mapboxgl.app; - -import android.app.Activity; -import android.os.Bundle; -import android.util.Log; - -public class FragmentActivity extends Activity { - - // - // Static members - // - - // Tag used for logging - private static final String TAG = "FragmentActivity"; - - // - // Lifecycle events - // - - // Called when activity is created - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Log.v(TAG, "onCreate"); - - // Load the layout - setContentView(R.layout.activity_fragment); - } -} diff --git a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java index acc899500..2166b7dd1 100644 --- a/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java +++ b/android/java/app/src/main/java/com/mapbox/mapboxgl/app/MainActivity.java @@ -41,6 +41,9 @@ public class MainActivity extends Activity { setContentView(R.layout.activity_main); mMapView = (MapView) findViewById(R.id.map); + // Load the API key + mMapView.setAccessToken("pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"); + // Need to pass on any saved state to the map mMapView.onCreate(savedInstanceState); } diff --git a/android/java/app/src/main/res/layout/activity_fragment.xml b/android/java/app/src/main/res/layout/activity_fragment.xml deleted file mode 100644 index 6310106fc..000000000 --- a/android/java/app/src/main/res/layout/activity_fragment.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapFragment.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapFragment.java deleted file mode 100644 index 1263ac56e..000000000 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapFragment.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.mapbox.mapboxgl.lib; - -import android.app.Activity; -import android.app.Fragment; -import android.content.res.TypedArray; -import android.os.Bundle; -import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -public class MapFragment extends Fragment { - - // - // Static members - // - - // Tag used for logging - private static final String TAG = "MapFragment"; - - // The map - private MapView mMapView; - - // The style attrs to load into the map - AttributeSet mAttrs; - - // - // Lifecycle events - // - - // Called when the fragment is created - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - Log.v(TAG, "onCreateView"); - - // Create the map - mMapView = new MapView(getActivity()); - - // Load the attributes - TypedArray typedArray = getActivity().obtainStyledAttributes(mAttrs, - R.styleable.MapFragment, 0, 0); - try { - double centerLongitude = typedArray.getFloat( - R.styleable.MapFragment_centerLongitude, 0.0f); - double centerLatitude = typedArray.getFloat( - R.styleable.MapFragment_centerLatitude, 0.0f); - LonLat centerCoordinate = new LonLat(centerLongitude, - centerLatitude); - mMapView.setCenterCoordinate(centerCoordinate); - mMapView.setDirection(typedArray.getFloat( - R.styleable.MapFragment_direction, 0.0f)); - mMapView.setZoomLevel(typedArray.getFloat( - R.styleable.MapFragment_zoomLevel, 0.0f)); - mMapView.setZoomEnabled(typedArray.getBoolean( - R.styleable.MapFragment_zoomEnabled, true)); - mMapView.setScrollEnabled(typedArray.getBoolean( - R.styleable.MapFragment_scrollEnabled, true)); - mMapView.setRotateEnabled(typedArray.getBoolean( - R.styleable.MapFragment_rotateEnabled, true)); - mMapView.setDebugActive(typedArray.getBoolean( - R.styleable.MapFragment_debugActive, false)); - } finally { - typedArray.recycle(); - } - - // Need to pass on any saved state to the map - mMapView.onCreate(savedInstanceState); - - // Return the map as the root view - return mMapView; - } - - // Called when the fragment is destroyed - @Override - public void onDestroyView() { - super.onDestroyView(); - Log.v(TAG, "onDestroyView"); - - // Need to pass on to view - mMapView.onDestroy(); - mMapView = null; - } - - // Called to load any fragment attributes set in the activity's layout - @Override - public void onInflate(Activity activity, AttributeSet attrs, - Bundle savedInstanceState) { - super.onInflate(activity, attrs, savedInstanceState); - Log.v(TAG, "onInflate"); - - // TODO seems this is called before onCreateView - // Kinda strange, is it supposed to be like this? - // Need to keep a copy of these and pass on to MapView later - - mAttrs = attrs; - } - - // Called when the fragment is visible - @Override - public void onStart() { - super.onStart(); - Log.v(TAG, "onStart"); - - // Need to pass on to view - mMapView.onStart(); - } - - // Called when the fragment is invisible - @Override - public void onStop() { - super.onStop(); - Log.v(TAG, "onStop"); - - // Need to pass on to view - mMapView.onStop(); - } - - // Called when the fragment is in the background - @Override - public void onPause() { - super.onPause(); - Log.v(TAG, "onPause"); - - // Need to pass on to view - mMapView.onPause(); - } - - // Called when the fragment is no longer in the background - @Override - public void onResume() { - super.onResume(); - Log.v(TAG, "onResume"); - - // Need to pass on to view - mMapView.onResume(); - } - - // Called before fragment is destroyed - @Override - public void onSaveInstanceState(Bundle outState) { - Log.v(TAG, "onSaveInstanceState"); - - // Need to retrieve any saved state from the map - mMapView.onSaveInstanceState(outState); - super.onSaveInstanceState(outState); - } -} diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 45eeda4e8..530d563dc 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -13,7 +13,6 @@ import android.graphics.PointF; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; -import android.os.Debug; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.util.Log; @@ -50,6 +49,8 @@ public class MapView extends SurfaceView { private static final String STATE_SCROLL_ENABLED = "scrollEnabled"; private static final String STATE_ROTATE_ENABLED = "rotateEnabled"; private static final String STATE_DEBUG_ACTIVE = "debugActive"; + private static final String STATE_STYLE_URL = "styleUrl"; + private static final String STATE_ACCESS_TOKEN = "accessToken"; // // Instance members @@ -58,12 +59,6 @@ public class MapView extends SurfaceView { // Used to call JNI NativeMapView private NativeMapView mNativeMapView; - // Used to style the map - private String mStyleUrl; - - // Used to load map tiles - private String mAccessToken; - // Used to handle DPI scaling private float mScreenDensity = 1.0f; @@ -79,8 +74,6 @@ public class MapView extends SurfaceView { // Used to track trackball long presses private TrackballLongPressTimeOut mCurrentTrackballLongPressTimeOut; - // Used to check connection status - private ConnectivityManager mConnectivityManager; private ConnectivityReceiver mConnectivityReceiver; // Holds the context @@ -93,6 +86,7 @@ public class MapView extends SurfaceView { private boolean mZoomEnabled = true; private boolean mScrollEnabled = true; private boolean mRotateEnabled = true; + private String mStyleUrl = "asset://styles/styles/bright-v6.json"; // // Constructors @@ -141,15 +135,9 @@ public class MapView extends SurfaceView { String dataPath = context.getFilesDir().getAbsolutePath(); String apkPath = context.getPackageCodePath(); - // Load the map style and API key - //mStyleUrl = "https://mapbox.github.io/mapbox-gl-styles/styles/bright-v6.json"; - mStyleUrl = "asset://styles/styles/bright-v6.json"; - mAccessToken = "pk.eyJ1IjoibGpiYWRlIiwiYSI6IlJSQ0FEZ2MifQ.7mE4aOegldh3595AG9dxpQ"; - // Create the NativeMapView mNativeMapView = new NativeMapView(this, cachePath, dataPath, apkPath); - mNativeMapView.setStyleURL(mStyleUrl); - mNativeMapView.setAccessToken(mAccessToken); + mNativeMapView.setStyleUrl(mStyleUrl); // Load the attributes TypedArray typedArray = context.obtainStyledAttributes(attrs, @@ -174,6 +162,10 @@ public class MapView extends SurfaceView { R.styleable.MapView_rotateEnabled, true)); setDebugActive(typedArray.getBoolean( R.styleable.MapView_debugActive, false)); + setStyleUrl(typedArray.getString( + R.styleable.MapView_styleUrl)); + setAccessToken(typedArray.getString( + R.styleable.MapView_accessToken)); } finally { typedArray.recycle(); } @@ -207,9 +199,9 @@ public class MapView extends SurfaceView { } // Check current connection status - mConnectivityManager = (ConnectivityManager)context.getSystemService( + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE); - NetworkInfo activeNetwork = mConnectivityManager.getActiveNetworkInfo(); + NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); boolean isConnected = (activeNetwork != null) && activeNetwork.isConnectedOrConnecting(); onConnectivityChanged(isConnected); @@ -326,6 +318,23 @@ public class MapView extends SurfaceView { mNativeMapView.toggleDebug(); } + public void setStyleUrl(String url) { + mStyleUrl = url; + mNativeMapView.setStyleUrl(url); + } + + public String getStyleUrl() { + return mStyleUrl; + } + + public void setAccessToken(String accessToken) { + mNativeMapView.setAccessToken(accessToken); + } + + public String getAccessToken() { + return mNativeMapView.getAccessToken(); + } + // // Style methods // @@ -334,12 +343,11 @@ public class MapView extends SurfaceView { // TODO } - // TODO seems like JSON simple may be better since it implements Map - // interface + // TODO seems like JSON simple may be better since it implements Map interface // Other candidates: fastjson, json-smart, fossnova json, public JSONObject getRawStyle() { try { - return new JSONObject(mNativeMapView.getStyleJSON()); + return new JSONObject(mNativeMapView.getStyleJson()); } catch (JSONException e) { e.printStackTrace(); return null; @@ -347,7 +355,7 @@ public class MapView extends SurfaceView { } public void setRawStyle(JSONObject style) { - mNativeMapView.setStyleJSON(style.toString()); + mNativeMapView.setStyleJson(style.toString()); } public void getStyleOrderedLayerNames() { @@ -397,6 +405,8 @@ public class MapView extends SurfaceView { setRotateEnabled(savedInstanceState .getBoolean(STATE_ROTATE_ENABLED)); setDebugActive(savedInstanceState.getBoolean(STATE_DEBUG_ACTIVE)); + setStyleUrl(savedInstanceState.getString(STATE_STYLE_URL)); + setAccessToken(savedInstanceState.getString(STATE_ACCESS_TOKEN)); } mNativeMapView.initializeDisplay(); @@ -415,6 +425,8 @@ public class MapView extends SurfaceView { outState.putBoolean(STATE_SCROLL_ENABLED, isScrollEnabled()); outState.putBoolean(STATE_ROTATE_ENABLED, isRotateEnabled()); outState.putBoolean(STATE_DEBUG_ACTIVE, isDebugActive()); + outState.putString(STATE_STYLE_URL, getStyleUrl()); + outState.putString(STATE_ACCESS_TOKEN, getAccessToken()); } // Called when we need to clean up diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java index fc525a594..7d24abf44 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/NativeMapView.java @@ -172,10 +172,6 @@ class NativeMapView { nativeResize(mNativeMapViewPtr, width, height, ratio, fbWidth, fbHeight); } - /*public void Set getActiveSources() { - return nativeGetActiveSources(mNativeMapViewPtr); - }*/ - public void setAppliedClasses(List classes) { nativeSetAppliedClasses(mNativeMapViewPtr, classes); } @@ -198,20 +194,20 @@ class NativeMapView { durationMilliseconds); } - public void setStyleURL(String url) { - nativeSetStyleURL(mNativeMapViewPtr, url); + public void setStyleUrl(String url) { + nativeSetStyleUrl(mNativeMapViewPtr, url); } - public void setStyleJSON(String newStyleJSON) { - setStyleJSON(newStyleJSON, ""); + public void setStyleJson(String newStyleJson) { + setStyleJson(newStyleJson, ""); } - public void setStyleJSON(String newStyleJSON, String base) { - nativeSetStyleJSON(mNativeMapViewPtr, newStyleJSON, base); + public void setStyleJson(String newStyleJson, String base) { + nativeSetStyleJson(mNativeMapViewPtr, newStyleJson, base); } - public String getStyleJSON() { - return nativeGetStyleJSON(mNativeMapViewPtr); + public String getStyleJson() { + return nativeGetStyleJson(mNativeMapViewPtr); } public void setAccessToken(String accessToken) { @@ -452,8 +448,6 @@ class NativeMapView { private native void nativeResize(long nativeMapViewPtr, int width, int height, float ratio, int fbWidth, int fbHeight); - //private native void Set nativeGetActiveSources(long nativeMapViewPtr); - private native void nativeSetAppliedClasses(long nativeMapViewPtr, List classes); @@ -462,12 +456,12 @@ class NativeMapView { private native void nativeSetDefaultTransitionDuration( long nativeMapViewPtr, long durationMilliseconds); - private native void nativeSetStyleURL(long nativeMapViewPtr, String url); + private native void nativeSetStyleUrl(long nativeMapViewPtr, String url); - private native void nativeSetStyleJSON(long nativeMapViewPtr, - String newStyleJSON, String base); + private native void nativeSetStyleJson(long nativeMapViewPtr, + String newStyleJson, String base); - private native String nativeGetStyleJSON(long nativeMapViewPtr); + private native String nativeGetStyleJson(long nativeMapViewPtr); private native void nativeSetAccessToken(long nativeMapViewPtr, String accessToken); diff --git a/android/java/lib/src/main/res/values/attrs.xml b/android/java/lib/src/main/res/values/attrs.xml index b42f729c9..191e7d000 100644 --- a/android/java/lib/src/main/res/values/attrs.xml +++ b/android/java/lib/src/main/res/values/attrs.xml @@ -7,6 +7,8 @@ + + @@ -17,16 +19,7 @@ - - - - - - - - - - - + + diff --git a/android/java/lib/src/main/res/values/strings.xml b/android/java/lib/src/main/res/values/strings.xml deleted file mode 100644 index f11f7450a..000000000 --- a/android/java/lib/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - From 900f6e87fa7a98c4b6e91f4809268295549797a8 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 18 Nov 2014 22:47:57 +1100 Subject: [PATCH 164/415] Fix null string crash --- android/cpp/jni.cpp | 41 +++++++++++++++++++ .../app/src/main/res/layout/activity_main.xml | 8 ++-- .../java/com/mapbox/mapboxgl/lib/MapView.java | 5 +-- .../java/lib/src/main/res/values/attrs.xml | 2 - include/mbgl/android/jni.hpp | 1 + 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/android/cpp/jni.cpp b/android/cpp/jni.cpp index 37ed35f77..497091832 100644 --- a/android/cpp/jni.cpp +++ b/android/cpp/jni.cpp @@ -43,6 +43,7 @@ jfieldID lon_lat_zoom_lat_id = nullptr; jfieldID lon_lat_zoom_zoom_id = nullptr; jclass runtime_exception_class = nullptr; +jclass null_pointer_exception_class = nullptr; jmethodID list_to_array_id = nullptr; @@ -61,16 +62,28 @@ bool throw_error(JNIEnv* env, const char* msg) { std::string std_string_from_jstring(JNIEnv* env, jstring jstr) { std::string str; + + if (jstr == nullptr) { + if (env->ThrowNew(null_pointer_exception_class, "String cannot be null.") < 0) { + env->ExceptionDescribe(); + return str; + } + + return str; + } + jsize len = env->GetStringLength(jstr); if (len < 0) { env->ExceptionDescribe(); return str; } + const jchar* chars = env->GetStringChars(jstr, nullptr); if (chars == nullptr) { env->ExceptionDescribe(); return str; } + std::u16string ustr(reinterpret_cast(chars), len); env->ReleaseStringChars(jstr, chars); chars = nullptr; @@ -93,6 +106,15 @@ jstring std_string_to_jstring(JNIEnv* env, std::string str) { std::vector std_vector_string_from_jobject(JNIEnv* env, jobject jlist) { std::vector vector; + if (jlist == nullptr) { + if (env->ThrowNew(null_pointer_exception_class, "List cannot be null.") < 0) { + env->ExceptionDescribe(); + return vector; + } + + return vector; + } + jobjectArray array = reinterpret_cast(env->CallObjectMethod(jlist, list_to_array_id)); if (env->ExceptionCheck() || (array == nullptr)) { env->ExceptionDescribe(); @@ -757,6 +779,12 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_ERR; } + null_pointer_exception_class = env->FindClass("java/lang/NullPointerException"); + if (null_pointer_exception_class == nullptr) { + env->ExceptionDescribe(); + return JNI_ERR; + } + jclass list_class = env->FindClass("java/util/List"); if (list_class == nullptr) { env->ExceptionDescribe(); @@ -877,12 +905,22 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_ERR; } + null_pointer_exception_class = reinterpret_cast(env->NewGlobalRef(null_pointer_exception_class)); + if (runtime_exception_class == nullptr) { + env->ExceptionDescribe(); + env->DeleteGlobalRef(lon_lat_class); + env->DeleteGlobalRef(lon_lat_zoom_class); + env->DeleteGlobalRef(runtime_exception_class); + return JNI_ERR; + } + array_list_class = reinterpret_cast(env->NewGlobalRef(array_list_class)); if (array_list_class == nullptr) { env->ExceptionDescribe(); env->DeleteGlobalRef(lon_lat_class); env->DeleteGlobalRef(lon_lat_zoom_class); env->DeleteGlobalRef(runtime_exception_class); + env->DeleteGlobalRef(null_pointer_exception_class); return JNI_ERR; } @@ -917,6 +955,9 @@ extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { env->DeleteGlobalRef(runtime_exception_class); runtime_exception_class = nullptr; + env->DeleteGlobalRef(null_pointer_exception_class); + null_pointer_exception_class = nullptr; + list_to_array_id = nullptr; env->DeleteGlobalRef(array_list_class); diff --git a/android/java/app/src/main/res/layout/activity_main.xml b/android/java/app/src/main/res/layout/activity_main.xml index e4b11e3ad..bdfaaec15 100644 --- a/android/java/app/src/main/res/layout/activity_main.xml +++ b/android/java/app/src/main/res/layout/activity_main.xml @@ -1,11 +1,13 @@ + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" + tools:context="${packageName}.${activityClass}"> + android:id="@+id/map" + app:styleUrl="asset://styles/styles/bright-v6.json"/> diff --git a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java index 530d563dc..d86a0df74 100644 --- a/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java +++ b/android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java @@ -86,7 +86,7 @@ public class MapView extends SurfaceView { private boolean mZoomEnabled = true; private boolean mScrollEnabled = true; private boolean mRotateEnabled = true; - private String mStyleUrl = "asset://styles/styles/bright-v6.json"; + private String mStyleUrl; // // Constructors @@ -137,7 +137,6 @@ public class MapView extends SurfaceView { // Create the NativeMapView mNativeMapView = new NativeMapView(this, cachePath, dataPath, apkPath); - mNativeMapView.setStyleUrl(mStyleUrl); // Load the attributes TypedArray typedArray = context.obtainStyledAttributes(attrs, @@ -164,8 +163,6 @@ public class MapView extends SurfaceView { R.styleable.MapView_debugActive, false)); setStyleUrl(typedArray.getString( R.styleable.MapView_styleUrl)); - setAccessToken(typedArray.getString( - R.styleable.MapView_accessToken)); } finally { typedArray.recycle(); } diff --git a/android/java/lib/src/main/res/values/attrs.xml b/android/java/lib/src/main/res/values/attrs.xml index 191e7d000..770b70317 100644 --- a/android/java/lib/src/main/res/values/attrs.xml +++ b/android/java/lib/src/main/res/values/attrs.xml @@ -8,7 +8,6 @@ - @@ -20,6 +19,5 @@ - diff --git a/include/mbgl/android/jni.hpp b/include/mbgl/android/jni.hpp index 28b9b8917..2dbdf2ebe 100644 --- a/include/mbgl/android/jni.hpp +++ b/include/mbgl/android/jni.hpp @@ -25,6 +25,7 @@ extern jfieldID lon_lat_zoom_lat_id; extern jfieldID lon_lat_zoom_zoom_id; extern jclass runtime_exception_class; +extern jclass null_pointer_exception_class;; extern jmethodID list_to_array_id; From e52cf05ac3f404762988fcbda9bc3d4facb417e1 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Tue, 18 Nov 2014 23:34:46 +1100 Subject: [PATCH 165/415] Fix json string crash --- platform/android/asset_request_baton.cpp | 13 +++++++------ platform/default/http_request_baton_curl.cpp | 2 +- src/style/style.cpp | 2 ++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/platform/android/asset_request_baton.cpp b/platform/android/asset_request_baton.cpp index 30819e506..a0349b9f6 100644 --- a/platform/android/asset_request_baton.cpp +++ b/platform/android/asset_request_baton.cpp @@ -53,7 +53,7 @@ void AssetRequestBaton::run(uv_async_t *async) { int error = 0; ptr->apk = zip_open(mbgl::android::apk_path.c_str(), 0, &error); - if (ptr->apk == nullptr || ptr->canceled || !ptr->request) { + if ((ptr->apk == nullptr) || ptr->canceled || !ptr->request) { // Opening the APK failed or was canceled. There isn't much left we can do. const int message_size = zip_error_to_str(nullptr, 0, error, errno); const std::unique_ptr message = boost::make_unique(message_size); @@ -65,7 +65,7 @@ void AssetRequestBaton::run(uv_async_t *async) { std::string apk_file_path = "assets/" + ptr->path; ptr->apk_file = zip_fopen(ptr->apk, apk_file_path.c_str(), ZIP_FL_NOCASE); - if (ptr->apk_file == nullptr || ptr->canceled || !ptr->request) { + if ((ptr->apk_file == nullptr) || ptr->canceled || !ptr->request) { // Opening the asset failed or was canceled. We already have an open file handle // though, which we'll have to close. zip_error_get(ptr->apk, &error, nullptr); @@ -77,7 +77,7 @@ void AssetRequestBaton::run(uv_async_t *async) { } struct zip_stat stat; - if (zip_stat(ptr->apk, apk_file_path.c_str(), ZIP_FL_NOCASE, &stat) != 0 || + if ((zip_stat(ptr->apk, apk_file_path.c_str(), ZIP_FL_NOCASE, &stat) != 0) || ptr->canceled || !ptr->request) { // Stating failed or was canceled. We already have an open file handle // though, which we'll have to close. @@ -90,9 +90,9 @@ void AssetRequestBaton::run(uv_async_t *async) { return; } - const std::unique_ptr body = boost::make_unique(stat.size); + const std::unique_ptr data = boost::make_unique(stat.size); - if (zip_fread(ptr->apk_file, reinterpret_cast(body.get()), stat.size) == -1 || + if (static_cast(zip_fread(ptr->apk_file, reinterpret_cast(data.get()), stat.size)) != stat.size || ptr->canceled || !ptr->request) { // Reading failed or was canceled. We already have an open file handle // though, which we'll have to close. @@ -108,7 +108,8 @@ void AssetRequestBaton::run(uv_async_t *async) { if (ptr->request) { ptr->request->response = std::unique_ptr(new Response); ptr->request->response->code = 200; - ptr->request->response->data = body.get(); + std::string body(data.get(), stat.size); + ptr->request->response->data = body; ptr->request->notify(); } diff --git a/platform/default/http_request_baton_curl.cpp b/platform/default/http_request_baton_curl.cpp index ffd352cfe..a5b0edbbf 100644 --- a/platform/default/http_request_baton_curl.cpp +++ b/platform/default/http_request_baton_curl.cpp @@ -448,7 +448,7 @@ static CURLcode sslctx_function(CURL */*curl*/, void *sslctx, void */*parm*/) { const std::unique_ptr pem = boost::make_unique(stat.size); - if (zip_fread(apk_file, reinterpret_cast(pem.get()), stat.size) == -1) { + if (static_cast(zip_fread(apk_file, reinterpret_cast(pem.get()), stat.size)) != stat.size) { zip_fclose(apk_file); apk_file = nullptr; zip_close(apk); diff --git a/src/style/style.cpp b/src/style/style.cpp index aa5b15088..c398f9d59 100644 --- a/src/style/style.cpp +++ b/src/style/style.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -91,6 +92,7 @@ void Style::loadJSON(const uint8_t *const data) { rapidjson::Document doc; doc.Parse<0>((const char *const)data); if (doc.HasParseError()) { + Log::Error(Event::ParseStyle, "Error parsing style JSON at %i: %s", doc.GetErrorOffset(), doc.GetParseError()); throw error::style_parse(doc.GetErrorOffset(), doc.GetParseError()); } From 69d42411ab10e352d16ebb618d94170edc9e7af8 Mon Sep 17 00:00:00 2001 From: Leith Bade Date: Wed, 19 Nov 2014 20:13:37 +1100 Subject: [PATCH 166/415] Fix hand/crash in pause when rotating screen --- android/cpp/native_map_view.cpp | 14 ++++++-------- src/map/map.cpp | 1 + 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/android/cpp/native_map_view.cpp b/android/cpp/native_map_view.cpp index 7a04688b0..3997d9176 100644 --- a/android/cpp/native_map_view.cpp +++ b/android/cpp/native_map_view.cpp @@ -541,20 +541,18 @@ void NativeMapView::loadExtensions() { void NativeMapView::stop() { mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::stop"); - assert(display != EGL_NO_DISPLAY); - assert(context != EGL_NO_CONTEXT); - - map.stop(); + if ((display != EGL_NO_DISPLAY) && (display != EGL_NO_CONTEXT)) { + map.stop(); + } } void NativeMapView::pause(bool wait_for_pause) { mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::pause %s", (wait_for_pause) ? "true" : "false"); - assert(display != EGL_NO_DISPLAY); - assert(context != EGL_NO_CONTEXT); - - map.pause(wait_for_pause); + if ((display != EGL_NO_DISPLAY) && (display != EGL_NO_CONTEXT)) { + map.pause(wait_for_pause); + } } void NativeMapView::resume() { diff --git a/src/map/map.cpp b/src/map/map.cpp index 3746fb4b6..a12cba00b 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -429,6 +429,7 @@ void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) { if (!style) { style = std::make_shared - - diff --git a/android/java/app/src/main/res/values-v14/styles.xml b/android/java/app/src/main/res/values-v14/styles.xml deleted file mode 100644 index a91fd0372..000000000 --- a/android/java/app/src/main/res/values-v14/styles.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/android/java/app/src/main/res/values/styles.xml b/android/java/app/src/main/res/values/styles.xml index 6ce89c7ba..4c67848fc 100644 --- a/android/java/app/src/main/res/values/styles.xml +++ b/android/java/app/src/main/res/values/styles.xml @@ -4,7 +4,7 @@ Base application theme, dependent on API level. This theme is replaced by AppBaseTheme from res/values-vXX/styles.xml on newer devices. --> - - - - - +