Merge pull request #676 from mapbox/android-mason
Android GL mega-merge
2
.gitignore
vendored
@@ -6,8 +6,10 @@
|
||||
/mason_packages
|
||||
/config.gypi
|
||||
/config-ios.gypi
|
||||
/config-android.gypi
|
||||
/build
|
||||
/include/mbgl/shader/shaders.hpp
|
||||
/src/shader/shaders_gl.cpp
|
||||
/src/shader/shaders_gles2.cpp
|
||||
/bin/style.bin.js
|
||||
*~
|
||||
|
||||
2
.mason
21
.travis.yml
@@ -4,6 +4,12 @@ matrix:
|
||||
exclude:
|
||||
- os: linux
|
||||
include:
|
||||
- os: linux
|
||||
env: CONFIG=release MASON_PLATFORM=android TESTMUNK=yes
|
||||
compiler: clang
|
||||
- os: linux
|
||||
env: CONFIG=debug MASON_PLATFORM=android TESTMUNK=no
|
||||
compiler: clang
|
||||
- os: linux
|
||||
env: BUILDTYPE=Release JOBS=16
|
||||
compiler: clang
|
||||
@@ -19,21 +25,26 @@ matrix:
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "bG4YYWMfl9API0MSRgmOaJrlGLv06tRg9KJNawBieZvBJbITPpxVGJZT3/l/SEJ+Rl15e2dRex4k+MGQlmT2SqPQxTEYWv1qxNigKPPcla7IWeNmWWqW8uVvFjdglojgBOK2k/xErVQtA4zDfi3mwSXH4DKwquXWsoEKmX2SV7M="
|
||||
- secure: "Cbvap9ubVKgjPe3hUhI6JGeDZzBXHpOG9RaYKh+SdoIPhKnlJiNOYm1egomi+e4uqJInlFKuVHTw7Ng9Cun6Zm0jIxpkSchv1GpsR7hmB3UGnGed19Dw8121FwuUaktN+4YnbVlsyd+u8EHD3+h58t4eELrLrZolM4rS7DL6caA="
|
||||
- secure: "MZHblLZXG/jWf2w0ZFlxCLDwx2qtGgRDODQyg1BR7JIuMz6AtWv8XR/sUczWLbiABCL0a/NzJF1g4v2pI7X69IntcjOdIABBgTh7++6+1TJ0Kp8viEltb55nQG3lHy/R6fOaI7Pj9tuCX0PCRtGA5C/fGnodLGEjy3RVOJ09ln0="
|
||||
- secure: "KaSQbhgjtV7ZCkesHmvrNsbQVjk5SPfGKB1VkWenRGYhLF45HpSRNwSxMQddZ566Pg7qIFgF1iWl/B0QW3B6AWL5WmzQ5AOJgwS876pNIc/UT7ubMPtgAtjpvw1bQvQP3B8MrB+3OE5c6tD+a3LhR9krV//dOsfErR5Yy+3Mbkc="
|
||||
- ANDROID_BUILD_INSTANCE_PROFILE: arn:aws:iam::234858372212:instance-profile/android-gl-build/travis/instance-profile/travis-mapbox-gl-native-AndroidInstanceProfile-13EA815DD8IQ9
|
||||
- secure: "RiBIBfVhhaMjU5ksuwJO3shdvG9FpinBjdSv4co9jg9171SR8edNriedHjVKSIeBhSGNmZmX+twS3dJS/By6tl/LKh9sTynA+ZAYYljkE7jn881B/gMrlYvdAA6og5KvkhV1/0iJWlhuZrMTkhpDR200iLgg3EWBhWjltzmDW/I="
|
||||
- secure: "CHBiUM60TolDbQnn+4IRA/tvOKwKs3g9EDvv8YHSJMg3FuHmjKQkprBasvxf3hnTXg4WLZEubmeDcyJ6RRzPP5mMSr/hksYl0pSjj/6TUecE5fHPVVeN7txVqkpOBf9i45Y+iBUQMjBb1NnDK3pHXxpnAs1Q/pe7vReErj4GF1U="
|
||||
- LD_LIBRARY_PATH: '/usr/local/lib'
|
||||
- TERM: dumb
|
||||
|
||||
before_install:
|
||||
- if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then sudo service mysql stop; fi
|
||||
- if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then sudo service postgresql stop; fi
|
||||
- source ./scripts/local_mason.sh
|
||||
- source ./scripts/travis_helper.sh
|
||||
- source ./scripts/flags.sh
|
||||
- ./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
|
||||
- if [[ ${TRAVIS_OS_NAME} == "linux" && ${MASON_PLATFORM} != "android" ]]; then export LD_LIBRARY_PATH=`mason prefix mesa 10.3.1`/lib; fi
|
||||
- if [[ ${TRAVIS_OS_NAME} == "linux" && ${MASON_PLATFORM} != "android" ]]; then glxinfo; fi
|
||||
|
||||
install:
|
||||
- make config.gypi
|
||||
- if [[ ${MASON_PLATFORM} != "android" ]]; then make config.gypi; fi
|
||||
- ulimit -c
|
||||
|
||||
before_script:
|
||||
|
||||
33
Makefile
@@ -2,6 +2,7 @@ BUILDTYPE ?= Release
|
||||
PYTHON ?= python
|
||||
V ?= 1
|
||||
PREFIX ?= /usr/local
|
||||
JOBS ?= 1
|
||||
|
||||
ifeq ($(shell uname -s), Darwin)
|
||||
PLATFORM ?= osx
|
||||
@@ -74,9 +75,34 @@ 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: build/bin/render.xcodeproj
|
||||
build/bin/render.xcodeproj: bin/render.gyp config.gypi
|
||||
build/bin/render.xcodeproj: bin/render.gyp config.gypi
|
||||
deps/run_gyp bin/render.gyp -Iconfig.gypi -Dplatform=$(PLATFORM) --depth=. --generator-output=./build -f xcode
|
||||
|
||||
.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`" && \
|
||||
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}" && \
|
||||
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 android/mapboxgl-app.gyp -Iconfig-android.gypi -Dplatform=android --depth=. --generator-output=./build/android -f make-android && \
|
||||
$(MAKE) -C build/android -j$(JOBS) 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 --parallel-threads=$(JOBS) build
|
||||
|
||||
##### Test cases ###############################################################
|
||||
|
||||
test: build/test/Makefile
|
||||
@@ -157,7 +183,10 @@ clean: clear_sqlite_cache 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
|
||||
-rm -rf ./android/java/build ./android/java/app/build ./android/java/lib/build
|
||||
-rm -rf ./android/java/lib/src/main/jniLibs ./android/java/lib/src/main/assets
|
||||
-rm -f ./android/test/features.zip
|
||||
|
||||
distclean: clean
|
||||
-rm -rf ./mason_packages
|
||||
|
||||
62
README.md
@@ -1,7 +1,7 @@
|
||||
[](https://travis-ci.org/mapbox/mapbox-gl-native/builds)
|
||||
|
||||
An OpenGL renderer for [Mapbox Vector Tiles](https://www.mapbox.com/blog/vector-tiles),
|
||||
implemented in C++11, currently targeting iOS, OS X, and Ubuntu Linux.
|
||||
implemented in C++11, currently targeting iOS, OS X, Android, and Ubuntu Linux.
|
||||
|
||||
# Depends
|
||||
|
||||
@@ -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
|
||||
|
||||
Then, you can then proceed to build the library:
|
||||
|
||||
@@ -102,6 +102,48 @@ Set an access token as described below, and then run:
|
||||
|
||||
make run-linux
|
||||
|
||||
## Android (on Linux)
|
||||
|
||||
Install a few build depedencies:
|
||||
|
||||
apt-get install -y make git build-essential automake \
|
||||
libtool make cmake pkg-config lib32stdc++6 lib32z1
|
||||
|
||||
Install Oracle JDK 7 (requires license agreement) from http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
|
||||
|
||||
export JAVA_HOME="/dir/to/jdk1.7.0_71"
|
||||
|
||||
Install the [Android NDK Revision 10d](https://developer.android.com/tools/sdk/ndk/index.html).
|
||||
|
||||
export ANDROID_NDK_PATH="/dir/to/android-ndk-r10d"
|
||||
|
||||
Install the Android SDK. We recommend doing this by way of [Android Studio](https://developer.android.com/sdk/installing/studio.html).
|
||||
|
||||
export ANDROID_HOME="/dir/to/android-sdk-linux"
|
||||
|
||||
Run:
|
||||
|
||||
make android
|
||||
|
||||
You can then open `android/java` in Android Studio via "Import Non-Android Studio Project".
|
||||
|
||||
## Android (on OS X)
|
||||
|
||||
Install Oracle JDK 7 (requires license agreement) from http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
|
||||
|
||||
Install the [Android NDK Revision 10d](https://developer.android.com/tools/sdk/ndk/index.html) for 64-bit OS X.
|
||||
|
||||
export ANDROID_NDK_PATH="/dir/to/android-ndk-r10d"
|
||||
|
||||
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"
|
||||
|
||||
Run:
|
||||
|
||||
make android
|
||||
|
||||
You can then open `android/java` in Android Studio via "Import Non-Android Studio Project".
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
@@ -123,6 +165,8 @@ For iOS and OS X use of the demo apps in Xcode, setup the access token by editin
|
||||
|
||||
For Linux, set the environment variable `MAPBOX_ACCESS_TOKEN` to your token.
|
||||
|
||||
For Android, gradle will take the value of `MAPBOX_ACCESS_TOKEN` and save it to `android/java/app/src/main/res/raw/token.txt` where the app will read it from.
|
||||
|
||||
# Style
|
||||
|
||||
Some styles in JSON format are included at `./styles`. See the [style spec](https://github.com/mapbox/mapbox-gl-style-spec) for more details.
|
||||
|
||||
202
android/LICENSE_COMMONS.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
13
android/LICENSE_GESTURES.md
Normal file
@@ -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.
|
||||
13
android/LICENSE_LOST.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Copyright (c) 2014 Mapzen
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
1024
android/cpp/jni.cpp
Normal file
826
android/cpp/native_map_view.cpp
Normal file
@@ -0,0 +1,826 @@
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <tuple>
|
||||
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
|
||||
#include <mbgl/android/native_map_view.hpp>
|
||||
#include <mbgl/android/jni.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/platform/event.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/platform/gl.hpp>
|
||||
#include <mbgl/util/std.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) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglQueryString(%d) returned error %d", name,
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglQueryString() failed");
|
||||
} else {
|
||||
char buf[513];
|
||||
for (int len = std::strlen(str), pos = 0; len > 0; len -= 512, pos += 512) {
|
||||
strncpy(buf, str + pos, 512);
|
||||
buf[512] = 0;
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "EGL %s: %s", label, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void log_gl_string(GLenum name, const char *label) {
|
||||
const GLubyte *str = glGetString(name);
|
||||
if (str == nullptr) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "glGetString(%d) returned error %d", name,
|
||||
glGetError());
|
||||
throw new std::runtime_error("glGetString() failed");
|
||||
} else {
|
||||
char buf[513];
|
||||
for (int len = std::strlen(reinterpret_cast<const char *>(str)), pos = 0; len > 0;
|
||||
len -= 512, pos += 512) {
|
||||
strncpy(buf, reinterpret_cast<const char *>(str) + pos, 512);
|
||||
buf[512] = 0;
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "GL %s: %s", label, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NativeMapView::NativeMapView(JNIEnv *env, jobject obj_)
|
||||
: mbgl::View(*this),
|
||||
fileSource(mbgl::platform::defaultCacheDatabase()),
|
||||
map(*this, fileSource) {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "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;
|
||||
}
|
||||
}
|
||||
|
||||
NativeMapView::~NativeMapView() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::~NativeMapView");
|
||||
terminateContext();
|
||||
destroySurface();
|
||||
terminateDisplay();
|
||||
|
||||
assert(vm != nullptr);
|
||||
assert(obj != nullptr);
|
||||
|
||||
jint ret;
|
||||
JNIEnv *env = nullptr;
|
||||
ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (ret == JNI_OK) {
|
||||
env->DeleteGlobalRef(obj);
|
||||
} else {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
|
||||
throw new std::runtime_error("GetEnv() failed");
|
||||
}
|
||||
obj = nullptr;
|
||||
vm = nullptr;
|
||||
}
|
||||
|
||||
void NativeMapView::activate() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::activate");
|
||||
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) {
|
||||
if (!eglMakeCurrent(display, surface, surface, context)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglMakeCurrent() failed");
|
||||
}
|
||||
} else {
|
||||
mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready");
|
||||
}
|
||||
}
|
||||
|
||||
void NativeMapView::deactivate() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::deactivate");
|
||||
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglMakeCurrent() failed");
|
||||
}
|
||||
}
|
||||
|
||||
void NativeMapView::swap() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::swap");
|
||||
|
||||
if (map.needsSwap() && (display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
|
||||
if (!eglSwapBuffers(display, surface)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglSwapBuffers() failed");
|
||||
}
|
||||
map.swapped();
|
||||
updateFps();
|
||||
} else {
|
||||
mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready");
|
||||
}
|
||||
}
|
||||
|
||||
void NativeMapView::notify() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notify()");
|
||||
// noop
|
||||
}
|
||||
|
||||
mbgl::Map &NativeMapView::getMap() { return map; }
|
||||
|
||||
mbgl::CachingHTTPFileSource &NativeMapView::getFileSource() { return fileSource; }
|
||||
|
||||
bool NativeMapView::inEmulator() {
|
||||
// Detect if we are in emulator
|
||||
char prop[PROP_VALUE_MAX];
|
||||
__system_property_get("ro.kernel.qemu", prop);
|
||||
return strtol(prop, nullptr, 0) == 1;
|
||||
}
|
||||
|
||||
void NativeMapView::initializeDisplay() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::initializeDisplay");
|
||||
|
||||
assert(display == EGL_NO_DISPLAY);
|
||||
assert(config == nullptr);
|
||||
assert(format < 0);
|
||||
|
||||
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if (display == EGL_NO_DISPLAY) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetDisplay() returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglGetDisplay() failed");
|
||||
}
|
||||
|
||||
EGLint major, minor;
|
||||
if (!eglInitialize(display, &major, &minor)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglInitialize() returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglInitialize() failed");
|
||||
}
|
||||
if ((major <= 1) && (minor < 3)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "EGL version is too low, need 1.3, got %d.%d", major,
|
||||
minor);
|
||||
throw new std::runtime_error("EGL version is too low");
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
// Detect if we are in emulator
|
||||
if (inEmulator()) {
|
||||
mbgl::Log::Warning(mbgl::Event::Android, "In emulator! Enabling hacks :-(");
|
||||
}
|
||||
|
||||
// Get all configs at least RGB 565 with 16 depth and 8 stencil
|
||||
EGLint configAttribs[] = {
|
||||
EGL_CONFIG_CAVEAT, EGL_NONE, EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_BUFFER_SIZE, 16, EGL_RED_SIZE,
|
||||
5, EGL_GREEN_SIZE, 6,
|
||||
EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE,
|
||||
16, EGL_STENCIL_SIZE, 8,
|
||||
(inEmulator() ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT, // Ugly hack
|
||||
(inEmulator() ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER, // Ugly hack
|
||||
EGL_NONE};
|
||||
EGLint numConfigs;
|
||||
if (!eglChooseConfig(display, configAttribs, nullptr, 0, &numConfigs)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglChooseConfig() failed");
|
||||
}
|
||||
if (numConfigs < 1) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs.");
|
||||
throw new std::runtime_error("eglChooseConfig() failed");
|
||||
}
|
||||
|
||||
const std::unique_ptr<EGLConfig[]> configs = mbgl::util::make_unique<EGLConfig[]>(numConfigs);
|
||||
if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglChooseConfig() failed");
|
||||
}
|
||||
|
||||
config = chooseConfig(configs.get(), numConfigs);
|
||||
if (config == nullptr) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen");
|
||||
throw new std::runtime_error("No config chosen");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib() returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Chosen window format is %d", format);
|
||||
}
|
||||
|
||||
void NativeMapView::terminateDisplay() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::terminateDisplay");
|
||||
|
||||
if (display != EGL_NO_DISPLAY) {
|
||||
// Destroy the surface first, if it still exists. This call needs a valid surface.
|
||||
if (surface != EGL_NO_SURFACE) {
|
||||
if (!eglDestroySurface(display, surface)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglDestroySurface() failed");
|
||||
}
|
||||
surface = EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglMakeCurrent() failed");
|
||||
}
|
||||
|
||||
if (!eglTerminate(display)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglTerminate() failed");
|
||||
}
|
||||
}
|
||||
|
||||
display = EGL_NO_DISPLAY;
|
||||
config = nullptr;
|
||||
format = -1;
|
||||
}
|
||||
|
||||
void NativeMapView::initializeContext() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::initializeContext");
|
||||
|
||||
assert(display != EGL_NO_DISPLAY);
|
||||
assert(context == EGL_NO_CONTEXT);
|
||||
assert(config != nullptr);
|
||||
|
||||
const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
|
||||
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
|
||||
if (context == EGL_NO_CONTEXT) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglCreateContext() failed");
|
||||
}
|
||||
}
|
||||
|
||||
void 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)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglMakeCurrent() failed");
|
||||
}
|
||||
|
||||
if (context != EGL_NO_CONTEXT) {
|
||||
if (!eglDestroyContext(display, context)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroyContext() returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglDestroyContext() failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context = EGL_NO_CONTEXT;
|
||||
}
|
||||
|
||||
void NativeMapView::createSurface(ANativeWindow *window_) {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "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 surfaceAttribs[] = {EGL_NONE};
|
||||
surface = eglCreateWindowSurface(display, config, window, surfaceAttribs);
|
||||
if (surface == EGL_NO_SURFACE) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglCreateWindowSurface() failed");
|
||||
}
|
||||
|
||||
if (!firstTime) {
|
||||
firstTime = true;
|
||||
|
||||
if (!eglMakeCurrent(display, surface, surface, context)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglMakeCurrent() failed");
|
||||
}
|
||||
|
||||
log_gl_string(GL_VENDOR, "Vendor");
|
||||
log_gl_string(GL_RENDERER, "Renderer");
|
||||
log_gl_string(GL_VERSION, "Version");
|
||||
if (!inEmulator()) {
|
||||
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)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglMakeCurrent() failed");
|
||||
}
|
||||
}
|
||||
|
||||
resume();
|
||||
}
|
||||
|
||||
void NativeMapView::destroySurface() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::destroySurface");
|
||||
|
||||
pause(true);
|
||||
|
||||
if (surface != EGL_NO_SURFACE) {
|
||||
if (!eglDestroySurface(display, surface)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglDestroySurface() failed");
|
||||
}
|
||||
}
|
||||
|
||||
surface = EGL_NO_SURFACE;
|
||||
|
||||
if (window != nullptr) {
|
||||
ANativeWindow_release(window);
|
||||
window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Speed
|
||||
/*
|
||||
typedef enum {
|
||||
Format16Bit = 0,
|
||||
Format32BitNoAlpha = 1,
|
||||
Format32BitAlpha = 2,
|
||||
Format24Bit = 3,
|
||||
Unknown = 4
|
||||
} BufferFormat;
|
||||
|
||||
typedef enum {
|
||||
Format16Depth8Stencil = 0,
|
||||
Format24Depth8Stencil = 1,
|
||||
} DepthStencilFormat;
|
||||
*/
|
||||
|
||||
// Quality
|
||||
typedef enum {
|
||||
Format16Bit = 3,
|
||||
Format32BitNoAlpha = 1,
|
||||
Format32BitAlpha = 2,
|
||||
Format24Bit = 0,
|
||||
Unknown = 4
|
||||
} BufferFormat;
|
||||
|
||||
typedef enum {
|
||||
Format16Depth8Stencil = 1,
|
||||
Format24Depth8Stencil = 0,
|
||||
} DepthStencilFormat;
|
||||
|
||||
// Tuple is <buffer_format, depth_stencil_format, is_not_conformant, is_caveat, config_num,
|
||||
// config_id>
|
||||
typedef std::tuple<BufferFormat, DepthStencilFormat, bool, bool, int, EGLConfig> ConfigProperties;
|
||||
|
||||
EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfigs) {
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Found %d configs", numConfigs);
|
||||
|
||||
// Create a list of configs that pass our filters
|
||||
std::list<ConfigProperties> configList;
|
||||
for (int i = 0; i < numConfigs; i++) {
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Config %d:", i);
|
||||
|
||||
EGLint caveat, conformant, bits, red, green, blue, alpha, alphaMask, depth, stencil,
|
||||
sampleBuffers, samples;
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alphaMask)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sampleBuffers)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d",
|
||||
eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL,
|
||||
"eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError());
|
||||
throw new std::runtime_error("eglGetConfigAttrib() failed");
|
||||
}
|
||||
|
||||
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", alphaMask);
|
||||
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", sampleBuffers);
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "...Samples: %d", samples);
|
||||
|
||||
bool configOk = true;
|
||||
configOk &= (depth == 24) || (depth == 16);
|
||||
configOk &= stencil == 8;
|
||||
configOk &= sampleBuffers == 0;
|
||||
configOk &= samples == 0;
|
||||
|
||||
// Filter our configs first for depth, stencil and anti-aliasing
|
||||
if (configOk) {
|
||||
// Work out the config's buffer format
|
||||
BufferFormat bufferFormat;
|
||||
if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) {
|
||||
bufferFormat = Format16Bit;
|
||||
} else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
|
||||
bufferFormat = Format32BitNoAlpha;
|
||||
} else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) {
|
||||
bufferFormat = Format32BitAlpha;
|
||||
} else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
|
||||
bufferFormat = Format24Bit;
|
||||
} else {
|
||||
bufferFormat = Unknown;
|
||||
}
|
||||
|
||||
// Work out the config's depth stencil format
|
||||
DepthStencilFormat depthStencilFormat;
|
||||
if ((depth == 16) && (stencil == 8)) {
|
||||
depthStencilFormat = Format16Depth8Stencil;
|
||||
} else {
|
||||
depthStencilFormat = Format24Depth8Stencil;
|
||||
}
|
||||
|
||||
bool isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT;
|
||||
bool isCaveat = caveat != EGL_NONE;
|
||||
EGLConfig configId = configs[i];
|
||||
|
||||
// Ignore formats we don't recognise
|
||||
if (bufferFormat != Unknown) {
|
||||
configList.push_back(std::make_tuple(bufferFormat, depthStencilFormat,
|
||||
isNotConformant, isCaveat, i, configId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (configList.empty()) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "Config list was empty.");
|
||||
}
|
||||
|
||||
// Sort the configs to find the best one
|
||||
configList.sort();
|
||||
usingDepth24 = std::get<1>(configList.front()) == Format24Depth8Stencil;
|
||||
bool isConformant = !std::get<2>(configList.front());
|
||||
bool isCaveat = std::get<3>(configList.front());
|
||||
int configNum = std::get<4>(configList.front());
|
||||
EGLConfig configId = std::get<5>(configList.front());
|
||||
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Chosen config is %d", configNum);
|
||||
|
||||
if (isCaveat) {
|
||||
mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config has a caveat.");
|
||||
}
|
||||
if (!isConformant) {
|
||||
mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config is not conformant.");
|
||||
}
|
||||
|
||||
return configId;
|
||||
}
|
||||
|
||||
void NativeMapView::start() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::start");
|
||||
|
||||
if (display == EGL_NO_DISPLAY) {
|
||||
initializeDisplay();
|
||||
}
|
||||
|
||||
if (context == EGL_NO_CONTEXT) {
|
||||
initializeContext();
|
||||
}
|
||||
|
||||
assert(display != EGL_NO_DISPLAY);
|
||||
assert(context != EGL_NO_CONTEXT);
|
||||
|
||||
map.start(true);
|
||||
}
|
||||
|
||||
void NativeMapView::loadExtensions() {
|
||||
const GLubyte *str = glGetString(GL_EXTENSIONS);
|
||||
if (str == nullptr) {
|
||||
mbgl::Log::Error(mbgl::Event::OpenGL, "glGetString(GL_EXTENSIONS) returned error %d",
|
||||
glGetError());
|
||||
return;
|
||||
}
|
||||
|
||||
std::string extensions(reinterpret_cast<const char *>(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 = reinterpret_cast<gl::PFNGLBINDVERTEXARRAYPROC>(
|
||||
eglGetProcAddress("glBindVertexArrayOES"));
|
||||
gl::DeleteVertexArrays = reinterpret_cast<gl::PFNGLDELETEVERTEXARRAYSPROC>(
|
||||
eglGetProcAddress("glDeleteVertexArraysOES"));
|
||||
gl::GenVertexArrays = reinterpret_cast<gl::PFNGLGENVERTEXARRAYSPROC>(
|
||||
eglGetProcAddress("glGenVertexArraysOES"));
|
||||
gl::IsVertexArray =
|
||||
reinterpret_cast<gl::PFNGLISVERTEXARRAYPROC>(eglGetProcAddress("glIsVertexArrayOES"));
|
||||
assert(gl::BindVertexArray != nullptr);
|
||||
assert(gl::DeleteVertexArrays != nullptr);
|
||||
assert(gl::GenVertexArrays != nullptr);
|
||||
assert(gl::IsVertexArray != nullptr);
|
||||
}
|
||||
|
||||
if (extensions.find("GL_OES_packed_depth_stencil") != std::string::npos) {
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Using GL_OES_packed_depth_stencil.");
|
||||
gl::isPackedDepthStencilSupported = true;
|
||||
}
|
||||
|
||||
if (extensions.find("GL_OES_depth24") != std::string::npos) {
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Using GL_OES_depth24.");
|
||||
if (usingDepth24) {
|
||||
gl::isDepth24Supported = true;
|
||||
} else {
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Preferring 16 bit depth.");
|
||||
}
|
||||
}
|
||||
|
||||
if (extensions.find("GL_KHR_debug") != std::string::npos) {
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Using GL_KHR_debug.");
|
||||
gl::DebugMessageControl = reinterpret_cast<gl::PFNGLDEBUGMESSAGECONTROLPROC>(
|
||||
eglGetProcAddress("glDebugMessageControl"));
|
||||
gl::DebugMessageInsert = reinterpret_cast<gl::PFNGLDEBUGMESSAGEINSERTPROC>(
|
||||
eglGetProcAddress("glDebugMessageInsert"));
|
||||
gl::DebugMessageCallback = reinterpret_cast<gl::PFNGLDEBUGMESSAGECALLBACKPROC>(
|
||||
eglGetProcAddress("glDebugMessageCallback"));
|
||||
gl::GetDebugMessageLog = reinterpret_cast<gl::PFNGLGETDEBUGMESSAGELOGPROC>(
|
||||
eglGetProcAddress("glGetDebugMessageLog"));
|
||||
gl::GetPointerv =
|
||||
reinterpret_cast<gl::PFNGLGETPOINTERVPROC>(eglGetProcAddress("glGetPointerv"));
|
||||
gl::PushDebugGroup =
|
||||
reinterpret_cast<gl::PFNGLPUSHDEBUGGROUPPROC>(eglGetProcAddress("glPushDebugGroup"));
|
||||
gl::PopDebugGroup =
|
||||
reinterpret_cast<gl::PFNGLPOPDEBUGGROUPPROC>(eglGetProcAddress("glPopDebugGroup"));
|
||||
gl::ObjectLabel =
|
||||
reinterpret_cast<gl::PFNGLOBJECTLABELPROC>(eglGetProcAddress("glObjectLabel"));
|
||||
gl::GetObjectLabel =
|
||||
reinterpret_cast<gl::PFNGLGETOBJECTLABELPROC>(eglGetProcAddress("glGetObjectLabel"));
|
||||
gl::ObjectPtrLabel =
|
||||
reinterpret_cast<gl::PFNGLOBJECTPTRLABELPROC>(eglGetProcAddress("glObjectPtrLabel"));
|
||||
gl::GetObjectPtrLabel = reinterpret_cast<gl::PFNGLGETOBJECTPTRLABELPROC>(
|
||||
eglGetProcAddress("glGetObjectPtrLabel"));
|
||||
assert(gl::DebugMessageControl != nullptr);
|
||||
assert(gl::DebugMessageInsert != nullptr);
|
||||
assert(gl::DebugMessageCallback != nullptr);
|
||||
assert(gl::GetDebugMessageLog != nullptr);
|
||||
assert(gl::GetPointerv != nullptr);
|
||||
assert(gl::PushDebugGroup != nullptr);
|
||||
assert(gl::PopDebugGroup != nullptr);
|
||||
assert(gl::ObjectLabel != nullptr);
|
||||
assert(gl::GetObjectLabel != nullptr);
|
||||
assert(gl::ObjectPtrLabel != nullptr);
|
||||
assert(gl::GetObjectPtrLabel != nullptr);
|
||||
} else {
|
||||
if (extensions.find("GL_EXT_debug_marker") != std::string::npos) {
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Using GL_EXT_debug_marker.");
|
||||
gl::InsertEventMarkerEXT = reinterpret_cast<gl::PFNGLINSERTEVENTMARKEREXTPROC>(
|
||||
eglGetProcAddress("glInsertEventMarkerEXT"));
|
||||
gl::PushGroupMarkerEXT = reinterpret_cast<gl::PFNGLPUSHGROUPMARKEREXTPROC>(
|
||||
eglGetProcAddress("glPushGroupMarkerEXT"));
|
||||
gl::PopGroupMarkerEXT = reinterpret_cast<gl::PFNGLPOPGROUPMARKEREXTPROC>(
|
||||
eglGetProcAddress("glPopGroupMarkerEXT"));
|
||||
assert(gl::InsertEventMarkerEXT != nullptr);
|
||||
assert(gl::PushGroupMarkerEXT != nullptr);
|
||||
assert(gl::PopGroupMarkerEXT != nullptr);
|
||||
}
|
||||
|
||||
if (extensions.find("GL_EXT_debug_label") != std::string::npos) {
|
||||
mbgl::Log::Info(mbgl::Event::OpenGL, "Using GL_EXT_debug_label.");
|
||||
gl::LabelObjectEXT = reinterpret_cast<gl::PFNGLLABELOBJECTEXTPROC>(
|
||||
eglGetProcAddress("glLabelObjectEXT"));
|
||||
gl::GetObjectLabelEXT = reinterpret_cast<gl::PFNGLGETOBJECTLABELEXTPROC>(
|
||||
eglGetProcAddress("glGetObjectLabelEXT"));
|
||||
assert(gl::LabelObjectEXT != nullptr);
|
||||
assert(gl::GetObjectLabelEXT != nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NativeMapView::stop() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::stop");
|
||||
|
||||
if ((display != EGL_NO_DISPLAY) && (context != EGL_NO_CONTEXT)) {
|
||||
map.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void NativeMapView::pause(bool waitForPause) {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::pause %s",
|
||||
(waitForPause) ? "true" : "false");
|
||||
|
||||
if ((display != EGL_NO_DISPLAY) && (context != EGL_NO_CONTEXT)) {
|
||||
map.pause(waitForPause);
|
||||
}
|
||||
}
|
||||
|
||||
void NativeMapView::resume() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::resume");
|
||||
|
||||
assert(display != EGL_NO_DISPLAY);
|
||||
assert(context != EGL_NO_CONTEXT);
|
||||
|
||||
if (surface != EGL_NO_SURFACE) {
|
||||
map.resume();
|
||||
} else {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "Not resuming because we are not ready");
|
||||
}
|
||||
}
|
||||
|
||||
void NativeMapView::notifyMapChange(mbgl::MapChange, mbgl::timestamp) {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notifyMapChange()");
|
||||
|
||||
assert(vm != nullptr);
|
||||
assert(obj != nullptr);
|
||||
|
||||
JavaVMAttachArgs args = {JNI_VERSION_1_2, "NativeMapView::notifyMapChange()", NULL};
|
||||
|
||||
jint ret;
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = false;
|
||||
ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (ret != JNI_OK) {
|
||||
if (ret != JNI_EDETACHED) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
|
||||
throw new std::runtime_error("GetEnv() failed");
|
||||
} else {
|
||||
ret = vm->AttachCurrentThread(&env, &args);
|
||||
if (ret != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "AttachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("AttachCurrentThread() failed");
|
||||
}
|
||||
detach = true;
|
||||
}
|
||||
}
|
||||
|
||||
env->CallVoidMethod(obj, onMapChangedId);
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
if (detach) {
|
||||
if ((ret = vm->DetachCurrentThread()) != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "DetachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("DetachCurrentThread() failed");
|
||||
}
|
||||
}
|
||||
env = nullptr;
|
||||
}
|
||||
|
||||
void NativeMapView::enableFps(bool enable) {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::enableFps()");
|
||||
|
||||
fpsEnabled = enable;
|
||||
}
|
||||
|
||||
void NativeMapView::updateFps() {
|
||||
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::updateFps()");
|
||||
|
||||
if (!fpsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
static int frames = 0;
|
||||
static int64_t timeElapsed = 0LL;
|
||||
|
||||
frames++;
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
int64_t currentTime = now.tv_sec * 1000000000LL + now.tv_nsec;
|
||||
|
||||
if (currentTime - timeElapsed >= 1) {
|
||||
fps = frames / ((currentTime - timeElapsed) / 1E9);
|
||||
mbgl::Log::Debug(mbgl::Event::Render, "FPS: %4.2f", fps);
|
||||
timeElapsed = currentTime;
|
||||
frames = 0;
|
||||
}
|
||||
|
||||
assert(vm != nullptr);
|
||||
assert(obj != nullptr);
|
||||
|
||||
JavaVMAttachArgs args = {JNI_VERSION_1_2, "NativeMapView::updateFps()", NULL};
|
||||
|
||||
jint ret;
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = false;
|
||||
ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (ret != JNI_OK) {
|
||||
if (ret != JNI_EDETACHED) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
|
||||
throw new std::runtime_error("GetEnv() failed");
|
||||
} else {
|
||||
ret = vm->AttachCurrentThread(&env, &args);
|
||||
if (ret != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "AttachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("AttachCurrentThread() failed");
|
||||
}
|
||||
detach = true;
|
||||
}
|
||||
}
|
||||
|
||||
env->CallVoidMethod(obj, onFpsChangedId, fps);
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
if (detach) {
|
||||
if ((ret = vm->DetachCurrentThread()) != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "DetachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("DetachCurrentThread() failed");
|
||||
}
|
||||
}
|
||||
env = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
android/java/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Gradle files
|
||||
.gradle/
|
||||
|
||||
# IntelliJ files
|
||||
.idea
|
||||
*.iml
|
||||
|
||||
# Build files
|
||||
build/
|
||||
*/build/
|
||||
*.so
|
||||
*.apk
|
||||
|
||||
# Lib assets
|
||||
lib/src/main/assets/
|
||||
|
||||
# Local settings
|
||||
local.properties
|
||||
|
||||
# Token file
|
||||
app/src/main/res/raw/token.txt
|
||||
63
android/java/app/build.gradle
Normal file
@@ -0,0 +1,63 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
task accessToken {
|
||||
def rawDir = new File("app/src/main/res/raw")
|
||||
rawDir.mkdirs()
|
||||
def tokenFile = new File("app/src/main/res/raw/token.txt")
|
||||
String token = System.getenv('MAPBOX_ACCESS_TOKEN')
|
||||
if (token == null) {
|
||||
throw new InvalidUserDataException("You must set the MAPBOX_ACCESS_TOKEN environment variable.")
|
||||
}
|
||||
tokenFile.write(token)
|
||||
}
|
||||
|
||||
gradle.projectsEvaluated {
|
||||
preBuild.dependsOn('accessToken')
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
buildToolsVersion "21.1.2"
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.mapbox.mapboxgl.app"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 21
|
||||
versionCode 3
|
||||
versionName "0.1.2"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/LICENSE.txt'
|
||||
exclude 'META-INF/NOTICE.txt'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
checkAllWarnings true
|
||||
disable 'UnusedAttribute'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':lib')
|
||||
compile 'com.android.support:support-annotations:21.0.0'
|
||||
compile 'com.android.support:support-v4:21.0.3'
|
||||
compile 'com.android.support:appcompat-v7:21.0.3'
|
||||
compile 'com.mapzen.android:lost:0.2'
|
||||
}
|
||||
34
android/java/app/lint.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
<!-- Ignore errors caused by icon burning -->
|
||||
<issue id="IconLauncherShape" severity="ignore">
|
||||
<ignore path="res/drawable-mdpi/icon.png" />
|
||||
<ignore path="res/drawable-hdpi/icon.png" />
|
||||
<ignore path="res/drawable-xdpi/icon.png" />
|
||||
<ignore path="res/drawable-xxdpi/icon.png" />
|
||||
<ignore path="res/drawable-mdpi/icon_burned.png" />
|
||||
<ignore path="res/drawable-hdpi/icon_burned.png" />
|
||||
<ignore path="res/drawable-xdpi/icon_burned.png" />
|
||||
<ignore path="res/drawable-xxdpi/icon_burned.png" />
|
||||
</issue>
|
||||
<issue id="IconDuplicates" severity="ignore">
|
||||
<ignore path="res/drawable-mdpi/icon.png" />
|
||||
<ignore path="res/drawable-hdpi/icon.png" />
|
||||
<ignore path="res/drawable-xdpi/icon.png" />
|
||||
<ignore path="res/drawable-xxdpi/icon.png" />
|
||||
<ignore path="res/drawable-mdpi/icon_burned.png" />
|
||||
<ignore path="res/drawable-hdpi/icon_burned.png" />
|
||||
<ignore path="res/drawable-xdpi/icon_burned.png" />
|
||||
<ignore path="res/drawable-xxdpi/icon_burned.png" />
|
||||
</issue>
|
||||
<issue id="UnusedResources" severity="ignore">
|
||||
<ignore path="res/drawable-mdpi/icon.png" />
|
||||
<ignore path="res/drawable-hdpi/icon.png" />
|
||||
<ignore path="res/drawable-xdpi/icon.png" />
|
||||
<ignore path="res/drawable-xxdpi/icon.png" />
|
||||
<ignore path="res/drawable-mdpi/icon_burned.png" />
|
||||
<ignore path="res/drawable-hdpi/icon_burned.png" />
|
||||
<ignore path="res/drawable-xdpi/icon_burned.png" />
|
||||
<ignore path="res/drawable-xxdpi/icon_burned.png" />
|
||||
</issue>
|
||||
</lint>
|
||||
24
android/java/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mapbox.mapboxgl.app">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@drawable/icon_burned"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme"
|
||||
android:supportsRtl="true">
|
||||
<activity
|
||||
android:name="com.mapbox.mapboxgl.app.MainActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,270 @@
|
||||
package com.mapbox.mapboxgl.app;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.mapbox.mapboxgl.lib.LonLatZoom;
|
||||
import com.mapbox.mapboxgl.lib.MapView;
|
||||
import com.mapzen.android.lost.LocationClient;
|
||||
import com.mapzen.android.lost.LocationListener;
|
||||
import com.mapzen.android.lost.LocationRequest;
|
||||
|
||||
public class MainActivity extends ActionBarActivity {
|
||||
|
||||
//
|
||||
// Static members
|
||||
//
|
||||
|
||||
// Tag used for logging
|
||||
private static final String TAG = "MainActivity";
|
||||
|
||||
//
|
||||
// Instance members
|
||||
//
|
||||
|
||||
// Holds the MapFragment
|
||||
private MapFragment mMapFragment;
|
||||
|
||||
// The FPS label
|
||||
private TextView mFpsTextView;
|
||||
|
||||
// The compass
|
||||
private ImageView mCompassView;
|
||||
|
||||
// Used for GPS
|
||||
private boolean mIsGpsOn = false;
|
||||
private LocationClient mLocationClient;
|
||||
private LocationListener mLocationListener;
|
||||
private LocationRequest mLocationRequest;
|
||||
|
||||
//
|
||||
// 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_main);
|
||||
mMapFragment = (MapFragment)getSupportFragmentManager().findFragmentById(R.id.fragment_map);
|
||||
mMapFragment.getMap().setOnFpsChangedListener(new MyOnFpsChangedListener());
|
||||
mMapFragment.getMap().setOnMapChangedListener(new MyOnMapChangedListener());
|
||||
|
||||
mFpsTextView = (TextView)findViewById(R.id.view_fps);
|
||||
mFpsTextView.setText("");
|
||||
|
||||
mCompassView = (ImageView)findViewById(R.id.view_compass);
|
||||
mCompassView.setOnClickListener(new CompassOnClickListener());
|
||||
|
||||
// Add a toolbar as the action bar
|
||||
Toolbar mainToolbar = (Toolbar)findViewById(R.id.toolbar_main);
|
||||
setSupportActionBar(mainToolbar);
|
||||
getSupportActionBar().setDisplayShowTitleEnabled(false);
|
||||
|
||||
// Add the spinner to select map styles
|
||||
Spinner styleSpinner = (Spinner)findViewById(R.id.spinner_style);
|
||||
ArrayAdapter styleAdapter = ArrayAdapter.createFromResource(getSupportActionBar().getThemedContext(),
|
||||
R.array.style_list, android.R.layout.simple_spinner_dropdown_item);
|
||||
styleSpinner.setAdapter(styleAdapter);
|
||||
styleSpinner.setOnItemSelectedListener(new StyleSpinnerListener());
|
||||
|
||||
// Prepare for GPS
|
||||
mLocationRequest = LocationRequest.create();
|
||||
mLocationRequest.setInterval(0);
|
||||
mLocationRequest.setSmallestDisplacement(0);
|
||||
|
||||
mLocationClient = new LocationClient(getApplicationContext(), new LocationClient.ConnectionCallbacks() {
|
||||
@Override
|
||||
public void onConnected(Bundle connectionHint) {
|
||||
updateLocation(mLocationClient.getLastLocation());
|
||||
|
||||
mLocationListener = new LocationListener() {
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
//Toast.makeText(getApplicationContext(), "Location: " + location.toString(), Toast.LENGTH_SHORT).show();
|
||||
updateLocation(location);
|
||||
}
|
||||
};
|
||||
|
||||
mLocationClient.requestLocationUpdates(mLocationRequest, mLocationListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected() {
|
||||
mLocationListener = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Called when our app goes into the background
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
Log.v(TAG, "onPause");
|
||||
|
||||
// Cancel GPS
|
||||
if (mIsGpsOn) {
|
||||
mLocationClient.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Called when our app comes into the foreground
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Log.v(TAG, "onResume");
|
||||
|
||||
// Restart GPS
|
||||
// Cancel any outstanding GPS
|
||||
if (mIsGpsOn) {
|
||||
mLocationClient.connect();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Other events
|
||||
//
|
||||
|
||||
// Adds items to the action bar menu
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_main, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
// Called when pressing action bar items
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_gps:
|
||||
// Toggle GPS position updates
|
||||
if (mIsGpsOn) {
|
||||
mIsGpsOn = false;
|
||||
item.setIcon(R.drawable.ic_action_location_searching);
|
||||
mLocationClient.disconnect();
|
||||
|
||||
} else {
|
||||
mIsGpsOn = true;
|
||||
item.setIcon(R.drawable.ic_action_location_found);
|
||||
mLocationClient.connect();
|
||||
}
|
||||
return true;
|
||||
|
||||
case R.id.action_debug:
|
||||
// Toggle debug mode
|
||||
mMapFragment.getMap().toggleDebug();
|
||||
|
||||
// Show the FPS counter
|
||||
if (mMapFragment.getMap().isDebugActive()) {
|
||||
mFpsTextView.setVisibility(View.VISIBLE);
|
||||
mFpsTextView.setText(getResources().getString(R.string.label_fps));
|
||||
} else {
|
||||
mFpsTextView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Handles location updates from GPS
|
||||
private void updateLocation(Location location) {
|
||||
if (location != null) {
|
||||
LonLatZoom coordinate = new LonLatZoom(location.getLongitude(), location.getLatitude(), 16);
|
||||
mMapFragment.getMap().setCenterCoordinate(coordinate, true);
|
||||
}
|
||||
}
|
||||
|
||||
// This class handles style change events
|
||||
private class StyleSpinnerListener implements AdapterView.OnItemSelectedListener {
|
||||
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
switch(position) {
|
||||
// Bright
|
||||
case 0:
|
||||
mMapFragment.getMap().setStyleUrl("asset://styles/styles/bright-v6.json");
|
||||
break;
|
||||
|
||||
// Basic
|
||||
case 1:
|
||||
mMapFragment.getMap().setStyleUrl("asset://styles/styles/basic-v6.json");
|
||||
break;
|
||||
|
||||
// Outdoors
|
||||
case 2:
|
||||
mMapFragment.getMap().setStyleUrl("asset://styles/styles/outdoors-v6.json");
|
||||
break;
|
||||
|
||||
// Satellite
|
||||
case 3:
|
||||
mMapFragment.getMap().setStyleUrl("asset://styles/styles/satellite-v6.json");
|
||||
break;
|
||||
|
||||
// Pencil
|
||||
case 4:
|
||||
mMapFragment.getMap().setStyleUrl("asset://styles/styles/pencil-v6.json");
|
||||
break;
|
||||
|
||||
// Empty
|
||||
case 5:
|
||||
mMapFragment.getMap().setStyleUrl("asset://styles/styles/empty-v6.json");
|
||||
break;
|
||||
|
||||
default:
|
||||
onNothingSelected(parent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
mMapFragment.getMap().setStyleUrl("");
|
||||
}
|
||||
}
|
||||
|
||||
// Called when FPS changes
|
||||
public class MyOnFpsChangedListener implements MapView.OnFpsChangedListener {
|
||||
|
||||
@Override
|
||||
public void onFpsChanged(double fps) {
|
||||
mFpsTextView.setText(getResources().getString(R.string.label_fps) + String.format(" %4.2f", fps));
|
||||
}
|
||||
}
|
||||
|
||||
// Called when map state changes
|
||||
public class MyOnMapChangedListener implements MapView.OnMapChangedListener {
|
||||
|
||||
@Override
|
||||
public void onMapChanged() {
|
||||
mCompassView.setRotation((float)mMapFragment.getMap().getDirection());
|
||||
}
|
||||
}
|
||||
|
||||
// Called when someone presses the compass
|
||||
public class CompassOnClickListener implements View.OnClickListener {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mMapFragment.getMap().resetNorth();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.mapbox.mapboxgl.app;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.mapbox.mapboxgl.lib.MapView;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class MapFragment extends Fragment {
|
||||
|
||||
//
|
||||
// Static members
|
||||
//
|
||||
|
||||
// Tag used for logging
|
||||
private static final String TAG = "MapFragment";
|
||||
|
||||
//
|
||||
// Instance members
|
||||
//
|
||||
|
||||
// The map
|
||||
private MapView mMap;
|
||||
|
||||
//
|
||||
// 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
|
||||
mMap = (MapView) inflater.inflate(R.layout.fragment_main, container, true);
|
||||
|
||||
// Load the access token
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(getActivity().getApplicationContext().getResources().openRawResource(R.raw.token)));
|
||||
String line = reader.readLine();
|
||||
mMap.setAccessToken(line);
|
||||
} catch (IOException e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
// Need to pass on any saved state to the map
|
||||
mMap.onCreate(savedInstanceState);
|
||||
|
||||
// Return the map as the root view
|
||||
return mMap;
|
||||
}
|
||||
|
||||
// Called when the fragment is destroyed
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
Log.v(TAG, "onDestroyView");
|
||||
|
||||
// Need to pass on to view
|
||||
mMap.onDestroy();
|
||||
mMap = null;
|
||||
}
|
||||
|
||||
// Called when the fragment is visible
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
Log.v(TAG, "onStart");
|
||||
|
||||
// Need to pass on to view
|
||||
mMap.onStart();
|
||||
}
|
||||
|
||||
// Called when the fragment is invisible
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
Log.v(TAG, "onStop");
|
||||
|
||||
// Need to pass on to view
|
||||
mMap.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
|
||||
mMap.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
|
||||
mMap.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
|
||||
mMap.onSaveInstanceState(outState);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
//
|
||||
// Property methods
|
||||
//
|
||||
|
||||
public MapView getMap() {
|
||||
return mMap;
|
||||
}
|
||||
}
|
||||
BIN
android/java/app/src/main/res/drawable-hdpi/ic_action_about.png
Normal file
|
After Width: | Height: | Size: 683 B |
|
After Width: | Height: | Size: 722 B |
|
After Width: | Height: | Size: 641 B |
BIN
android/java/app/src/main/res/drawable-hdpi/icon.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
android/java/app/src/main/res/drawable-hdpi/icon_burned.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
android/java/app/src/main/res/drawable-mdpi/compass.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
android/java/app/src/main/res/drawable-mdpi/ic_action_about.png
Normal file
|
After Width: | Height: | Size: 465 B |
|
After Width: | Height: | Size: 484 B |
|
After Width: | Height: | Size: 482 B |
BIN
android/java/app/src/main/res/drawable-mdpi/icon.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
android/java/app/src/main/res/drawable-mdpi/icon_burned.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
android/java/app/src/main/res/drawable-xhdpi/ic_action_about.png
Normal file
|
After Width: | Height: | Size: 860 B |
|
After Width: | Height: | Size: 994 B |
|
After Width: | Height: | Size: 854 B |
BIN
android/java/app/src/main/res/drawable-xhdpi/icon.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
android/java/app/src/main/res/drawable-xhdpi/icon_burned.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
BIN
android/java/app/src/main/res/drawable-xxhdpi/icon.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
android/java/app/src/main/res/drawable-xxhdpi/icon_burned.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
51
android/java/app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar_main"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_style"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<fragment
|
||||
android:name="com.mapbox.mapboxgl.app.MapFragment"
|
||||
android:id="@+id/fragment_map"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="${packageName}.${activityClass}"
|
||||
tools:layout="@layout/fragment_main" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/view_compass"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="end|top"
|
||||
android:padding="10dp"
|
||||
android:contentDescription="@string/compass_desc"
|
||||
android:src="@drawable/compass"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/view_fps"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:text="@string/label_fps"
|
||||
android:padding="10dp" />
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
4
android/java/app/src/main/res/layout/fragment_main.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.mapbox.mapboxgl.lib.MapView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
11
android/java/app/src/main/res/menu/menu_main.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/action_gps"
|
||||
android:icon="@drawable/ic_action_location_searching"
|
||||
android:title="@string/action_gps"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item android:id="@+id/action_debug"
|
||||
android:icon="@drawable/ic_action_about"
|
||||
android:title="@string/action_debug"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
23
android/java/app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Mapbox GL</string>
|
||||
|
||||
<string name="action_gps">Toggle GPS location</string>
|
||||
|
||||
<string name="action_debug">Toggle debug mode</string>
|
||||
|
||||
<string name="label_fps">FPS:</string>
|
||||
|
||||
<string name="compass_desc">Map compass. Click to reset the map rotation to North.</string>
|
||||
|
||||
<string-array name="style_list">
|
||||
<item>Bright</item>
|
||||
<item>Basic</item>
|
||||
<item>Outdoors</item>
|
||||
<item>Satellite</item>
|
||||
<!--<item>Pencil</item>-->
|
||||
<!--<item>Empty</item>-->
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
5
android/java/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="AppBaseTheme" parent="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
<style name="AppTheme" parent="AppBaseTheme" />
|
||||
</resources>
|
||||
9
android/java/build.gradle
Normal file
@@ -0,0 +1,9 @@
|
||||
// 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:1.0.0'
|
||||
}
|
||||
}
|
||||
BIN
android/java/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
android/java/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Sat Dec 06 12:43:10 EST 2014
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
|
||||
164
android/java/gradlew
vendored
Executable file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
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 "$@"
|
||||
90
android/java/gradlew.bat
vendored
Normal file
@@ -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
|
||||
47
android/java/lib/build.gradle
Normal file
@@ -0,0 +1,47 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
buildToolsVersion "21.1.2"
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 21
|
||||
versionCode 3
|
||||
versionName "0.1.2"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
checkAllWarnings true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
jniDebuggable true
|
||||
}
|
||||
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
all*.exclude group: 'commons-logging', module: 'commons-logging'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'commons-validator:commons-validator:1.4.0'
|
||||
compile 'com.android.support:support-annotations:21.0.3'
|
||||
compile 'com.android.support:support-v4:21.0.3'
|
||||
}
|
||||
3
android/java/lib/lint.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<lint>
|
||||
</lint>
|
||||
9
android/java/lib/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mapbox.mapboxgl.lib">
|
||||
|
||||
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.almeros.android.multitouch;
|
||||
|
||||
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 actionCode
|
||||
* @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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.almeros.android.multitouch;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.view.MotionEvent;
|
||||
import com.almeros.android.multitouch.BaseGestureDetector;
|
||||
|
||||
/**
|
||||
* @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 = 0f;
|
||||
float y = 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.almeros.android.multitouch;
|
||||
|
||||
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 / Math.PI);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package com.almeros.android.multitouch;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.almeros.android.multitouch;
|
||||
|
||||
import android.content.Context;
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
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 actionCode
|
||||
* @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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
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 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
|
||||
PointF mCurrFocusInternal = determineFocalPoint(curr);
|
||||
PointF 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 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
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;
|
||||
|
||||
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();
|
||||
float mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
|
||||
float mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
|
||||
|
||||
final float edgeSlop = mEdgeSlop;
|
||||
|
||||
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 > mRightSlopEdge
|
||||
|| y0 > mBottomSlopEdge;
|
||||
boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > mRightSlopEdge
|
||||
|| y1 > mBottomSlopEdge;
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.mapbox.mapboxgl.lib;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class LonLat implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<LonLat> CREATOR = new Parcelable.Creator<LonLat>() {
|
||||
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;
|
||||
}
|
||||
|
||||
protected 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;
|
||||
return Double.doubleToLongBits(lat) == Double.doubleToLongBits(other.lat) && Double.doubleToLongBits(lon) == Double.doubleToLongBits(other.lon);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.mapbox.mapboxgl.lib;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class LonLatZoom extends LonLat implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<LonLatZoom> CREATOR = new Parcelable.Creator<LonLatZoom>() {
|
||||
public LonLatZoom createFromParcel(Parcel in) {
|
||||
return new LonLatZoom(in);
|
||||
}
|
||||
|
||||
public LonLatZoom[] newArray(int size) {
|
||||
return new LonLatZoom[size];
|
||||
}
|
||||
};
|
||||
|
||||
private double zoom;
|
||||
|
||||
public LonLatZoom(double lon, double lat, double zoom) {
|
||||
super(lon, lat);
|
||||
this.zoom = zoom;
|
||||
}
|
||||
|
||||
public LonLatZoom(LonLat lonLat, double zoom) {
|
||||
super(lonLat.getLon(), lonLat.getLat());
|
||||
this.zoom = zoom;
|
||||
}
|
||||
|
||||
private LonLatZoom(Parcel in) {
|
||||
super(in);
|
||||
zoom = in.readDouble();
|
||||
}
|
||||
|
||||
public double getZoom() {
|
||||
return zoom;
|
||||
}
|
||||
|
||||
public void setZoom(double zoom) {
|
||||
this.zoom = zoom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
long temp;
|
||||
temp = super.hashCode();
|
||||
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;
|
||||
return super.equals(obj) && Double.doubleToLongBits(zoom) == Double.doubleToLongBits(other.zoom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LonLatZoom [lon=" + super.getLon() + ", lat=" + super.getLat() + ", zoom=" + zoom
|
||||
+ "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeDouble(zoom);
|
||||
}
|
||||
|
||||
}
|
||||
1211
android/java/lib/src/main/java/com/mapbox/mapboxgl/lib/MapView.java
Normal file
@@ -0,0 +1,554 @@
|
||||
package com.mapbox.mapboxgl.lib;
|
||||
|
||||
import android.view.Surface;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// 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 cachePath, String dataPath, String apkPath) {
|
||||
mMapView = mapView;
|
||||
|
||||
// Create the NativeMapView
|
||||
mNativeMapViewPtr = nativeCreate(cachePath, dataPath, apkPath);
|
||||
}
|
||||
|
||||
//
|
||||
// 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 pause() {
|
||||
nativePause(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
nativeResume(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
nativeRun(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void rerender() {
|
||||
nativeRerender(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
nativeUpdate(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);
|
||||
}
|
||||
|
||||
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 addClass(String clazz) {
|
||||
nativeAddClass(mNativeMapViewPtr, clazz);
|
||||
}
|
||||
|
||||
public void removeClass(String clazz) {
|
||||
nativeRemoveClass(mNativeMapViewPtr, clazz);
|
||||
}
|
||||
|
||||
public boolean hasClass(String clazz) {
|
||||
return nativeHasClass(mNativeMapViewPtr, clazz);
|
||||
}
|
||||
|
||||
public void setClasses(List<String> classes) {
|
||||
nativeSetClasses(mNativeMapViewPtr, classes);
|
||||
}
|
||||
|
||||
public List<String> getClasses() {
|
||||
return nativeGetClasses(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setDefaultTransitionDuration() {
|
||||
setDefaultTransitionDuration(0);
|
||||
}
|
||||
|
||||
public long getDefaultTransitionDuration() {
|
||||
return nativeGetDefaultTransitionDuration(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setDefaultTransitionDuration(long milliseconds) {
|
||||
if (milliseconds < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"durationMilliseconds cannot be negative.");
|
||||
}
|
||||
|
||||
nativeSetDefaultTransitionDuration(mNativeMapViewPtr,
|
||||
milliseconds);
|
||||
}
|
||||
|
||||
public void setStyleUrl(String url) {
|
||||
nativeSetStyleUrl(mNativeMapViewPtr, url);
|
||||
}
|
||||
|
||||
public void setStyleJson(String 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);
|
||||
}
|
||||
|
||||
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 setBearing(double degrees) {
|
||||
setBearing(degrees, 0.0);
|
||||
}
|
||||
|
||||
public void setBearing(double degrees, double duration) {
|
||||
nativeSetBearing(mNativeMapViewPtr, degrees, duration);
|
||||
}
|
||||
|
||||
public void setBearing(double degrees, double cx, double cy) {
|
||||
nativeSetBearing(mNativeMapViewPtr, degrees, cx, cy);
|
||||
}
|
||||
|
||||
public double getBearing() {
|
||||
return nativeGetBearing(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void resetNorth() {
|
||||
nativeResetNorth(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void startRotating() {
|
||||
nativeStartRotating(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void stopRotating() {
|
||||
nativeStopRotating(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
nativeSetDebug(mNativeMapViewPtr, debug);
|
||||
}
|
||||
|
||||
public void toggleDebug() {
|
||||
nativeToggleDebug(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public boolean getDebug() {
|
||||
return nativeGetDebug(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void setReachability(boolean status) {
|
||||
nativeSetReachability(mNativeMapViewPtr, status);
|
||||
}
|
||||
|
||||
//
|
||||
// Callbacks
|
||||
//
|
||||
|
||||
protected void onMapChanged() {
|
||||
mMapView.onMapChanged();
|
||||
}
|
||||
|
||||
protected void onFpsChanged(double fps) {
|
||||
mMapView.onFpsChanged(fps);
|
||||
}
|
||||
|
||||
//
|
||||
// JNI methods
|
||||
//
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
nativeDestroy(mNativeMapViewPtr);
|
||||
mNativeMapViewPtr = 0;
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
private native long nativeCreate(String cachePath, String dataPath, String apkPath);
|
||||
|
||||
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 nativePause(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeResume(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeRun(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeRerender(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeUpdate(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 nativeAddClass(long nativeMapViewPtr, String clazz);
|
||||
|
||||
private native void nativeRemoveClass(long nativeMapViewPtr, String clazz);
|
||||
|
||||
private native boolean nativeHasClass(long nativeMapViewPtr, String clazz);
|
||||
|
||||
private native void nativeSetClasses(long nativeMapViewPtr,
|
||||
List<String> classes);
|
||||
|
||||
private native List<String> nativeGetClasses(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetDefaultTransitionDuration(
|
||||
long nativeMapViewPtr, long milliseconds);
|
||||
|
||||
private native long nativeGetDefaultTransitionDuration(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetStyleUrl(long nativeMapViewPtr, String url);
|
||||
|
||||
private native void nativeSetStyleJson(long nativeMapViewPtr,
|
||||
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,
|
||||
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 nativeSetBearing(long nativeMapViewPtr, double degrees,
|
||||
double duration);
|
||||
|
||||
private native void nativeSetBearing(long nativeMapViewPtr, double degrees,
|
||||
double cx, double cy);
|
||||
|
||||
private native double nativeGetBearing(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeResetNorth(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeStartRotating(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeStopRotating(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetDebug(long nativeMapViewPtr, boolean debug);
|
||||
|
||||
private native void nativeToggleDebug(long nativeMapViewPtr);
|
||||
|
||||
private native boolean nativeGetDebug(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeSetReachability(long nativeMapViewPtr, boolean status);
|
||||
}
|
||||
23
android/java/lib/src/main/res/values/attrs.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<resources>
|
||||
<attr name="centerLongitude" format="float" />
|
||||
<attr name="centerLatitude" format="float" />
|
||||
<attr name="zoomLevel" format="float" />
|
||||
<attr name="direction" format="float" />
|
||||
<attr name="zoomEnabled" format="boolean" />
|
||||
<attr name="scrollEnabled" format="boolean" />
|
||||
<attr name="rotateEnabled" format="boolean" />
|
||||
<attr name="debugActive" format="boolean" />
|
||||
<attr name="styleUrl" format="string" />
|
||||
|
||||
<declare-styleable name="MapView">
|
||||
<attr name="centerLongitude" />
|
||||
<attr name="centerLatitude" />
|
||||
<attr name="zoomLevel" />
|
||||
<attr name="direction" />
|
||||
<attr name="zoomEnabled" />
|
||||
<attr name="scrollEnabled" />
|
||||
<attr name="rotateEnabled" />
|
||||
<attr name="debugActive" />
|
||||
<attr name="styleUrl" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
2
android/java/settings.gradle
Normal file
@@ -0,0 +1,2 @@
|
||||
include ':app'
|
||||
include ':lib'
|
||||
64
android/mapboxgl-app.gyp
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
'includes': [
|
||||
'../gyp/common.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'androidapp',
|
||||
'product_name': 'mapbox-gl',
|
||||
'type': 'shared_library',
|
||||
'sources': [
|
||||
'./cpp/native_map_view.cpp',
|
||||
'./cpp/jni.cpp',
|
||||
],
|
||||
'cflags_cc': [
|
||||
'-I<(boost_root)/include',
|
||||
],
|
||||
'libraries': [
|
||||
'<@(openssl_static_libs)',
|
||||
'<@(curl_static_libs)',
|
||||
'<@(png_static_libs)',
|
||||
'<@(jpeg_static_libs)',
|
||||
'<@(sqlite3_static_libs)',
|
||||
'<@(uv_static_libs)',
|
||||
'<@(nu_static_libs)',
|
||||
'<@(zip_static_libs)',
|
||||
],
|
||||
'variables': {
|
||||
'ldflags': [
|
||||
'-llog',
|
||||
'-landroid',
|
||||
'-lEGL',
|
||||
'-lGLESv2',
|
||||
'-lstdc++',
|
||||
'-latomic',
|
||||
'<@(png_ldflags)',
|
||||
'<@(jpeg_ldflags)',
|
||||
'<@(sqlite3_ldflags)',
|
||||
'<@(openssl_ldflags)',
|
||||
'<@(curl_ldflags)',
|
||||
'<@(zlib_ldflags)',
|
||||
'<@(zip_ldflags)',
|
||||
],
|
||||
},
|
||||
'conditions': [
|
||||
['OS == "mac"', {
|
||||
'xcode_settings': {
|
||||
'OTHER_LDFLAGS': [ '<@(ldflags)' ],
|
||||
}
|
||||
}, {
|
||||
'libraries': [ '<@(ldflags)' ],
|
||||
}]
|
||||
],
|
||||
'dependencies': [
|
||||
'../mapboxgl.gyp:mbgl-standalone',
|
||||
'../mapboxgl.gyp:mbgl-android',
|
||||
'../mapboxgl.gyp:copy_certificate_bundle',
|
||||
],
|
||||
'copies': [{
|
||||
'files': [ '../styles' ],
|
||||
'destination': '<(PRODUCT_DIR)'
|
||||
}],
|
||||
},
|
||||
],
|
||||
}
|
||||
8
android/scripts/build-debug.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
export BUILDTYPE=Debug
|
||||
|
||||
./android/scripts/common.sh $1
|
||||
9
android/scripts/build-release.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
export BUILDTYPE=Release
|
||||
export TESTMUNK=yes
|
||||
|
||||
./android/scripts/common.sh $1
|
||||
24
android/scripts/common.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
NAME=$1
|
||||
export JOBS=2
|
||||
|
||||
export CC=clang CXX=clang++
|
||||
export CXXFLAGS="-Wno-unknown-warning-option -Wno-unused-local-typedefs -Wno-unknown-pragmas ${CXXFLAGS}";
|
||||
|
||||
export MASON_PLATFORM=android
|
||||
|
||||
mkdir ./android/java/app/src/main/res/raw/
|
||||
echo "${MAPBOX_ACCESS_TOKEN}" >> ./android/java/app/src/main/res/raw/token.txt
|
||||
|
||||
make android BUILDTYPE=$BUILDTYPE JOBS=$JOBS
|
||||
|
||||
aws s3 cp ./android/java/app/build/outputs/apk/app-debug.apk s3://mapbox-gl-testing/android/${NAME}/app-debug.apk
|
||||
aws s3 cp ./android/java/app/build/outputs/apk/app-release-unsigned.apk s3://mapbox-gl-testing/android/${NAME}/app-release-unsigned.apk
|
||||
|
||||
if [[ $TESTMUNK == "yes" ]]; then
|
||||
./android/scripts/upload-testmunk.sh ./android/java/app/build/outputs/apk/app-debug.apk
|
||||
fi
|
||||
82
android/scripts/run-build.sh
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
cwd=$(pwd)
|
||||
|
||||
region="us-east-1"
|
||||
region_ami="ami-f0ed9098"
|
||||
sleep=10
|
||||
instance_name="android-gl-build-$TRAVIS_REPO_SLUG-$TRAVIS_JOB_NUMBER"
|
||||
echo $ami_name
|
||||
|
||||
NAME=$TRAVIS_JOB_ID
|
||||
|
||||
user_data="#!/bin/bash
|
||||
cd /android
|
||||
git clone git://github.com/mapbox/mapbox-gl-native.git
|
||||
|
||||
pushd mapbox-gl-native
|
||||
if [[ $TRAVIS_PULL_REQUEST == 'false' ]]; then
|
||||
git checkout $TRAVIS_COMMIT
|
||||
else
|
||||
git fetch origin +refs/pull/$TRAVIS_PULL_REQUEST/merge:
|
||||
git checkout FETCH_HEAD
|
||||
fi
|
||||
git submodule update --init --recursive
|
||||
|
||||
export ANDROID_NDK_PATH=/android/android-ndk-r10d
|
||||
export JAVA_HOME=/android/jdk1.7.0_71
|
||||
export ANDROID_HOME=/android/android-sdk-linux
|
||||
export PATH=\$PATH:/android/jdk1.7.0_71/bin
|
||||
export MAPBOX_ACCESS_TOKEN=$MAPBOX_ACCESS_TOKEN
|
||||
export TESTMUNK_KEY=$TESTMUNK_KEY
|
||||
|
||||
if ./android/scripts/build-$CONFIG.sh $NAME &>../build.log; then
|
||||
echo 'ANDROID BUILD PASSED'
|
||||
else
|
||||
echo 'ANDROID BUILD FAILED'
|
||||
fi
|
||||
popd
|
||||
|
||||
aws s3 cp --region us-east-1 build.log s3://mapbox-gl-testing/android/${NAME}/build-log.txt
|
||||
shutdown -P now"
|
||||
|
||||
id=$(aws ec2 run-instances \
|
||||
--region $region \
|
||||
--image-id $region_ami \
|
||||
--count 1 \
|
||||
--instance-type m3.large \
|
||||
--iam-instance-profile Arn="$ANDROID_BUILD_INSTANCE_PROFILE" \
|
||||
--instance-initiated-shutdown-behavior terminate \
|
||||
--user-data "$user_data" | jq -r '.Instances[0].InstanceId')
|
||||
|
||||
echo "Instance: $region $id"
|
||||
|
||||
sleep 10
|
||||
|
||||
result=$(aws ec2 create-tags --region $region --resources $id --tags "Key=Name,Value=$instance_name")
|
||||
|
||||
instance_status_terminated=$(aws ec2 describe-instances --region $region --instance-id $id | jq -r '.Reservations[0].Instances[0].State.Name')
|
||||
|
||||
until [ "$instance_status_terminated" = "terminated" ]; do
|
||||
instance_status_terminated=$(aws ec2 describe-instances --region $region --instance-id $id | jq -r '.Reservations[0].Instances[0].State.Name')
|
||||
echo "Instance terminating status $region $id: $instance_status_terminated"
|
||||
sleep $sleep
|
||||
done
|
||||
|
||||
echo "Build finished"
|
||||
|
||||
output=$(aws ec2 get-console-output --region $region --instance-id $id | jq -r '.Output')
|
||||
|
||||
aws s3 cp --region $region s3://mapbox-gl-testing/android/${NAME}/build-log.txt build.log
|
||||
cat build.log
|
||||
|
||||
if [[ $output == *"ANDROID BUILD PASSED"* ]]; then
|
||||
echo "ANDROID BUILD PASSED"
|
||||
exit 0
|
||||
else
|
||||
echo "ANDROID BUILD FAILED"
|
||||
exit 1
|
||||
fi
|
||||
25
android/scripts/upload-testmunk.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
EMAIL="leith@mapbox.com"
|
||||
APP_NAME="Mapbox"
|
||||
APK_PATH=$1
|
||||
|
||||
echo "submitting testrun to testmunk"
|
||||
|
||||
echo "uploading features..."
|
||||
|
||||
zip -r android/test/features.zip android/test/features/
|
||||
RESPONSE=$(curl -f -H "Accept: application/vnd.testmunk.v1+json" -F "file=@android/test/features.zip" "https://${TESTMUNK_KEY}@api.testmunk.com/apps/${APP_NAME}/testcases")
|
||||
|
||||
echo "uploading apk..."
|
||||
|
||||
RESPONSE=$(curl -f -H "Accept: application/vnd.testmunk.v1+json" -F "file=@${APK_PATH}" -F "email=${EMAIL}" -F "autoStart=true" -F "public=true" "https://${TESTMUNK_KEY}@api.testmunk.com/apps/${APP_NAME}/testruns")
|
||||
|
||||
TESTRUN_ID=$(echo "${RESPONSE}" | jq -r '.id')
|
||||
TESTRUN_NAME=$(echo "${RESPONSE}" | jq -r '.name')
|
||||
|
||||
echo "successully uploaded to Testmunk as '${TESTRUN_NAME}'"
|
||||
echo "public link to test results: https://www.testmunk.com/testrun/${TESTRUN_ID}"
|
||||
1
android/test/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
features.zip
|
||||
5
android/test/features/load-app.feature
Normal file
@@ -0,0 +1,5 @@
|
||||
Feature: Testapp V.1.2
|
||||
|
||||
Scenario: 1) Start app and load map
|
||||
Given I await for 5 seconds
|
||||
|
||||
1
android/test/features/step_definitions/calabash_steps.rb
Executable file
@@ -0,0 +1 @@
|
||||
require 'calabash-android/calabash_steps'
|
||||
6
android/test/features/step_definitions/my_steps.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
Then /^I await for ([\d\.]+) second(?:s)?$/ do |num_seconds|
|
||||
num_seconds = num_seconds.to_f
|
||||
sleep num_seconds
|
||||
|
||||
end
|
||||
36
android/test/features/support/app_installation_hooks.rb
Executable file
@@ -0,0 +1,36 @@
|
||||
require 'calabash-android/management/app_installation'
|
||||
|
||||
AfterConfiguration do |config|
|
||||
FeatureNameMemory.feature_name = nil
|
||||
end
|
||||
|
||||
Before do |scenario|
|
||||
@scenario_is_outline = (scenario.class == Cucumber::Ast::OutlineTable::ExampleRow)
|
||||
if @scenario_is_outline
|
||||
scenario = scenario.scenario_outline
|
||||
end
|
||||
|
||||
feature_name = scenario.feature.title
|
||||
if FeatureNameMemory.feature_name != feature_name \
|
||||
or ENV["RESET_BETWEEN_SCENARIOS"] == "1"
|
||||
if ENV["RESET_BETWEEN_SCENARIOS"] == "1"
|
||||
log "New scenario - reinstalling apps"
|
||||
else
|
||||
log "First scenario in feature - reinstalling apps"
|
||||
end
|
||||
|
||||
uninstall_apps
|
||||
install_app(ENV["TEST_APP_PATH"])
|
||||
install_app(ENV["APP_PATH"])
|
||||
FeatureNameMemory.feature_name = feature_name
|
||||
FeatureNameMemory.invocation = 1
|
||||
else
|
||||
FeatureNameMemory.invocation += 1
|
||||
end
|
||||
end
|
||||
|
||||
FeatureNameMemory = Class.new
|
||||
class << FeatureNameMemory
|
||||
@feature_name = nil
|
||||
attr_accessor :feature_name, :invocation
|
||||
end
|
||||
13
android/test/features/support/app_life_cycle_hooks.rb
Executable file
@@ -0,0 +1,13 @@
|
||||
require 'calabash-android/management/adb'
|
||||
require 'calabash-android/operations'
|
||||
|
||||
Before do |scenario|
|
||||
start_test_server_in_background
|
||||
end
|
||||
|
||||
After do |scenario|
|
||||
if scenario.failed?
|
||||
screenshot_embed
|
||||
end
|
||||
shutdown_test_server
|
||||
end
|
||||
1
android/test/features/support/env.rb
Executable file
@@ -0,0 +1 @@
|
||||
require 'calabash-android/cucumber'
|
||||
0
android/test/features/support/hooks.rb
Executable file
BIN
common/icon.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
25
configure
vendored
@@ -24,6 +24,18 @@ case ${MASON_PLATFORM} in
|
||||
ZLIB_VERSION=system
|
||||
BOOST_VERSION=system
|
||||
;;
|
||||
'android')
|
||||
SQLITE_VERSION=3.8.6
|
||||
LIBPNG_VERSION=1.6.13
|
||||
LIBJPEG_VERSION=v8d
|
||||
OPENSSL_VERSION=1.0.1i
|
||||
LIBCURL_VERSION=7.38.0
|
||||
LIBUV_VERSION=0.11.29
|
||||
ZLIB_VERSION=system
|
||||
BOOST_VERSION=1.57.0
|
||||
NUNICODE_VERSION=1.5.1
|
||||
LIBZIP_VERSION=0.11.2
|
||||
;;
|
||||
*)
|
||||
GLFW_VERSION=e1ae9af5
|
||||
SQLITE_VERSION=3.8.6
|
||||
@@ -69,6 +81,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
|
||||
@@ -127,6 +146,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+=" }
|
||||
}
|
||||
|
||||
@@ -32,6 +32,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',
|
||||
'-Wno-error=unused-parameter',
|
||||
],
|
||||
}],
|
||||
|
||||
83
gyp/mbgl-android.gypi
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
'targets': [
|
||||
{ 'target_name': 'mbgl-android',
|
||||
'product_name': 'mbgl-android',
|
||||
'type': 'static_library',
|
||||
'standalone_static_library': 1,
|
||||
'hard_dependency': 1,
|
||||
'dependencies': [
|
||||
'version',
|
||||
],
|
||||
'variables': {
|
||||
'cflags_cc': [
|
||||
'<@(png_cflags)',
|
||||
'<@(jpeg_cflags)',
|
||||
'<@(uv_cflags)',
|
||||
'<@(curl_cflags)',
|
||||
'<@(nu_cflags)',
|
||||
'<@(zip_cflags)',
|
||||
'<@(openssl_cflags)',
|
||||
'-I<(boost_root)/include',
|
||||
],
|
||||
'cflags': [
|
||||
'<@(uv_cflags)',
|
||||
'<@(nu_cflags)',
|
||||
],
|
||||
'ldflags': [
|
||||
'<@(png_ldflags)',
|
||||
'<@(jpeg_ldflags)',
|
||||
'<@(uv_ldflags)',
|
||||
'<@(curl_ldflags)',
|
||||
'<@(nu_ldflags)',
|
||||
'<@(zip_ldflags)',
|
||||
],
|
||||
},
|
||||
'sources': [
|
||||
'../platform/android/cache_database_data.cpp',
|
||||
'../platform/android/shader_cache_data.cpp',
|
||||
'../platform/android/log_android.cpp',
|
||||
'../platform/android/asset_request_libzip.cpp',
|
||||
'../platform/default/string_stdlib.cpp',
|
||||
'../platform/default/http_request_baton_curl.cpp',
|
||||
'../platform/default/image.cpp',
|
||||
'../platform/default/image_reader.cpp',
|
||||
'../platform/default/png_reader.cpp',
|
||||
'../platform/default/jpeg_reader.cpp',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../include',
|
||||
'../src',
|
||||
],
|
||||
'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)' ],
|
||||
}]
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -12,5 +12,9 @@
|
||||
'includes': ['mbgl-linux.gypi'],
|
||||
'variables': { 'platform_library': 'mbgl-linux' },
|
||||
}],
|
||||
['platform == "android"', {
|
||||
'includes': ['mbgl-android.gypi'],
|
||||
'variables': { 'platform_library': 'mbgl-android' },
|
||||
}],
|
||||
],
|
||||
}
|
||||
|
||||
41
include/mbgl/android/jni.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef MBGL_ANDROID_JNI
|
||||
#define MBGL_ANDROID_JNI
|
||||
|
||||
#include <string>
|
||||
#include <jni.h>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
extern std::string cachePath;
|
||||
extern std::string dataPath;
|
||||
extern std::string apkPath;
|
||||
extern std::string androidRelease;
|
||||
|
||||
extern jmethodID onMapChangedId;
|
||||
extern jmethodID onFpsChangedId;
|
||||
|
||||
extern jclass lonLatClass;
|
||||
extern jmethodID lonLatConstructorId;
|
||||
extern jfieldID lonLatLonId;
|
||||
extern jfieldID lonLatLatId;
|
||||
|
||||
extern jclass lonLatZoomClass;
|
||||
extern jmethodID lonLatZoomConstructorId;
|
||||
extern jfieldID lonLatZoomLonId;
|
||||
extern jfieldID lonLatZoomLatId;
|
||||
extern jfieldID lonLatZoomZoomId;
|
||||
|
||||
extern jclass runtimeExceptionClass;
|
||||
extern jclass nullPointerExceptionClass;
|
||||
;
|
||||
|
||||
extern jmethodID listToArrayId;
|
||||
|
||||
extern jclass arrayListClass;
|
||||
extern jmethodID arrayListConstructorId;
|
||||
extern jmethodID arrayListAddId;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
87
include/mbgl/android/native_map_view.hpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef MBGL_ANDROID_NATIVE_MAP_VIEW
|
||||
#define MBGL_ANDROID_NATIVE_MAP_VIEW
|
||||
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/map/view.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/storage/caching_http_file_source.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <jni.h>
|
||||
#include <android/native_window.h>
|
||||
#include <EGL/egl.h>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class NativeMapView : public mbgl::View, private mbgl::util::noncopyable {
|
||||
public:
|
||||
NativeMapView(JNIEnv *env, jobject obj);
|
||||
virtual ~NativeMapView();
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
void swap() override;
|
||||
|
||||
void notify() override;
|
||||
void notifyMapChange(mbgl::MapChange change, mbgl::timestamp delay) override;
|
||||
|
||||
mbgl::Map &getMap();
|
||||
mbgl::CachingHTTPFileSource &getFileSource();
|
||||
|
||||
void initializeDisplay();
|
||||
void terminateDisplay();
|
||||
|
||||
void initializeContext();
|
||||
void terminateContext();
|
||||
|
||||
void createSurface(ANativeWindow *window);
|
||||
void destroySurface();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void resume();
|
||||
void pause(bool waitForPause = false);
|
||||
|
||||
void enableFps(bool enable);
|
||||
void updateFps();
|
||||
|
||||
private:
|
||||
EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs);
|
||||
|
||||
void loadExtensions();
|
||||
|
||||
bool inEmulator();
|
||||
|
||||
private:
|
||||
JavaVM *vm = nullptr;
|
||||
jobject obj = nullptr;
|
||||
|
||||
ANativeWindow *window = nullptr;
|
||||
|
||||
mbgl::CachingHTTPFileSource fileSource;
|
||||
mbgl::Map map;
|
||||
|
||||
EGLDisplay display = EGL_NO_DISPLAY;
|
||||
EGLSurface surface = EGL_NO_SURFACE;
|
||||
EGLContext context = EGL_NO_CONTEXT;
|
||||
|
||||
EGLConfig config = nullptr;
|
||||
EGLint format = -1;
|
||||
|
||||
std::string styleUrl;
|
||||
std::string apiKey;
|
||||
|
||||
bool firstTime = false;
|
||||
|
||||
bool usingDepth24 = false;
|
||||
|
||||
bool fpsEnabled = false;
|
||||
double fps = 0.0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <iosfwd>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -38,7 +40,7 @@ public:
|
||||
~Map();
|
||||
|
||||
// Start the map render thread. It is asynchronous.
|
||||
void start();
|
||||
void start(bool startPaused = 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,6 +48,12 @@ public:
|
||||
// this will be a busy waiting loop.
|
||||
void stop(std::function<void ()> callback = std::function<void ()>());
|
||||
|
||||
// Pauses the render thread. The render thread will stop running but will not be terminated and will not lose state until resumed.
|
||||
void pause(bool waitForPause = false);
|
||||
|
||||
// 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.
|
||||
@@ -66,7 +74,7 @@ public:
|
||||
|
||||
// Size
|
||||
void resize(uint16_t width, uint16_t height, float ratio = 1);
|
||||
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width, uint16_t fb_height);
|
||||
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight);
|
||||
|
||||
// Styling
|
||||
void addClass(const std::string&);
|
||||
@@ -76,6 +84,7 @@ public:
|
||||
std::vector<std::string> getClasses() const;
|
||||
|
||||
void setDefaultTransitionDuration(uint64_t milliseconds = 0);
|
||||
uint64_t getDefaultTransitionDuration();
|
||||
void setStyleURL(const std::string &url);
|
||||
void setStyleJSON(std::string newStyleJSON, const std::string &base = "");
|
||||
std::string getStyleJSON() const;
|
||||
@@ -126,6 +135,9 @@ private:
|
||||
util::ptr<Sprite> getSprite();
|
||||
uv::worker& getWorker();
|
||||
|
||||
// Checks if render thread needs to pause
|
||||
void checkForPause();
|
||||
|
||||
// Setup
|
||||
void setup();
|
||||
|
||||
@@ -154,6 +166,14 @@ private:
|
||||
std::unique_ptr<uv::async> asyncTerminate;
|
||||
std::unique_ptr<uv::async> asyncRender;
|
||||
|
||||
bool terminating = false;
|
||||
bool pausing = false;
|
||||
bool isPaused = false;
|
||||
std::mutex mutexRun;
|
||||
std::condition_variable condRun;
|
||||
std::mutex mutexPause;
|
||||
std::condition_variable condPause;
|
||||
|
||||
// If cleared, the next time the render thread attempts to render the map, it will *actually*
|
||||
// render the map.
|
||||
std::atomic_flag isClean = ATOMIC_FLAG_INIT;
|
||||
@@ -195,6 +215,8 @@ private:
|
||||
std::string styleJSON = "";
|
||||
std::vector<std::string> classes;
|
||||
|
||||
std::atomic_uint_fast64_t defaultTransitionDuration;
|
||||
|
||||
bool debug = false;
|
||||
timestamp animationTime = 0;
|
||||
|
||||
|
||||
24
include/mbgl/platform/android/log_android.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef MBGL_PLATFORM_ANDROID_LOG_ANDROID
|
||||
#define MBGL_PLATFORM_ANDROID_LOG_ANDROID
|
||||
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class AndroidLogBackend : public LogBackend {
|
||||
private:
|
||||
int severityToPriority(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
|
||||
@@ -32,7 +32,11 @@ enum class Event : uint8_t {
|
||||
Database,
|
||||
HttpRequest,
|
||||
Sprite,
|
||||
Image,
|
||||
OpenGL,
|
||||
JNI,
|
||||
Android,
|
||||
Crash
|
||||
};
|
||||
|
||||
MBGL_DEFINE_ENUM_CLASS(EventClass, Event, {
|
||||
@@ -45,7 +49,11 @@ MBGL_DEFINE_ENUM_CLASS(EventClass, Event, {
|
||||
{ Event::Database, "Database" },
|
||||
{ Event::HttpRequest, "HttpRequest" },
|
||||
{ Event::Sprite, "Sprite" },
|
||||
{ Event::Image, "Image" },
|
||||
{ Event::OpenGL, "OpenGL" },
|
||||
{ Event::JNI, "JNI" },
|
||||
{ Event::Android, "Android" },
|
||||
{ Event::Crash, "Crash" },
|
||||
{ Event(-1), "Unknown" },
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
#else
|
||||
#error Unsupported Apple platform
|
||||
#endif
|
||||
#elif MBGL_USE_GLES2
|
||||
#elif __ANDROID__
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#else
|
||||
@@ -35,6 +36,13 @@ struct Error : ::std::runtime_error {
|
||||
const GLenum code;
|
||||
};
|
||||
|
||||
void checkError(const char *cmd, const char *file, int line);
|
||||
|
||||
#if defined(DEBUG)
|
||||
#define MBGL_CHECK_ERROR(cmd) ([&]() { struct _ { inline ~_() { ::mbgl::gl::checkError(#cmd, __FILE__, __LINE__); } } _; return cmd; }())
|
||||
#else
|
||||
#define MBGL_CHECK_ERROR(cmd) (cmd)
|
||||
#endif
|
||||
|
||||
// GL_KHR_debug / GL_ARB_debug_output
|
||||
#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
|
||||
@@ -136,6 +144,14 @@ extern PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays;
|
||||
extern PFNGLGENVERTEXARRAYSPROC GenVertexArrays;
|
||||
extern PFNGLISVERTEXARRAYPROC IsVertexArray;
|
||||
|
||||
// GL_EXT_packed_depth_stencil / GL_OES_packed_depth_stencil
|
||||
extern bool isPackedDepthStencilSupported;
|
||||
#define GL_DEPTH24_STENCIL8 0x88F0
|
||||
|
||||
// GL_OES_depth24
|
||||
extern bool isDepth24Supported;
|
||||
#define GL_DEPTH_COMPONENT24 0x81A6
|
||||
|
||||
// GL_ARB_get_program_binary / GL_OES_get_program_binary
|
||||
#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257
|
||||
#define GL_PROGRAM_BINARY_LENGTH 0x8741
|
||||
@@ -153,9 +169,9 @@ extern PFNGLPROGRAMPARAMETERIPROC ProgramParameteri;
|
||||
// static int indent = 0;
|
||||
inline void start_group(const std::string &str) {
|
||||
if (gl::PushDebugGroup != nullptr) {
|
||||
gl::PushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(str.size()), str.c_str());
|
||||
MBGL_CHECK_ERROR(gl::PushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(str.size()), str.c_str()));
|
||||
} else if (gl::PushGroupMarkerEXT != nullptr) {
|
||||
gl::PushGroupMarkerEXT(GLsizei(str.size()), str.c_str());
|
||||
MBGL_CHECK_ERROR(gl::PushGroupMarkerEXT(GLsizei(str.size()), str.c_str()));
|
||||
}
|
||||
// fprintf(stderr, "%s%s\n", std::string(indent * 4, ' ').c_str(), str.c_str());
|
||||
// indent++;
|
||||
@@ -163,9 +179,9 @@ inline void start_group(const std::string &str) {
|
||||
|
||||
inline void end_group() {
|
||||
if (gl::PopDebugGroup != nullptr) {
|
||||
gl::PopDebugGroup();
|
||||
MBGL_CHECK_ERROR(gl::PopDebugGroup());
|
||||
} else if (gl::PopGroupMarkerEXT != nullptr) {
|
||||
gl::PopGroupMarkerEXT();
|
||||
MBGL_CHECK_ERROR(gl::PopGroupMarkerEXT());
|
||||
}
|
||||
// indent--;
|
||||
}
|
||||
@@ -174,14 +190,11 @@ inline void start_group(const std::string &) {}
|
||||
inline void end_group() {}
|
||||
#endif
|
||||
|
||||
|
||||
struct group {
|
||||
inline group(const std::string &str) { start_group(str); }
|
||||
~group() { end_group(); };
|
||||
};
|
||||
|
||||
void checkError(const char *cmd, const char *file, int line);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,10 +203,4 @@ void checkError(const char *cmd, const char *file, int line);
|
||||
#define glDepthRange glDepthRangef
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG)
|
||||
#define MBGL_CHECK_ERROR(cmd) ([&]() { struct _ { inline ~_() { ::mbgl::gl::checkError(#cmd, __FILE__, __LINE__); } } _; return cmd; }())
|
||||
#else
|
||||
#define MBGL_CHECK_ERROR(cmd) (cmd)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
38
include/mbgl/storage/asset_request_baton.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef MBGL_STORAGE_ASSET_REQUEST_BATON
|
||||
#define MBGL_STORAGE_ASSET_REQUEST_BATON
|
||||
|
||||
#include <mbgl/util/uv.hpp>
|
||||
#include <thread>
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class AssetRequest;
|
||||
|
||||
struct AssetRequestBaton {
|
||||
AssetRequestBaton(AssetRequest *request_, const std::string &path, uv_loop_t *loop);
|
||||
|
||||
const std::thread::id threadId;
|
||||
AssetRequest *request = nullptr;
|
||||
std::unique_ptr<uv::async> asyncRun;
|
||||
std::string path;
|
||||
bool canceled = false;
|
||||
|
||||
void cancel();
|
||||
static void notifyError(AssetRequestBaton *ptr, const int code, const char *message);
|
||||
static void notifySuccess(AssetRequestBaton *ptr, const std::string body);
|
||||
static void cleanup(AssetRequestBaton *ptr);
|
||||
|
||||
// IMPLEMENT THIS PLATFORM SPECIFIC FUNCTION:
|
||||
|
||||
// Called to load the asset. Platform-specific implementation.
|
||||
static void run(AssetRequestBaton *ptr);
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -29,6 +29,9 @@ public:
|
||||
// Set the Mapbox API access token
|
||||
void setAccessToken(std::string);
|
||||
|
||||
// Get the Mapbox API access token
|
||||
std::string getAccessToken() const;
|
||||
|
||||
std::unique_ptr<Request> request(ResourceType type, const std::string &url);
|
||||
|
||||
void prepare(std::function<void()> fn);
|
||||
@@ -37,7 +40,7 @@ public:
|
||||
void setReachability(bool reachable);
|
||||
|
||||
private:
|
||||
std::thread::id thread_id;
|
||||
std::thread::id threadId;
|
||||
|
||||
// Mapbox API access token.
|
||||
std::string accessToken;
|
||||
|
||||
@@ -48,7 +48,7 @@ struct HTTPRequestBaton {
|
||||
HTTPRequestBaton(const std::string &path);
|
||||
~HTTPRequestBaton();
|
||||
|
||||
const std::thread::id thread_id;
|
||||
const std::thread::id threadId;
|
||||
const std::string path;
|
||||
|
||||
HTTPRequest *request = nullptr;
|
||||
|
||||
@@ -16,6 +16,8 @@ class rwlock;
|
||||
class loop;
|
||||
class async;
|
||||
class worker;
|
||||
class mutex;
|
||||
class cond;
|
||||
|
||||
}
|
||||
|
||||
|
||||
202
platform/android/asset_request_libzip.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
#include <mbgl/android/jni.hpp>
|
||||
#include <mbgl/storage/asset_request.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
#include <mbgl/util/uv_detail.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <cerrno>
|
||||
// NOTE a bug in the Android NDK breaks std::errno
|
||||
// See https://code.google.com/p/android/issues/detail?id=72349
|
||||
// After this is fixed change usage errno to std::errno
|
||||
#include <zip.h>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
struct AssetRequestBaton {
|
||||
AssetRequestBaton(AssetRequest *request_, const std::string &path, uv_loop_t *loop);
|
||||
~AssetRequestBaton();
|
||||
|
||||
const std::thread::id threadId;
|
||||
AssetRequest *request = nullptr;
|
||||
std::unique_ptr<uv::async> asyncRun;
|
||||
std::string path;
|
||||
bool canceled = false;
|
||||
|
||||
void cancel();
|
||||
static void notifyError(AssetRequestBaton *ptr, const int code, const char *message);
|
||||
static void notifySuccess(AssetRequestBaton *ptr, const std::string body);
|
||||
static void cleanup(AssetRequestBaton *ptr);
|
||||
static void run(AssetRequestBaton *ptr);
|
||||
};
|
||||
|
||||
AssetRequestBaton::AssetRequestBaton(AssetRequest *request_, const std::string &path_, uv_loop_t *loop)
|
||||
: threadId(std::this_thread::get_id()),
|
||||
request(request_),
|
||||
path(path_) {
|
||||
|
||||
asyncRun = mbgl::util::make_unique<uv::async>(loop, [this]() {
|
||||
run(this);
|
||||
});
|
||||
|
||||
asyncRun->send();
|
||||
}
|
||||
|
||||
AssetRequestBaton::~AssetRequestBaton() {
|
||||
}
|
||||
|
||||
void AssetRequestBaton::cancel() {
|
||||
canceled = true;
|
||||
}
|
||||
|
||||
void AssetRequestBaton::notifyError(AssetRequestBaton *ptr, const int code, const char *message) {
|
||||
assert(std::this_thread::get_id() == ptr->threadId);
|
||||
|
||||
if (ptr->request && !ptr->canceled) {
|
||||
ptr->request->response = std::unique_ptr<Response>(new Response);
|
||||
ptr->request->response->code = code;
|
||||
ptr->request->response->message = message;
|
||||
ptr->request->notify();
|
||||
}
|
||||
}
|
||||
|
||||
void AssetRequestBaton::notifySuccess(AssetRequestBaton *ptr, const std::string body) {
|
||||
assert(std::this_thread::get_id() == ptr->threadId);
|
||||
|
||||
if (ptr->request && !ptr->canceled) {
|
||||
ptr->request->response = std::unique_ptr<Response>(new Response);
|
||||
ptr->request->response->code = 200;
|
||||
ptr->request->response->data = body;
|
||||
ptr->request->notify();
|
||||
}
|
||||
}
|
||||
|
||||
void AssetRequestBaton::cleanup(AssetRequestBaton *ptr) {
|
||||
assert(std::this_thread::get_id() == ptr->threadId);
|
||||
|
||||
if (ptr->request) {
|
||||
ptr->request->ptr = nullptr;
|
||||
}
|
||||
|
||||
ptr->asyncRun.reset();
|
||||
|
||||
delete ptr;
|
||||
ptr = nullptr;
|
||||
}
|
||||
|
||||
void AssetRequestBaton::run(AssetRequestBaton *ptr) {
|
||||
assert(std::this_thread::get_id() == ptr->threadId);
|
||||
|
||||
if (ptr->canceled || !ptr->request) {
|
||||
// Either the AssetRequest object has been destructed, or the
|
||||
// request was canceled.
|
||||
cleanup(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
int error = 0;
|
||||
struct zip *apk = zip_open(mbgl::android::apkPath.c_str(), 0, &error);
|
||||
if ((apk == nullptr) || ptr->canceled || !ptr->request) {
|
||||
// Opening the APK failed or was canceled. There isn't much left we can do.
|
||||
const int messageSize = zip_error_to_str(nullptr, 0, error, errno);
|
||||
const std::unique_ptr<char[]> message = mbgl::util::make_unique<char[]>(messageSize);
|
||||
zip_error_to_str(message.get(), 0, error, errno);
|
||||
notifyError(ptr, 500, message.get());
|
||||
cleanup(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string apkFilePath = "assets/" + ptr->path;
|
||||
struct zip_file *apkFile = zip_fopen(apk, apkFilePath.c_str(), ZIP_FL_NOCASE);
|
||||
if ((apkFile == 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(apk, &error, nullptr);
|
||||
notifyError(ptr, error == ZIP_ER_NOENT ? 404 : 500, zip_strerror(apk));
|
||||
zip_close(apk);
|
||||
apk = nullptr;
|
||||
cleanup(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
struct zip_stat stat;
|
||||
if ((zip_stat(apk, apkFilePath.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.
|
||||
notifyError(ptr, 500, zip_strerror(apk));
|
||||
zip_fclose(apkFile);
|
||||
apkFile = nullptr;
|
||||
zip_close(apk);
|
||||
apk = nullptr;
|
||||
cleanup(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::unique_ptr<char[]> data = mbgl::util::make_unique<char[]>(stat.size);
|
||||
|
||||
if (static_cast<zip_uint64_t>(zip_fread(apkFile, reinterpret_cast<void *>(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.
|
||||
notifyError(ptr, 500, zip_file_strerror(apkFile));
|
||||
zip_fclose(apkFile);
|
||||
apkFile = nullptr;
|
||||
zip_close(apk);
|
||||
apk = nullptr;
|
||||
cleanup(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string body(data.get(), stat.size);
|
||||
notifySuccess(ptr, body);
|
||||
|
||||
if (zip_fclose(apkFile) != 0) {
|
||||
// Closing the asset failed. But there isn't anything we can do.
|
||||
}
|
||||
apkFile = nullptr;
|
||||
|
||||
if (zip_close(apk) != 0) {
|
||||
// Closing the APK failed. But there isn't anything we can do.
|
||||
}
|
||||
apk = nullptr;
|
||||
|
||||
cleanup(ptr);
|
||||
}
|
||||
|
||||
AssetRequest::AssetRequest(const std::string &path_, uv_loop_t *loop)
|
||||
: BaseRequest(path_) {
|
||||
if (!path.empty() && path[0] == '/') {
|
||||
// This is an absolute path. We don't allow this. Note that this is not a way to absolutely
|
||||
// prevent access to resources outside the application bundle; e.g. there could be symlinks
|
||||
// in the application bundle that link to outside. We don't care about these.
|
||||
response = util::make_unique<Response>();
|
||||
response->code = 403;
|
||||
response->message = "Path is outside the application bundle";
|
||||
notify();
|
||||
} else {
|
||||
// Note: The AssetRequestBaton object is deleted in AssetRequestBaton::cleanup().
|
||||
ptr = new AssetRequestBaton(this, path, loop);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetRequest::cancel() {
|
||||
assert(std::this_thread::get_id() == threadId);
|
||||
|
||||
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(std::this_thread::get_id() == threadId);
|
||||
cancel();
|
||||
|
||||
// Note: The AssetRequestBaton object is deleted in AssetRequestBaton::cleanup().
|
||||
}
|
||||
|
||||
}
|
||||
13
platform/android/cache_database_data.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/android/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace platform {
|
||||
|
||||
// Returns the path to the default cache database on this system.
|
||||
std::string defaultCacheDatabase() {
|
||||
return mbgl::android::cachePath + "/mbgl-cache.db";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
59
platform/android/log_android.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include <mbgl/platform/android/log_android.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdarg>
|
||||
#define __STDC_FORMAT_MACROS // NDK bug workaround: https://code.google.com/p/android/issues/detail?id=72349
|
||||
#include <cinttypes>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
int AndroidLogBackend::severityToPriority(EventSeverity severity) {
|
||||
switch(severity) {
|
||||
case EventSeverity::Debug:
|
||||
return ANDROID_LOG_DEBUG;
|
||||
|
||||
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 AndroidLogBackend::record(EventSeverity severity, Event event, const std::string &msg) {
|
||||
__android_log_print(severityToPriority(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);
|
||||
|
||||
const int len = vsnprintf(nullptr, 0, format, args) + 1;
|
||||
char* buf = new char[len];
|
||||
vsnprintf(buf, len, format, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
__android_log_print(severityToPriority(severity), EventClass(event).c_str(), "%s", buf);
|
||||
|
||||
delete buf;
|
||||
buf = nullptr;
|
||||
}
|
||||
|
||||
void AndroidLogBackend::record(EventSeverity severity, Event event, int64_t code) {
|
||||
__android_log_print(severityToPriority(severity), EventClass(event).c_str(), "(%" PRId64 ")", code);
|
||||
}
|
||||
|
||||
void AndroidLogBackend::record(EventSeverity severity, Event event, int64_t code, const std::string &msg) {
|
||||
__android_log_print(severityToPriority(severity), EventClass(event).c_str(), "(%" PRId64 ") %s", code, msg.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
13
platform/android/shader_cache_data.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/android/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace platform {
|
||||
|
||||
// Returns the path to the default shader cache on this system.
|
||||
std::string defaultShaderCache() {
|
||||
return mbgl::android::cachePath + "/mbgl-shader-cache-";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ NSURLSession *session = nullptr;
|
||||
NSString *userAgent = nil;
|
||||
|
||||
void HTTPRequestBaton::start(const util::ptr<HTTPRequestBaton> &ptr) {
|
||||
assert(std::this_thread::get_id() == ptr->thread_id);
|
||||
assert(std::this_thread::get_id() == ptr->threadId);
|
||||
|
||||
// Starts the request.
|
||||
util::ptr<HTTPRequestBaton> baton = ptr;
|
||||
@@ -144,7 +144,7 @@ void HTTPRequestBaton::start(const util::ptr<HTTPRequestBaton> &ptr) {
|
||||
}
|
||||
|
||||
void HTTPRequestBaton::stop(const util::ptr<HTTPRequestBaton> &ptr) {
|
||||
assert(std::this_thread::get_id() == ptr->thread_id);
|
||||
assert(std::this_thread::get_id() == ptr->threadId);
|
||||
assert(ptr->ptr);
|
||||
|
||||
NSURLSessionDataTask *task = CFBridgingRelease(ptr->ptr);
|
||||
|
||||